001package org.apache.archiva.metadata.repository.jcr; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import com.google.common.collect.ImmutableMap; 023import org.apache.archiva.checksum.ChecksumAlgorithm; 024import org.apache.archiva.metadata.QueryParameter; 025import org.apache.archiva.metadata.model.*; 026import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet; 027import org.apache.archiva.metadata.repository.*; 028import org.apache.archiva.metadata.repository.stats.model.RepositoryStatistics; 029import org.apache.archiva.metadata.repository.stats.model.RepositoryStatisticsProvider; 030import org.apache.commons.lang3.StringUtils; 031import org.apache.jackrabbit.JcrConstants; 032import org.apache.jackrabbit.commons.JcrUtils; 033import org.apache.jackrabbit.commons.cnd.CndImporter; 034import org.apache.jackrabbit.commons.cnd.ParseException; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038import javax.annotation.ParametersAreNonnullByDefault; 039import javax.jcr.*; 040import javax.jcr.query.*; 041import java.io.IOException; 042import java.io.InputStreamReader; 043import java.io.Reader; 044import java.time.ZonedDateTime; 045import java.util.*; 046import java.util.Map.Entry; 047import java.util.function.Consumer; 048import java.util.function.Function; 049import java.util.stream.Collectors; 050import java.util.stream.Stream; 051import java.util.stream.StreamSupport; 052 053import static javax.jcr.Property.JCR_LAST_MODIFIED; 054import static org.apache.archiva.metadata.repository.jcr.JcrConstants.*; 055 056/** 057 * TODO below: revise storage format for project version metadata 058 * TODO revise reference storage 059 */ 060@ParametersAreNonnullByDefault 061public class JcrMetadataRepository 062 extends AbstractMetadataRepository implements MetadataRepository, RepositoryStatisticsProvider { 063 064 065 private static final String QUERY_ARTIFACT_1 = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/"; 066 067 static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_1 = "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE 068 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) INNER JOIN [" + FACET_NODE_TYPE 069 + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE ([facet].["; 070 static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_2 = "] = $value)"; 071 072 static final String QUERY_ARTIFACTS_BY_METADATA_1 = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact INNER JOIN [" + FACET_NODE_TYPE 073 + "] AS facet ON ISCHILDNODE(facet, artifact) WHERE ([facet].["; 074 static final String QUERY_ARTIFACTS_BY_METADATA_2 = "] = $value)"; 075 076 static final String QUERY_ARTIFACTS_BY_PROPERTY_1 = "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE 077 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE ([projectVersion].["; 078 static final String QUERY_ARTIFACTS_BY_PROPERTY_2 = "] = $value)"; 079 080 081 private static final String QUERY_ARTIFACT_2 = "')"; 082 083 private Logger log = LoggerFactory.getLogger(JcrMetadataRepository.class); 084 085 private Repository repository; 086 087 public JcrMetadataRepository(MetadataService metadataService, Repository repository) 088 throws RepositoryException { 089 super(metadataService); 090 this.repository = repository; 091 } 092 093 094 public static void initializeNodeTypes(Session session) 095 throws RepositoryException { 096 097 // TODO: consider using namespaces for facets instead of the current approach: 098 // (if used, check if actually called by normal injection) 099// for ( String facetId : metadataFacetFactories.keySet() ) 100// { 101// session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId ); 102// } 103 Workspace workspace = session.getWorkspace(); 104 NamespaceRegistry registry = workspace.getNamespaceRegistry(); 105 106 if (!Arrays.asList(registry.getPrefixes()).contains("archiva")) { 107 registry.registerNamespace("archiva", "http://archiva.apache.org/jcr/"); 108 } 109 110 try ( 111 Reader cndReader = new InputStreamReader( 112 Thread.currentThread().getContextClassLoader().getResourceAsStream("org/apache/archiva/metadata/repository/jcr/jcr-schema.cnd"))) { 113 CndImporter.registerNodeTypes(cndReader, session); 114 } catch (ParseException e) { 115 e.printStackTrace(); 116 } catch (IOException e) { 117 e.printStackTrace(); 118 } 119 120 } 121 122 private Session getSession(RepositorySession repositorySession) throws MetadataRepositoryException { 123 if (repositorySession instanceof JcrRepositorySession) { 124 return ((JcrRepositorySession) repositorySession).getJcrSession(); 125 } else { 126 throw new MetadataRepositoryException("The given session object is not a JcrSession instance: " + repositorySession.getClass().getName()); 127 } 128 } 129 130 @Override 131 public void updateProject(RepositorySession session, String repositoryId, ProjectMetadata project) 132 throws MetadataRepositoryException { 133 final Session jcrSession = getSession(session); 134 updateProject(jcrSession, repositoryId, project.getNamespace(), project.getId()); 135 } 136 137 private void updateProject(Session jcrSession, String repositoryId, String namespace, String projectId) 138 throws MetadataRepositoryException { 139 updateNamespace(jcrSession, repositoryId, namespace); 140 141 try { 142 getOrAddProjectNode(jcrSession, repositoryId, namespace, projectId); 143 } catch (RepositoryException e) { 144 throw new MetadataRepositoryException(e.getMessage(), e); 145 } 146 } 147 148 @Override 149 public void updateArtifact(RepositorySession session, String repositoryId, String namespace, String projectId, String projectVersion, 150 ArtifactMetadata artifactMeta) 151 throws MetadataRepositoryException { 152 final Session jcrSession = getSession(session); 153 updateNamespace(session, repositoryId, namespace); 154 155 try { 156 Node node = 157 getOrAddArtifactNode(jcrSession, repositoryId, namespace, projectId, projectVersion, artifactMeta.getId()); 158 159 node.setProperty("id", artifactMeta.getId()); 160 Calendar cal = GregorianCalendar.from(artifactMeta.getFileLastModified()); 161 node.setProperty(JCR_LAST_MODIFIED, cal); 162 163 cal = GregorianCalendar.from(artifactMeta.getWhenGathered()); 164 node.setProperty("whenGathered", cal); 165 166 node.setProperty("size", artifactMeta.getSize()); 167 168 int idx = 0; 169 Node cslistNode = getOrAddNodeByPath(node, "checksums", CHECKSUMS_FOLDER_TYPE, true); 170 NodeIterator nit = cslistNode.getNodes("*"); 171 while (nit.hasNext()) { 172 Node csNode = nit.nextNode(); 173 if (csNode.isNodeType(CHECKSUM_NODE_TYPE)) { 174 csNode.remove(); 175 } 176 } 177 for (Map.Entry<ChecksumAlgorithm, String> entry : artifactMeta.getChecksums().entrySet()) { 178 String type = entry.getKey().name(); 179 Node csNode = cslistNode.addNode(type, CHECKSUM_NODE_TYPE); 180 csNode.setProperty("type", type); 181 csNode.setProperty("value", entry.getValue()); 182 } 183 184 node.setProperty("version", artifactMeta.getVersion()); 185 186 // iterate over available facets to update/add/remove from the artifactMetadata 187 for (String facetId : metadataService.getSupportedFacets()) { 188 MetadataFacet metadataFacet = artifactMeta.getFacet(facetId); 189 if (metadataFacet == null) { 190 continue; 191 } 192 if (node.hasNode(facetId)) { 193 node.getNode(facetId).remove(); 194 } 195 if (metadataFacet != null) { 196 // recreate, to ensure properties are removed 197 Node n = node.addNode(facetId, FACET_NODE_TYPE); 198 n.setProperty("facetId", facetId); 199 200 for (Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet()) { 201 n.setProperty(entry.getKey(), entry.getValue()); 202 } 203 } 204 } 205 } catch (RepositoryException e) { 206 throw new MetadataRepositoryException(e.getMessage(), e); 207 } 208 } 209 210 @Override 211 public void updateProjectVersion(RepositorySession session, String repositoryId, String namespace, String projectId, 212 ProjectVersionMetadata versionMetadata) 213 throws MetadataRepositoryException { 214 final Session jcrSession = getSession(session); 215 updateProject(jcrSession, repositoryId, namespace, projectId); 216 217 try { 218 Node versionNode = 219 getOrAddProjectVersionNode(jcrSession, repositoryId, namespace, projectId, versionMetadata.getId()); 220 versionNode.setProperty("id", versionMetadata.getId()); 221 versionNode.setProperty("name", StringUtils.isEmpty(versionMetadata.getName()) ? "" : versionMetadata.getName()); 222 versionNode.setProperty("description", StringUtils.isEmpty(versionMetadata.getDescription()) ? "" : versionMetadata.getDescription()); 223 versionNode.setProperty("url", versionMetadata.getUrl()); 224 versionNode.setProperty("incomplete", versionMetadata.isIncomplete()); 225 226 // FIXME: decide how to treat these in the content repo 227 if (versionMetadata.getScm() != null) { 228 versionNode.setProperty("scm.connection", versionMetadata.getScm().getConnection()); 229 versionNode.setProperty("scm.developerConnection", versionMetadata.getScm().getDeveloperConnection()); 230 versionNode.setProperty("scm.url", versionMetadata.getScm().getUrl()); 231 } 232 if (versionMetadata.getCiManagement() != null) { 233 versionNode.setProperty("ci.system", versionMetadata.getCiManagement().getSystem()); 234 versionNode.setProperty("ci.url", versionMetadata.getCiManagement().getUrl()); 235 } 236 if (versionMetadata.getIssueManagement() != null) { 237 versionNode.setProperty("issue.system", versionMetadata.getIssueManagement().getSystem()); 238 versionNode.setProperty("issue.url", versionMetadata.getIssueManagement().getUrl()); 239 } 240 if (versionMetadata.getOrganization() != null) { 241 versionNode.setProperty("org.name", versionMetadata.getOrganization().getName()); 242 versionNode.setProperty("org.url", versionMetadata.getOrganization().getUrl()); 243 } 244 int i = 0; 245 Node licensesNode = JcrUtils.getOrAddNode(versionNode, "licenses", LICENSES_FOLDER_TYPE); 246 Set<String> licNames = new HashSet<>(); 247 for (License license : versionMetadata.getLicenses()) { 248 Node licNode = JcrUtils.getOrAddNode(licensesNode, license.getName(), LICENSE_NODE_TYPE); 249 licNode.setProperty("index", i); 250 licNode.setProperty("name", license.getName()); 251 licNode.setProperty("url", license.getUrl()); 252 licNames.add(license.getName()); 253 i++; 254 } 255 NodeIterator nodeIterator = licensesNode.getNodes(); 256 while (nodeIterator.hasNext()) { 257 Node n = nodeIterator.nextNode(); 258 if (!licNames.contains(n.getName())) { 259 n.remove(); 260 } 261 } 262 i = 0; 263 Node mailinglistsListNode = JcrUtils.getOrAddNode(versionNode, "mailinglists", MAILINGLISTS_FOLDER_TYPE); 264 Set<String> listNames = new HashSet<>(); 265 for (MailingList mailingList : versionMetadata.getMailingLists()) { 266 final String name = mailingList.getName(); 267 Node mailNode = JcrUtils.getOrAddNode(mailinglistsListNode, mailingList.getName(), MAILINGLIST_NODE_TYPE); 268 mailNode.setProperty("index", i); 269 mailNode.setProperty("archive", mailingList.getMainArchiveUrl()); 270 mailNode.setProperty("name", mailingList.getName()); 271 mailNode.setProperty("post", mailingList.getPostAddress()); 272 mailNode.setProperty("unsubscribe", mailingList.getUnsubscribeAddress()); 273 mailNode.setProperty("subscribe", mailingList.getSubscribeAddress()); 274 mailNode.setProperty("otherArchives", 275 join(mailingList.getOtherArchives())); 276 i++; 277 listNames.add(name); 278 } 279 nodeIterator = mailinglistsListNode.getNodes(); 280 while (nodeIterator.hasNext()) { 281 Node n = nodeIterator.nextNode(); 282 if (!listNames.contains(n.getName())) { 283 n.remove(); 284 } 285 } 286 if (!versionMetadata.getDependencies().isEmpty()) { 287 Node dependenciesNode = JcrUtils.getOrAddNode(versionNode, "dependencies", DEPENDENCIES_FOLDER_TYPE); 288 289 for (Dependency dependency : versionMetadata.getDependencies()) { 290 // Note that we deliberately don't alter the namespace path - not enough dependencies for 291 // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each 292 // component of the ID as that creates extra depth and causes a great cost in space and memory 293 294 // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now. 295 // This won't support types where the extension doesn't match the type. 296 // (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM) 297 String id = 298 dependency.getNamespace() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion(); 299 if (dependency.getClassifier() != null) { 300 id += "-" + dependency.getClassifier(); 301 } 302 id += "." + dependency.getType(); 303 304 Node n = JcrUtils.getOrAddNode(dependenciesNode, id, DEPENDENCY_NODE_TYPE); 305 n.setProperty("id", id); 306 307 n.setProperty("namespace", dependency.getNamespace()); 308 n.setProperty("artifactId", dependency.getArtifactId()); 309 n.setProperty("version", dependency.getVersion()); 310 n.setProperty("type", dependency.getType()); 311 n.setProperty("classifier", dependency.getClassifier()); 312 n.setProperty("scope", dependency.getScope()); 313 n.setProperty("systemPath", dependency.getSystemPath()); 314 n.setProperty("optional", dependency.isOptional()); 315 n.setProperty("projectId", dependency.getProjectId()); 316 // TODO: Fixig 317 Node refNode = findArtifactNode(jcrSession, dependency.getNamespace(), 318 dependency.getProjectId(), dependency.getVersion(), dependency.getArtifactId()); 319 if (refNode!=null) { 320 n.setProperty("link", refNode.getPath()); 321 } 322 323 // node has no native content at this time, just facets 324 // no need to list a type as it's implied by the path. Parents are Maven specific. 325 326 // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet 327 // (should also have been added to the Dependency) 328 329 // TODO: add a property that is a weak reference to the originating artifact, creating it if 330 // necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an 331 // artifact, which gives a different meaning to "incomplete" which is a known local project 332 // that doesn't have metadata yet but has artifacts). (Though we may want to give it the 333 // artifact mixin and another property to identify all non-local artifacts for the closure 334 // reports) 335 } 336 } 337 338 for (MetadataFacet facet : versionMetadata.getFacetList()) { 339 // recreate, to ensure properties are removed 340 if (versionNode.hasNode(facet.getFacetId())) { 341 versionNode.getNode(facet.getFacetId()).remove(); 342 } 343 Node n = versionNode.addNode(facet.getFacetId(), FACET_NODE_TYPE); 344 345 for (Map.Entry<String, String> entry : facet.toProperties().entrySet()) { 346 n.setProperty(entry.getKey(), entry.getValue()); 347 } 348 } 349 } catch (RepositoryException e) { 350 throw new MetadataRepositoryException(e.getMessage(), e); 351 } 352 } 353 354 private void updateNamespace(Session jcrSession, String repositoryId, String namespace) throws MetadataRepositoryException { 355 try { 356 Node node = getOrAddNamespaceNode(jcrSession, repositoryId, namespace); 357 node.setProperty("id", namespace); 358 node.setProperty("namespace", namespace); 359 } catch (RepositoryException e) { 360 throw new MetadataRepositoryException(e.getMessage(), e); 361 } 362 } 363 364 @Override 365 public void updateNamespace(RepositorySession session, String repositoryId, String namespace) 366 throws MetadataRepositoryException { 367 updateNamespace(getSession(session), repositoryId, namespace); 368 } 369 370 @Override 371 public void removeProject(RepositorySession session, String repositoryId, String namespace, String projectId) 372 throws MetadataRepositoryException { 373 final Session jcrSession = getSession(session); 374 try { 375 Node root = jcrSession.getRootNode(); 376 String namespacePath = getNamespacePath(repositoryId, namespace); 377 378 if (root.hasNode(namespacePath)) { 379 Iterator<Node> nodeIterator = JcrUtils.getChildNodes(root.getNode(namespacePath)).iterator(); 380 while (nodeIterator.hasNext()) { 381 Node node = nodeIterator.next(); 382 if (node.isNodeType(org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_MIXIN_TYPE) && projectId.equals(node.getName())) { 383 node.remove(); 384 } 385 } 386 387 } 388 } catch (RepositoryException e) { 389 throw new MetadataRepositoryException(e.getMessage(), e); 390 } 391 392 } 393 394 395 @Override 396 public boolean hasMetadataFacet(RepositorySession session, String repositoryId, String facetId) 397 throws MetadataRepositoryException { 398 final Session jcrSession = getSession(session); 399 try { 400 Node node = jcrSession.getRootNode().getNode(getFacetPath(repositoryId, facetId)); 401 return node.getNodes().hasNext(); 402 } catch (PathNotFoundException e) { 403 // ignored - the facet doesn't exist, so return false 404 return false; 405 } catch (RepositoryException e) { 406 throw new MetadataRepositoryException(e.getMessage(), e); 407 } 408 } 409 410 @Override 411 public List<String> getMetadataFacets(RepositorySession session, String repositoryId, String facetId) 412 throws MetadataRepositoryException { 413 final Session jcrSession = getSession(session); 414 List<String> facets = new ArrayList<>(); 415 416 try { 417 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and 418 // are paths themselves 419 Node node = jcrSession.getRootNode().getNode(getFacetPath(repositoryId, facetId)); 420 421 // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of 422 // paths helpful? 423 recurse(facets, "", node); 424 } catch (PathNotFoundException e) { 425 // ignored - the facet doesn't exist, so return the empty list 426 } catch (RepositoryException e) { 427 throw new MetadataRepositoryException(e.getMessage(), e); 428 } 429 return facets; 430 } 431 432 private <T> Spliterator<T> createResultSpliterator(QueryResult result, Function<Row, T> converter) throws MetadataRepositoryException { 433 final RowIterator rowIterator; 434 try { 435 rowIterator = result.getRows(); 436 } catch (RepositoryException e) { 437 throw new MetadataRepositoryException(e.getMessage(), e); 438 } 439 return new Spliterator<T>() { 440 @Override 441 public boolean tryAdvance(Consumer<? super T> action) { 442 while (rowIterator.hasNext()) { 443 T item = converter.apply(rowIterator.nextRow()); 444 if (item != null) { 445 action.accept(item); 446 return true; 447 } 448 } 449 return false; 450 } 451 452 @Override 453 public Spliterator<T> trySplit() { 454 return null; 455 } 456 457 @Override 458 public long estimateSize() { 459 return 0; 460 } 461 462 @Override 463 public int characteristics() { 464 return ORDERED + NONNULL; 465 } 466 }; 467 } 468 469 private StringBuilder appendQueryParams(StringBuilder query, String selector, String defaultProperty, QueryParameter queryParameter) { 470 if (queryParameter.getSortFields().size() == 0) { 471 query.append(" ORDER BY [").append(selector).append("].[").append(defaultProperty).append("]"); 472 if (queryParameter.isAscending()) { 473 query.append(" ASC"); 474 } else { 475 query.append(" DESC"); 476 } 477 } else { 478 query.append(" ORDER BY"); 479 for (String property : queryParameter.getSortFields()) { 480 query.append(" [").append(selector).append("].[").append(property).append("]"); 481 if (queryParameter.isAscending()) { 482 query.append(" ASC"); 483 } else { 484 query.append(" DESC"); 485 } 486 } 487 } 488 return query; 489 } 490 491 private <T extends MetadataFacet> Function<Row, Optional<T>> getFacetFromRowFunc(MetadataFacetFactory<T> factory, String repositoryId) { 492 return (Row row) -> { 493 try { 494 Node node = row.getNode("facet"); 495 if (node.hasProperty("archiva:name")) { 496 String facetName = node.getProperty("archiva:name").getString(); 497 return Optional.ofNullable(createFacetFromNode(factory, node, repositoryId, facetName)); 498 } else { 499 return Optional.empty(); 500 } 501 } catch (RepositoryException e) { 502 log.error("Exception encountered {}", e.getMessage()); 503 return Optional.empty(); 504 } 505 }; 506 } 507 508 @Override 509 public <T extends MetadataFacet> Stream<T> getMetadataFacetStream(RepositorySession session, String repositoryId, Class<T> facetClazz, QueryParameter queryParameter) throws MetadataRepositoryException { 510 final Session jcrSession = getSession(session); 511 final MetadataFacetFactory<T> factory = metadataService.getFactory(facetClazz); 512 final String facetId = factory.getFacetId(); 513 final String facetPath = '/' + getFacetPath(repositoryId, facetId); 514 StringBuilder query = new StringBuilder("SELECT * FROM ["); 515 query.append(FACET_NODE_TYPE).append("] AS facet WHERE ISDESCENDANTNODE(facet, [") 516 .append(facetPath).append("]) AND [facet].[archiva:name] IS NOT NULL"); 517 appendQueryParams(query, "facet", "archiva:name", queryParameter); 518 String q = query.toString(); 519 Map<String, String> params = new HashMap<>(); 520 QueryResult result = runNativeJcrQuery(jcrSession, q, params, queryParameter.getOffset(), queryParameter.getLimit()); 521 final Function<Row, Optional<T>> rowFunc = getFacetFromRowFunc(factory, repositoryId); 522 return StreamSupport.stream(createResultSpliterator(result, rowFunc), false).filter(Optional::isPresent).map(Optional::get); 523 524 } 525 526 private void recurse(List<String> facets, String prefix, Node node) 527 throws RepositoryException { 528 for (Node n : JcrUtils.getChildNodes(node)) { 529 String name = prefix + "/" + n.getName(); 530 if (n.hasNodes()) { 531 recurse(facets, name, n); 532 } else { 533 // strip leading / first 534 facets.add(name.substring(1)); 535 } 536 } 537 } 538 539 540 @Override 541 public <T extends MetadataFacet> T getMetadataFacet(RepositorySession session, String repositoryId, Class<T> clazz, String name) throws MetadataRepositoryException { 542 if (!metadataService.supportsFacet(clazz)) { 543 log.warn("The required metadata class is not supported: " + clazz); 544 return null; 545 } 546 final Session jcrSession = getSession(session); 547 final MetadataFacetFactory<T> factory = getFacetFactory(clazz); 548 final String facetId = factory.getFacetId(); 549 try { 550 Node root = jcrSession.getRootNode(); 551 Node node = root.getNode(getFacetPath(repositoryId, facetId, name)); 552 553 if (getSupportedFacets().size() == 0) { 554 return null; 555 } 556 557 return createFacetFromNode(factory, node, repositoryId, name); 558 } catch (PathNotFoundException e) { 559 // ignored - the facet doesn't exist, so return null 560 } catch (RepositoryException e) { 561 throw new MetadataRepositoryException(e.getMessage(), e); 562 } 563 return null; 564 } 565 566 private <T extends MetadataFacet> T createFacetFromNode(final MetadataFacetFactory<T> factory, final Node node) throws RepositoryException { 567 return createFacetFromNode(factory, node, null, null); 568 } 569 570 private <T extends MetadataFacet> T createFacetFromNode(final MetadataFacetFactory<T> factory, final Node node, 571 final String repositoryId, final String name) throws RepositoryException { 572 if (factory != null) { 573 T metadataFacet; 574 if (repositoryId != null) { 575 metadataFacet = factory.createMetadataFacet(repositoryId, name); 576 } else { 577 metadataFacet = factory.createMetadataFacet(); 578 } 579 Map<String, String> map = new HashMap<>(); 580 for (Property property : JcrUtils.getProperties(node)) { 581 String p = property.getName(); 582 if (!p.startsWith("jcr:")) { 583 map.put(p, property.getString()); 584 } 585 } 586 metadataFacet.fromProperties(map); 587 return metadataFacet; 588 } 589 return null; 590 } 591 592 @Override 593 public void addMetadataFacet(RepositorySession session, String repositoryId, MetadataFacet metadataFacet) 594 throws MetadataRepositoryException { 595 final Session jcrSession = getSession(session); 596 try { 597 Node repo = getOrAddRepositoryNode(jcrSession, repositoryId); 598 Node facets = JcrUtils.getOrAddNode(repo, "facets", FACETS_FOLDER_TYPE); 599 600 String id = metadataFacet.getFacetId(); 601 Node facetNode = JcrUtils.getOrAddNode(facets, id, FACET_ID_CONTAINER_TYPE); 602 if (!facetNode.hasProperty("id")) { 603 facetNode.setProperty("id", id); 604 } 605 606 Node facetInstance = getOrAddNodeByPath(facetNode, metadataFacet.getName(), FACET_NODE_TYPE, true); 607 if (!facetInstance.hasProperty("archiva:facetId")) { 608 facetInstance.setProperty("archiva:facetId", id); 609 facetInstance.setProperty("archiva:name", metadataFacet.getName()); 610 } 611 612 for (Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet()) { 613 facetInstance.setProperty(entry.getKey(), entry.getValue()); 614 } 615 session.save(); 616 } catch (RepositoryException | MetadataSessionException e) { 617 throw new MetadataRepositoryException(e.getMessage(), e); 618 } 619 } 620 621 @Override 622 public void removeNamespace(RepositorySession session, String repositoryId, String projectId) 623 throws MetadataRepositoryException { 624 final Session jcrSession = getSession(session); 625 try { 626 Node root = jcrSession.getRootNode(); 627 String path = getNamespacePath(repositoryId, projectId); 628 if (root.hasNode(path)) { 629 Node node = root.getNode(path); 630 if (node.isNodeType(NAMESPACE_MIXIN_TYPE)) { 631 node.remove(); 632 } 633 } 634 } catch (RepositoryException e) { 635 throw new MetadataRepositoryException(e.getMessage(), e); 636 } 637 } 638 639 @Override 640 public void removeMetadataFacets(RepositorySession session, String repositoryId, String facetId) 641 throws MetadataRepositoryException { 642 final Session jcrSession = getSession(session); 643 try { 644 Node root = jcrSession.getRootNode(); 645 String path = getFacetPath(repositoryId, facetId); 646 if (root.hasNode(path)) { 647 root.getNode(path).remove(); 648 } 649 } catch (RepositoryException e) { 650 throw new MetadataRepositoryException(e.getMessage(), e); 651 } 652 } 653 654 @Override 655 public void removeMetadataFacet(RepositorySession session, String repositoryId, String facetId, String name) 656 throws MetadataRepositoryException { 657 final Session jcrSession = getSession(session); 658 try { 659 Node root = jcrSession.getRootNode(); 660 String path = getFacetPath(repositoryId, facetId, name); 661 if (root.hasNode(path)) { 662 Node node = root.getNode(path); 663 do { 664 // also remove empty container nodes 665 Node parent = node.getParent(); 666 node.remove(); 667 node = parent; 668 } 669 while (!node.hasNodes()); 670 } 671 } catch (RepositoryException e) { 672 throw new MetadataRepositoryException(e.getMessage(), e); 673 } 674 } 675 676 private StringBuilder buildArtifactByDateRangeQuery(String repoId, ZonedDateTime startTime, ZonedDateTime endTime, 677 QueryParameter queryParameter) { 678 StringBuilder q = getArtifactQuery(repoId); 679 680 if (startTime != null) { 681 q.append(" AND [artifact].[whenGathered] >= $start"); 682 } 683 if (endTime != null) { 684 q.append(" AND [artifact].[whenGathered] <= $end"); 685 } 686 appendQueryParams(q, "artifact", "whenGathered", queryParameter); 687 return q; 688 } 689 690 private QueryResult queryArtifactByDateRange(Session jcrSession, String repositoryId, 691 ZonedDateTime startTime, ZonedDateTime endTime, 692 QueryParameter queryParameter) throws MetadataRepositoryException { 693 String q = buildArtifactByDateRangeQuery(repositoryId, startTime, endTime, queryParameter).toString(); 694 695 try { 696 Query query = jcrSession.getWorkspace().getQueryManager().createQuery(q, Query.JCR_SQL2); 697 query.setOffset(queryParameter.getOffset()); 698 query.setLimit(queryParameter.getLimit()); 699 ValueFactory valueFactory = jcrSession.getValueFactory(); 700 if (startTime != null) { 701 query.bindValue("start", valueFactory.createValue(createCalendar(startTime.withZoneSameInstant(ModelInfo.STORAGE_TZ)))); 702 } 703 if (endTime != null) { 704 query.bindValue("end", valueFactory.createValue(createCalendar(endTime.withZoneSameInstant(ModelInfo.STORAGE_TZ)))); 705 } 706 return query.execute(); 707 } catch (RepositoryException e) { 708 throw new MetadataRepositoryException(e.getMessage(), e); 709 } 710 } 711 712 @Override 713 public List<ArtifactMetadata> getArtifactsByDateRange(RepositorySession session, String repoId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) 714 throws MetadataRepositoryException { 715 final Session jcrSession = getSession(session); 716 717 List<ArtifactMetadata> artifacts; 718 try { 719 QueryResult result = queryArtifactByDateRange(jcrSession, repoId, startTime, endTime, queryParameter); 720 721 artifacts = new ArrayList<>(); 722 for (Node n : JcrUtils.getNodes(result)) { 723 artifacts.add(getArtifactFromNode(repoId, n)); 724 } 725 } catch (RepositoryException e) { 726 throw new MetadataRepositoryException(e.getMessage(), e); 727 } 728 return artifacts; 729 } 730 731 private Function<Row, Optional<ArtifactMetadata>> getArtifactFromRowFunc(final String repositoryId) { 732 return (Row row) -> { 733 try { 734 return Optional.of(getArtifactFromNode(repositoryId, row.getNode("artifact"))); 735 } catch (RepositoryException e) { 736 return Optional.empty(); 737 } 738 }; 739 } 740 741 @Override 742 public Stream<ArtifactMetadata> getArtifactByDateRangeStream(RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException { 743 final Session jcrSession = getSession(session); 744 final QueryResult result = queryArtifactByDateRange(jcrSession, repositoryId, startTime, endTime, queryParameter); 745 final Function<Row, Optional<ArtifactMetadata>> rowFunc = getArtifactFromRowFunc(repositoryId); 746 return StreamSupport.stream(createResultSpliterator(result, rowFunc), false).filter(Optional::isPresent).map(Optional::get); 747 } 748 749 750 @Override 751 public List<ArtifactMetadata> getArtifactsByChecksum(RepositorySession session, String repositoryId, String checksum) 752 throws MetadataRepositoryException { 753 final Session jcrSession = getSession(session); 754 List<ArtifactMetadata> artifacts; 755 756 String q = getArtifactQuery(repositoryId).append(" AND ([artifact].[checksums/*/value] = $checksum)").toString(); 757 758 try { 759 Query query = jcrSession.getWorkspace().getQueryManager().createQuery(q, Query.JCR_SQL2); 760 ValueFactory valueFactory = jcrSession.getValueFactory(); 761 query.bindValue("checksum", valueFactory.createValue(checksum)); 762 QueryResult result = query.execute(); 763 764 artifacts = new ArrayList<>(); 765 for (Node n : JcrUtils.getNodes(result)) { 766 artifacts.add(getArtifactFromNode(repositoryId, n)); 767 } 768 } catch (RepositoryException e) { 769 throw new MetadataRepositoryException(e.getMessage(), e); 770 } 771 return artifacts; 772 } 773 774 public List<ArtifactMetadata> runJcrQuery(Session jcrSession, String repositoryId, String q, Map<String, String> bindingParam) 775 throws MetadataRepositoryException { 776 return runJcrQuery(jcrSession, repositoryId, q, bindingParam, true); 777 } 778 779 public List<ArtifactMetadata> runJcrQuery(final Session jcrSession, final String repositoryId, final String qParam, 780 final Map<String, String> bindingParam, final boolean checkPath) 781 throws MetadataRepositoryException { 782 783 String q = qParam; 784 List<ArtifactMetadata> artifacts; 785 if (repositoryId != null && checkPath) { 786 q += " AND ISDESCENDANTNODE(artifact,'/" + getRepositoryContentPath(repositoryId) + "')"; 787 } 788 789 log.info("Running JCR Query: {}", q); 790 791 try { 792 QueryResult result = runNativeJcrQuery(jcrSession, q, bindingParam); 793 artifacts = new ArrayList<>(); 794 RowIterator rows = result.getRows(); 795 while (rows.hasNext()) { 796 Row row = rows.nextRow(); 797 Node node = row.getNode("artifact"); 798 artifacts.add(getArtifactFromNode(repositoryId, node)); 799 } 800 } catch (RepositoryException e) { 801 throw new MetadataRepositoryException(e.getMessage(), e); 802 } 803 log.info("Artifacts found {}", artifacts.size()); 804 for (ArtifactMetadata meta : artifacts) { 805 log.info("Artifact: " + meta.getVersion() + " " + meta.getFacetList()); 806 } 807 return artifacts; 808 } 809 810 public QueryResult runNativeJcrQuery(final Session jcrSession, final String q, final Map<String, String> bindingParam) throws MetadataRepositoryException { 811 return runNativeJcrQuery(jcrSession, q, bindingParam, 0, Long.MAX_VALUE); 812 } 813 814 public QueryResult runNativeJcrQuery(final Session jcrSession, final String q, final Map<String, String> bindingParam, long offset, long maxEntries) 815 throws MetadataRepositoryException { 816 Map<String, String> bindings; 817 if (bindingParam == null) { 818 bindings = new HashMap<>(); 819 } else { 820 bindings = bindingParam; 821 } 822 823 try { 824 log.debug("Query: offset={}, limit={}, query={}", offset, maxEntries, q); 825 Query query = jcrSession.getWorkspace().getQueryManager().createQuery(q, Query.JCR_SQL2); 826 query.setLimit(maxEntries); 827 query.setOffset(offset); 828 ValueFactory valueFactory = jcrSession.getValueFactory(); 829 for (Entry<String, String> entry : bindings.entrySet()) { 830 log.debug("Binding: {}={}", entry.getKey(), entry.getValue()); 831 Value value = valueFactory.createValue(entry.getValue()); 832 log.debug("Binding value {}={}", entry.getKey(), value); 833 query.bindValue(entry.getKey(), value); 834 } 835 long start = System.currentTimeMillis(); 836 log.debug("Execute query {}", query); 837 QueryResult result = query.execute(); 838 long end = System.currentTimeMillis(); 839 log.info("JCR Query ran in {} milliseconds: {}", end - start, q); 840 return result; 841 } catch (RepositoryException e) { 842 throw new MetadataRepositoryException(e.getMessage(), e); 843 } 844 } 845 846 @Override 847 public List<ArtifactMetadata> getArtifactsByProjectVersionFacet(RepositorySession session, String key, String value, String repositoryId) 848 throws MetadataRepositoryException { 849 final Session jcrSession = getSession(session); 850 final String q = new StringBuilder(QUERY_ARTIFACTS_BY_PROJECT_VERSION_1).append(key).append(QUERY_ARTIFACTS_BY_PROJECT_VERSION_2).toString(); 851 return runJcrQuery(jcrSession, repositoryId, q, ImmutableMap.of("value", value)); 852 } 853 854 855 @Override 856 public List<ArtifactMetadata> getArtifactsByAttribute(RepositorySession session, String key, String value, String repositoryId) 857 throws MetadataRepositoryException { 858 final Session jcrSession = getSession(session); 859 final String q = new StringBuilder(QUERY_ARTIFACTS_BY_METADATA_1).append(key).append(QUERY_ARTIFACTS_BY_METADATA_2).toString(); 860 return runJcrQuery(jcrSession, repositoryId, q, ImmutableMap.of("value", value)); 861 } 862 863 864 @Override 865 public List<ArtifactMetadata> getArtifactsByProjectVersionAttribute(RepositorySession session, String key, String value, String repositoryId) 866 throws MetadataRepositoryException { 867 final Session jcrSession = getSession(session); 868 final String q = new StringBuilder(QUERY_ARTIFACTS_BY_PROPERTY_1).append(key).append(QUERY_ARTIFACTS_BY_PROPERTY_2).toString(); 869 return runJcrQuery(jcrSession, repositoryId, q, ImmutableMap.of("value", value)); 870 } 871 872 873 @Override 874 public void removeRepository(RepositorySession session, String repositoryId) 875 throws MetadataRepositoryException { 876 final Session jcrSession = getSession(session); 877 try { 878 Node root = jcrSession.getRootNode(); 879 String path = getRepositoryPath(repositoryId); 880 if (root.hasNode(path)) { 881 root.getNode(path).remove(); 882 } 883 } catch (RepositoryException e) { 884 throw new MetadataRepositoryException(e.getMessage(), e); 885 } 886 } 887 888 @Override 889 public List<ArtifactMetadata> getArtifacts(RepositorySession session, String repositoryId) 890 throws MetadataRepositoryException { 891 final Session jcrSession = getSession(session); 892 List<ArtifactMetadata> artifacts; 893 894 String q = getArtifactQuery(repositoryId).toString(); 895 896 try { 897 Query query = jcrSession.getWorkspace().getQueryManager().createQuery(q, Query.JCR_SQL2); 898 QueryResult result = query.execute(); 899 900 artifacts = new ArrayList<>(); 901 for (Node n : JcrUtils.getNodes(result)) { 902 if (n.isNodeType(ARTIFACT_NODE_TYPE)) { 903 artifacts.add(getArtifactFromNode(repositoryId, n)); 904 } 905 } 906 } catch (RepositoryException e) { 907 throw new MetadataRepositoryException(e.getMessage(), e); 908 } 909 return artifacts; 910 } 911 912 private static StringBuilder getArtifactQuery(String repositoryId) { 913 return new StringBuilder(QUERY_ARTIFACT_1).append(getRepositoryContentPath(repositoryId)).append(QUERY_ARTIFACT_2); 914 } 915 916 @Override 917 public ProjectMetadata getProject(RepositorySession session, String repositoryId, String namespace, String projectId) 918 throws MetadataResolutionException { 919 final Session jcrSession; 920 try { 921 jcrSession = getSession(session); 922 } catch (MetadataRepositoryException e) { 923 throw new MetadataResolutionException(e.getMessage()); 924 } 925 ProjectMetadata metadata = null; 926 927 try { 928 Node root = jcrSession.getRootNode(); 929 930 // basically just checking it exists 931 String path = getProjectPath(repositoryId, namespace, projectId); 932 if (root.hasNode(path)) { 933 metadata = new ProjectMetadata(); 934 metadata.setId(projectId); 935 metadata.setNamespace(namespace); 936 } 937 } catch (RepositoryException e) { 938 throw new MetadataResolutionException(e.getMessage(), e); 939 } 940 941 return metadata; 942 } 943 944 private static Optional<License> getLicense(Node licenseNode) { 945 try { 946 String licenseName = licenseNode.getName(); 947 String licenseUrl = getPropertyString(licenseNode, "url"); 948 License license = new License(); 949 license.setName(licenseName); 950 license.setUrl(licenseUrl); 951 return Optional.of(license); 952 } catch (RepositoryException e) { 953 return Optional.empty(); 954 } 955 } 956 957 private static Optional<MailingList> getMailinglist(Node mailinglistNode) { 958 try { 959 String mailingListName = mailinglistNode.getName(); 960 MailingList mailinglist = new MailingList(); 961 mailinglist.setName(mailingListName); 962 mailinglist.setMainArchiveUrl(getPropertyString(mailinglistNode, "archive")); 963 String n = "otherArchives"; 964 if (mailinglistNode.hasProperty(n)) { 965 mailinglist.setOtherArchives(Arrays.asList(getPropertyString(mailinglistNode, n).split(","))); 966 } else { 967 mailinglist.setOtherArchives(Collections.<String>emptyList()); 968 } 969 mailinglist.setPostAddress(getPropertyString(mailinglistNode, "post")); 970 mailinglist.setSubscribeAddress(getPropertyString(mailinglistNode, "subscribe")); 971 mailinglist.setUnsubscribeAddress(getPropertyString(mailinglistNode, "unsubscribe")); 972 return Optional.of(mailinglist); 973 } catch (RepositoryException e) { 974 return Optional.empty(); 975 } 976 977 } 978 979 @Override 980 public ProjectVersionMetadata getProjectVersion(RepositorySession session, String repositoryId, String namespace, String projectId, 981 String projectVersion) 982 throws MetadataResolutionException { 983 final Session jcrSession; 984 try { 985 jcrSession = getSession(session); 986 } catch (MetadataRepositoryException e) { 987 throw new MetadataResolutionException(e.getMessage()); 988 } 989 ProjectVersionMetadata versionMetadata; 990 991 try { 992 Node root = jcrSession.getRootNode(); 993 994 String path = getProjectVersionPath(repositoryId, namespace, projectId, projectVersion); 995 if (!root.hasNode(path)) { 996 return null; 997 } 998 999 Node node = root.getNode(path); 1000 1001 versionMetadata = new ProjectVersionMetadata(); 1002 versionMetadata.setId(projectVersion); 1003 versionMetadata.setName(getPropertyString(node, "name")); 1004 versionMetadata.setDescription(getPropertyString(node, "description")); 1005 versionMetadata.setUrl(getPropertyString(node, "url")); 1006 versionMetadata.setIncomplete( 1007 node.hasProperty("incomplete") && node.getProperty("incomplete").getBoolean()); 1008 1009 // FIXME: decide how to treat these in the content repo 1010 String scmConnection = getPropertyString(node, "scm.connection"); 1011 String scmDeveloperConnection = getPropertyString(node, "scm.developerConnection"); 1012 String scmUrl = getPropertyString(node, "scm.url"); 1013 if (scmConnection != null || scmDeveloperConnection != null || scmUrl != null) { 1014 Scm scm = new Scm(); 1015 scm.setConnection(scmConnection); 1016 scm.setDeveloperConnection(scmDeveloperConnection); 1017 scm.setUrl(scmUrl); 1018 versionMetadata.setScm(scm); 1019 } 1020 1021 String ciSystem = getPropertyString(node, "ci.system"); 1022 String ciUrl = getPropertyString(node, "ci.url"); 1023 if (ciSystem != null || ciUrl != null) { 1024 CiManagement ci = new CiManagement(); 1025 ci.setSystem(ciSystem); 1026 ci.setUrl(ciUrl); 1027 versionMetadata.setCiManagement(ci); 1028 } 1029 1030 String issueSystem = getPropertyString(node, "issue.system"); 1031 String issueUrl = getPropertyString(node, "issue.url"); 1032 if (issueSystem != null || issueUrl != null) { 1033 IssueManagement issueManagement = new IssueManagement(); 1034 issueManagement.setSystem(issueSystem); 1035 issueManagement.setUrl(issueUrl); 1036 versionMetadata.setIssueManagement(issueManagement); 1037 } 1038 1039 String orgName = getPropertyString(node, "org.name"); 1040 String orgUrl = getPropertyString(node, "org.url"); 1041 if (orgName != null || orgUrl != null) { 1042 Organization org = new Organization(); 1043 org.setName(orgName); 1044 org.setUrl(orgUrl); 1045 versionMetadata.setOrganization(org); 1046 } 1047 1048 if (node.hasNode("licenses")) { 1049 Node licensesListNode = node.getNode("licenses"); 1050 List<License> licenseList = StreamSupport.stream(JcrUtils.getChildNodes(licensesListNode).spliterator(),false) 1051 .map(JcrMetadataRepository::getLicense).filter(Optional::isPresent) 1052 .map(Optional::get).sorted().collect(Collectors.toList()); 1053 versionMetadata.setLicenses(licenseList); 1054 } 1055 if (node.hasNode("mailinglists")) { 1056 Node mailinglistsListNode = node.getNode("mailinglists"); 1057 List<MailingList> mailinglistList = StreamSupport.stream(JcrUtils.getChildNodes(mailinglistsListNode).spliterator(), false) 1058 .map(JcrMetadataRepository::getMailinglist) 1059 .filter(Optional::isPresent) 1060 .map(Optional::get) 1061 .sorted().collect(Collectors.toList()); 1062 versionMetadata.setMailingLists(mailinglistList); 1063 } 1064 1065 if (node.hasNode("dependencies")) { 1066 Node dependenciesNode = node.getNode("dependencies"); 1067 for (Node n : JcrUtils.getChildNodes(dependenciesNode)) { 1068 if (n.isNodeType(DEPENDENCY_NODE_TYPE)) { 1069 Dependency dependency = new Dependency(); 1070 // FIXME: correct these properties 1071 dependency.setNamespace(getPropertyString(n, "namespace")); 1072 dependency.setProjectId(getPropertyString(n, "projectId")); 1073 dependency.setVersion(getPropertyString(n, "version")); 1074 dependency.setArtifactId(getPropertyString(n, "artifactId")); 1075 dependency.setClassifier(getPropertyString(n, "classifier")); 1076 dependency.setOptional(Boolean.valueOf(getPropertyString(n, "optional"))); 1077 dependency.setScope(getPropertyString(n, "scope")); 1078 dependency.setSystemPath(getPropertyString(n, "systemPath")); 1079 dependency.setType(getPropertyString(n, "type")); 1080 versionMetadata.addDependency(dependency); 1081 } 1082 } 1083 } 1084 1085 retrieveFacetProperties(versionMetadata, node); 1086 } catch (RepositoryException e) { 1087 throw new MetadataResolutionException(e.getMessage(), e); 1088 } 1089 1090 return versionMetadata; 1091 } 1092 1093 private void retrieveFacetProperties(FacetedMetadata metadata, Node node) throws RepositoryException { 1094 for (Node n : JcrUtils.getChildNodes(node)) { 1095 if (n.isNodeType(FACET_NODE_TYPE)) { 1096 String name = n.getName(); 1097 MetadataFacetFactory factory = metadataService.getFactory(name); 1098 if (factory == null) { 1099 log.error("Attempted to load unknown project version metadata facet: {}", name); 1100 } else { 1101 MetadataFacet facet = createFacetFromNode(factory, n); 1102 metadata.addFacet(facet); 1103 } 1104 } 1105 } 1106 } 1107 1108 @Override 1109 public List<String> getArtifactVersions(RepositorySession session, String repositoryId, String namespace, String projectId, 1110 String projectVersion) 1111 throws MetadataResolutionException { 1112 final Session jcrSession; 1113 try { 1114 jcrSession = getSession(session); 1115 } catch (MetadataRepositoryException e) { 1116 throw new MetadataResolutionException(e.getMessage()); 1117 } 1118 Set<String> versions = new LinkedHashSet<String>(); 1119 1120 try { 1121 Node root = jcrSession.getRootNode(); 1122 1123 Node node = root.getNode(getProjectVersionPath(repositoryId, namespace, projectId, projectVersion)); 1124 1125 for (Node n : JcrUtils.getChildNodes(node)) { 1126 versions.add(n.getProperty("version").getString()); 1127 } 1128 } catch (PathNotFoundException e) { 1129 // ignore repo not found for now 1130 } catch (RepositoryException e) { 1131 throw new MetadataResolutionException(e.getMessage(), e); 1132 } 1133 1134 return new ArrayList<>(versions); 1135 } 1136 1137 @Override 1138 public List<ProjectVersionReference> getProjectReferences(RepositorySession session, String repositoryId, String namespace, 1139 String projectId, String projectVersion) 1140 throws MetadataResolutionException { 1141 final Session jcrSession; 1142 try { 1143 jcrSession = getSession(session); 1144 } catch (MetadataRepositoryException e) { 1145 throw new MetadataResolutionException(e.getMessage()); 1146 } 1147 1148 List<ProjectVersionReference> references = new ArrayList<>(); 1149 1150 // TODO: bind variables instead 1151 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId 1152 + "/content]) AND [namespace]='" + namespace + "' AND [artifactId]='" + projectId + "'"; 1153 if (projectVersion != null) { 1154 q += " AND [version]='" + projectVersion + "'"; 1155 } 1156 try { 1157 Query query = jcrSession.getWorkspace().getQueryManager().createQuery(q, Query.JCR_SQL2); 1158 QueryResult result = query.execute(); 1159 1160 for (Node n : JcrUtils.getNodes(result)) { 1161 n = n.getParent(); // dependencies grouping element 1162 1163 n = n.getParent(); // project version 1164 String usedByProjectVersion = n.getName(); 1165 1166 n = n.getParent(); // project 1167 String usedByProject = n.getName(); 1168 1169 n = n.getParent(); // namespace 1170 String usedByNamespace = n.getProperty("namespace").getString(); 1171 1172 ProjectVersionReference ref = new ProjectVersionReference(); 1173 ref.setNamespace(usedByNamespace); 1174 ref.setProjectId(usedByProject); 1175 ref.setProjectVersion(usedByProjectVersion); 1176 ref.setReferenceType(ProjectVersionReference.ReferenceType.DEPENDENCY); 1177 references.add(ref); 1178 } 1179 } catch (RepositoryException e) { 1180 throw new MetadataResolutionException(e.getMessage(), e); 1181 } 1182 1183 return references; 1184 } 1185 1186 @Override 1187 public List<String> getRootNamespaces(RepositorySession session, String repositoryId) 1188 throws MetadataResolutionException { 1189 return this.getChildNamespaces(session, repositoryId, null); 1190 } 1191 1192 @Override 1193 public List<String> getChildNamespaces(RepositorySession session, String repositoryId, String baseNamespace) 1194 throws MetadataResolutionException { 1195 String path = baseNamespace != null 1196 ? getNamespacePath(repositoryId, baseNamespace) 1197 : getRepositoryContentPath(repositoryId); 1198 1199 try { 1200 return getNodeNames(getSession(session), path, NAMESPACE_MIXIN_TYPE); 1201 } catch (MetadataRepositoryException e) { 1202 throw new MetadataResolutionException(e.getMessage()); 1203 } 1204 } 1205 1206 @Override 1207 public List<String> getProjects(RepositorySession session, String repositoryId, String namespace) 1208 throws MetadataResolutionException { 1209 try { 1210 return getNodeNames(getSession(session), getNamespacePath(repositoryId, namespace), org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_MIXIN_TYPE); 1211 } catch (MetadataRepositoryException e) { 1212 throw new MetadataResolutionException(e.getMessage()); 1213 } 1214 } 1215 1216 @Override 1217 public List<String> getProjectVersions(RepositorySession session, String repositoryId, String namespace, String projectId) 1218 throws MetadataResolutionException { 1219 try { 1220 return getNodeNames(getSession(session), getProjectPath(repositoryId, namespace, projectId), PROJECT_VERSION_NODE_TYPE); 1221 } catch (MetadataRepositoryException e) { 1222 throw new MetadataResolutionException(e.getMessage()); 1223 } 1224 } 1225 1226 @Override 1227 public void removeTimestampedArtifact(RepositorySession session, ArtifactMetadata artifactMetadata, String baseVersion) 1228 throws MetadataRepositoryException { 1229 final Session jcrSession = getSession(session); 1230 String repositoryId = artifactMetadata.getRepositoryId(); 1231 1232 try { 1233 Node root = jcrSession.getRootNode(); 1234 String path = 1235 getProjectVersionPath(repositoryId, artifactMetadata.getNamespace(), artifactMetadata.getProject(), 1236 baseVersion); 1237 1238 if (root.hasNode(path)) { 1239 Node node = root.getNode(path); 1240 1241 for (Node n : JcrUtils.getChildNodes(node)) { 1242 if (n.isNodeType(ARTIFACT_NODE_TYPE)) { 1243 if (n.hasProperty("version")) { 1244 String version = n.getProperty("version").getString(); 1245 if (StringUtils.equals(version, artifactMetadata.getVersion())) { 1246 n.remove(); 1247 } 1248 } 1249 1250 } 1251 } 1252 } 1253 } catch (RepositoryException e) { 1254 throw new MetadataRepositoryException(e.getMessage(), e); 1255 } 1256 1257 1258 } 1259 1260 1261 @Override 1262 public void removeProjectVersion(RepositorySession session, String repoId, String namespace, String projectId, String projectVersion) 1263 throws MetadataRepositoryException { 1264 final Session jcrSession = getSession(session); 1265 try { 1266 1267 String path = getProjectPath(repoId, namespace, projectId); 1268 Node root = jcrSession.getRootNode(); 1269 1270 Node nodeAtPath = root.getNode(path); 1271 1272 for (Node node : JcrUtils.getChildNodes(nodeAtPath)) { 1273 if (node.isNodeType(PROJECT_VERSION_NODE_TYPE) && StringUtils.equals(projectVersion, 1274 node.getName())) { 1275 node.remove(); 1276 } 1277 } 1278 } catch (RepositoryException e) { 1279 throw new MetadataRepositoryException(e.getMessage(), e); 1280 } 1281 } 1282 1283 @Override 1284 public void removeArtifact(RepositorySession session, String repositoryId, String namespace, String projectId, String projectVersion, 1285 String id) 1286 throws MetadataRepositoryException { 1287 final Session jcrSession = getSession(session); 1288 try { 1289 Node root = jcrSession.getRootNode(); 1290 String path = getArtifactPath(repositoryId, namespace, projectId, projectVersion, id); 1291 if (root.hasNode(path)) { 1292 root.getNode(path).remove(); 1293 } 1294 1295 // remove version 1296 1297 path = getProjectPath(repositoryId, namespace, projectId); 1298 1299 Node nodeAtPath = root.getNode(path); 1300 1301 for (Node node : JcrUtils.getChildNodes(nodeAtPath)) { 1302 if (node.isNodeType(PROJECT_VERSION_NODE_TYPE) // 1303 && StringUtils.equals(node.getName(), projectVersion)) { 1304 node.remove(); 1305 } 1306 } 1307 } catch (RepositoryException e) { 1308 throw new MetadataRepositoryException(e.getMessage(), e); 1309 } 1310 } 1311 1312 @Override 1313 public void removeFacetFromArtifact(RepositorySession session, String repositoryId, String namespace, String project, String projectVersion, 1314 MetadataFacet metadataFacet) 1315 throws MetadataRepositoryException { 1316 final Session jcrSession = getSession(session); 1317 try { 1318 Node root = jcrSession.getRootNode(); 1319 String path = getProjectVersionPath(repositoryId, namespace, project, projectVersion); 1320 1321 if (root.hasNode(path)) { 1322 Node node = root.getNode(path); 1323 1324 for (Node n : JcrUtils.getChildNodes(node)) { 1325 if (n.isNodeType(ARTIFACT_NODE_TYPE)) { 1326 ArtifactMetadata artifactMetadata = getArtifactFromNode(repositoryId, n); 1327 log.debug("artifactMetadata: {}", artifactMetadata); 1328 MetadataFacet metadataFacetToRemove = artifactMetadata.getFacet(metadataFacet.getFacetId()); 1329 if (metadataFacetToRemove != null && metadataFacet.equals(metadataFacetToRemove)) { 1330 n.remove(); 1331 } 1332 } 1333 } 1334 } 1335 } catch (RepositoryException e) { 1336 throw new MetadataRepositoryException(e.getMessage(), e); 1337 } 1338 } 1339 1340 @Override 1341 public List<ArtifactMetadata> getArtifacts(RepositorySession session, String repositoryId, String namespace, String projectId, 1342 String projectVersion) 1343 throws MetadataResolutionException { 1344 final Session jcrSession; 1345 try { 1346 jcrSession = getSession(session); 1347 } catch (MetadataRepositoryException e) { 1348 throw new MetadataResolutionException(e.getMessage()); 1349 } 1350 List<ArtifactMetadata> artifacts = new ArrayList<>(); 1351 1352 try { 1353 Node root = jcrSession.getRootNode(); 1354 String path = getProjectVersionPath(repositoryId, namespace, projectId, projectVersion); 1355 1356 if (root.hasNode(path)) { 1357 Node node = root.getNode(path); 1358 1359 for (Node n : JcrUtils.getChildNodes(node)) { 1360 if (n.isNodeType(ARTIFACT_NODE_TYPE)) { 1361 artifacts.add(getArtifactFromNode(repositoryId, n)); 1362 } 1363 } 1364 } 1365 } catch (RepositoryException e) { 1366 throw new MetadataResolutionException(e.getMessage(), e); 1367 } 1368 1369 return artifacts; 1370 } 1371 1372 1373 @Override 1374 public void close() 1375 throws MetadataRepositoryException { 1376 } 1377 1378 1379 /** 1380 * Exact is ignored as we can't do exact search in any property, we need a key 1381 */ 1382 @Override 1383 public List<ArtifactMetadata> searchArtifacts(RepositorySession session, String repositoryId, String text, boolean exact) 1384 throws MetadataRepositoryException { 1385 return searchArtifacts(session, repositoryId, null, text, exact); 1386 } 1387 1388 @Override 1389 public List<ArtifactMetadata> searchArtifacts(RepositorySession session, String repositoryId, String key, String text, boolean exact) 1390 throws MetadataRepositoryException { 1391 final Session jcrSession = getSession(session); 1392 String theKey = key == null ? "*" : "[" + key + "]"; 1393 String projectVersionCondition = 1394 exact ? "(projectVersion." + theKey + " = $value)" : "contains([projectVersion]." + theKey + ", $value)"; 1395 String facetCondition = exact ? "(facet." + theKey + " = $value)" : "contains([facet]." + theKey + ", $value)"; 1396 String descendantCondition = repositoryId == null ? 1397 " AND [projectVersion].[jcr:path] LIKE '/repositories/%/content/%'" : 1398 " AND ISDESCENDANTNODE(projectVersion,'/" + getRepositoryContentPath(repositoryId) + "')"; 1399 List<ArtifactMetadata> result = new ArrayList<>(); 1400 if (key == null || (key != null && Arrays.binarySearch(PROJECT_VERSION_VERSION_PROPERTIES, key) >= 0)) { 1401 // We search only for project version properties if the key is a valid property name 1402 String q1 = 1403 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE 1404 + "] AS projectVersion LEFT OUTER JOIN [" + ARTIFACT_NODE_TYPE 1405 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE " + projectVersionCondition + descendantCondition; 1406 result.addAll(runJcrQuery(jcrSession, repositoryId, q1, ImmutableMap.of("value", text), false)); 1407 } 1408 String q2 = 1409 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE 1410 + "] AS projectVersion LEFT OUTER JOIN [" + ARTIFACT_NODE_TYPE 1411 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) LEFT OUTER JOIN [" + FACET_NODE_TYPE 1412 + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE " + facetCondition + descendantCondition; 1413 result.addAll(runJcrQuery(jcrSession, repositoryId, q2, ImmutableMap.of("value", text), false)); 1414 return result; 1415 } 1416 1417 private ArtifactMetadata getArtifactFromNode(String repositoryId, Node artifactNode) 1418 throws RepositoryException { 1419 String id = artifactNode.getName(); 1420 1421 ArtifactMetadata artifact = new ArtifactMetadata(); 1422 artifact.setId(id); 1423 artifact.setRepositoryId(repositoryId == null ? artifactNode.getAncestor(2).getName() : repositoryId); 1424 1425 Node projectVersionNode = artifactNode.getParent(); 1426 Node projectNode = projectVersionNode.getParent(); 1427 Node namespaceNode = projectNode.getParent(); 1428 1429 artifact.setNamespace(namespaceNode.getProperty("namespace").getString()); 1430 artifact.setProject(projectNode.getName()); 1431 artifact.setProjectVersion(projectVersionNode.getName()); 1432 artifact.setVersion(artifactNode.hasProperty("version") 1433 ? artifactNode.getProperty("version").getString() 1434 : projectVersionNode.getName()); 1435 1436 if (artifactNode.hasProperty(JCR_LAST_MODIFIED)) { 1437 artifact.setFileLastModified(artifactNode.getProperty(JCR_LAST_MODIFIED).getDate().getTimeInMillis()); 1438 } 1439 1440 if (artifactNode.hasProperty("whenGathered")) { 1441 Calendar cal = artifactNode.getProperty("whenGathered").getDate(); 1442 artifact.setWhenGathered(ZonedDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId())); 1443 } 1444 1445 if (artifactNode.hasProperty("size")) { 1446 artifact.setSize(artifactNode.getProperty("size").getLong()); 1447 } 1448 1449 Node cslistNode = getOrAddNodeByPath(artifactNode, "checksums"); 1450 NodeIterator csNodeIt = cslistNode.getNodes("*"); 1451 while (csNodeIt.hasNext()) { 1452 Node csNode = csNodeIt.nextNode(); 1453 if (csNode.isNodeType(CHECKSUM_NODE_TYPE)) { 1454 addChecksum(artifact, csNode); 1455 } 1456 } 1457 1458 retrieveFacetProperties(artifact, artifactNode); 1459 return artifact; 1460 } 1461 1462 private void addChecksum(ArtifactMetadata artifact, Node n) { 1463 try { 1464 ChecksumAlgorithm alg = ChecksumAlgorithm.valueOf(n.getProperty("type").getString()); 1465 String value = n.getProperty("value").getString(); 1466 artifact.setChecksum(alg, value); 1467 } catch (Throwable e) { 1468 log.error("Could not set checksum from node {}", n); 1469 } 1470 } 1471 1472 private static String getPropertyString(Node node, String name) 1473 throws RepositoryException { 1474 return node.hasProperty(name) ? node.getProperty(name).getString() : null; 1475 } 1476 1477 private List<String> getNodeNames(Session jcrSession, String path, String nodeType) 1478 throws MetadataResolutionException { 1479 1480 List<String> names = new ArrayList<>(); 1481 1482 try { 1483 Node root = jcrSession.getRootNode(); 1484 1485 Node nodeAtPath = root.getNode(path); 1486 1487 for (Node node : JcrUtils.getChildNodes(nodeAtPath)) { 1488 if (node.isNodeType(nodeType)) { 1489 names.add(node.getName()); 1490 } 1491 } 1492 } catch (PathNotFoundException e) { 1493 // ignore repo not found for now 1494 } catch (RepositoryException e) { 1495 throw new MetadataResolutionException(e.getMessage(), e); 1496 } 1497 1498 return names; 1499 } 1500 1501 private static String getRepositoryPath(String repositoryId) { 1502 return "repositories/" + repositoryId; 1503 } 1504 1505 private static String getRepositoryContentPath(String repositoryId) { 1506 return getRepositoryPath(repositoryId) + "/content"; 1507 } 1508 1509 private static String getFacetPath(String repositoryId, String facetId) { 1510 return StringUtils.isEmpty(facetId) ? getRepositoryPath(repositoryId) + "/facets" : 1511 getRepositoryPath(repositoryId) + "/facets/" + facetId; 1512 } 1513 1514 private static String getNamespacePath(String repositoryId, String namespace) { 1515 return getRepositoryContentPath(repositoryId) + "/" + namespace.replace('.', '/'); 1516 } 1517 1518 private static String getProjectPath(String repositoryId, String namespace, String projectId) { 1519 return getNamespacePath(repositoryId, namespace) + "/" + projectId; 1520 } 1521 1522 private static String getProjectVersionPath(String repositoryId, String namespace, String projectId, 1523 String projectVersion) { 1524 return getProjectPath(repositoryId, namespace, projectId) + "/" + projectVersion; 1525 } 1526 1527 private static String getArtifactPath(String repositoryId, String namespace, String projectId, 1528 String projectVersion, String id) { 1529 return getProjectVersionPath(repositoryId, namespace, projectId, projectVersion) + "/" + id; 1530 } 1531 1532 private Node getOrAddNodeByPath(Node baseNode, String name) 1533 throws RepositoryException { 1534 return getOrAddNodeByPath(baseNode, name, null); 1535 } 1536 1537 private Node getOrAddNodeByPath(Node baseNode, String name, String nodeType) throws RepositoryException { 1538 return getOrAddNodeByPath(baseNode, name, nodeType, false); 1539 } 1540 1541 private Node getOrAddNodeByPath(Node baseNode, String name, String nodeType, boolean primaryType) 1542 throws RepositoryException { 1543 log.debug("getOrAddNodeByPath " + baseNode + " " + name + " " + nodeType); 1544 Node node = baseNode; 1545 for (String n : name.split("/")) { 1546 if (nodeType != null && primaryType) { 1547 node = JcrUtils.getOrAddNode(node, n, nodeType); 1548 } else { 1549 node = JcrUtils.getOrAddNode(node, n); 1550 if (nodeType != null && !node.isNodeType(nodeType)) { 1551 node.addMixin(nodeType); 1552 } 1553 } 1554 if (!node.hasProperty("id")) { 1555 node.setProperty("id", n); 1556 } 1557 } 1558 return node; 1559 } 1560 1561 private Node getOrAddNodeByPath(Node baseNode, String name, String primaryType, String... mixinTypes) 1562 throws RepositoryException { 1563 log.debug("getOrAddNodeByPath baseNode={}, name={}, primary={}, mixin={}", baseNode, name, primaryType, mixinTypes); 1564 Node node = baseNode; 1565 for (String n : name.split("/")) { 1566 node = JcrUtils.getOrAddNode(node, n, primaryType); 1567 for (String mixin : mixinTypes) { 1568 if (mixin != null && !node.isNodeType(mixin)) { 1569 node.addMixin(mixin); 1570 } 1571 1572 } 1573 if (!node.hasProperty("id")) { 1574 node.setProperty("id", n); 1575 } 1576 } 1577 return node; 1578 } 1579 1580 private static String getFacetPath(String repositoryId, String facetId, String name) { 1581 return getFacetPath(repositoryId, facetId) + "/" + name; 1582 } 1583 1584 private Node getOrAddRepositoryNode(Session jcrSession, String repositoryId) 1585 throws RepositoryException { 1586 log.debug("getOrAddRepositoryNode " + repositoryId); 1587 Node root = jcrSession.getRootNode(); 1588 Node node = JcrUtils.getOrAddNode(root, "repositories"); 1589 log.debug("Repositories " + node); 1590 node = JcrUtils.getOrAddNode(node, repositoryId, REPOSITORY_NODE_TYPE); 1591 if (!node.hasProperty("id")) { 1592 node.setProperty("id", repositoryId); 1593 } 1594 return node; 1595 } 1596 1597 private Node getOrAddRepositoryContentNode(Session jcrSession, String repositoryId) 1598 throws RepositoryException { 1599 Node node = getOrAddRepositoryNode(jcrSession, repositoryId); 1600 return JcrUtils.getOrAddNode(node, "content", CONTENT_NODE_TYPE); 1601 } 1602 1603 private Node getOrAddNamespaceNode(Session jcrSession, String repositoryId, String namespace) 1604 throws RepositoryException { 1605 Node repo = getOrAddRepositoryContentNode(jcrSession, repositoryId); 1606 return getOrAddNodeByPath(repo, namespace.replace('.', '/'), FOLDER_TYPE, NAMESPACE_MIXIN_TYPE); 1607 } 1608 1609 private Node getOrAddProjectNode(Session jcrSession, String repositoryId, String namespace, String projectId) 1610 throws RepositoryException { 1611 Node namespaceNode = getOrAddNamespaceNode(jcrSession, repositoryId, namespace); 1612 Node node = JcrUtils.getOrAddNode(namespaceNode, projectId, FOLDER_TYPE); 1613 if (!node.isNodeType(PROJECT_MIXIN_TYPE)) { 1614 node.addMixin(PROJECT_MIXIN_TYPE); 1615 } 1616 if (!node.hasProperty("id")) { 1617 node.setProperty("id", projectId); 1618 } 1619 return node; 1620 } 1621 1622 private Node getOrAddProjectVersionNode(Session jcrSession, String repositoryId, String namespace, String projectId, 1623 String projectVersion) 1624 throws RepositoryException { 1625 Node projectNode = getOrAddProjectNode(jcrSession, repositoryId, namespace, projectId); 1626 log.debug("Project node {}", projectNode); 1627 Node projectVersionNode = JcrUtils.getOrAddNode(projectNode, projectVersion, PROJECT_VERSION_NODE_TYPE); 1628 if (!projectVersionNode.hasProperty("id")) { 1629 projectVersionNode.setProperty("id", projectVersion); 1630 } 1631 1632 log.debug("Project version node {}", projectVersionNode); 1633 return projectVersionNode; 1634 } 1635 1636 private Node getOrAddArtifactNode(Session jcrSession, String repositoryId, String namespace, String projectId, String projectVersion, 1637 String id) 1638 throws RepositoryException { 1639 Node versionNode = getOrAddProjectVersionNode(jcrSession, repositoryId, namespace, projectId, projectVersion); 1640 Node node = JcrUtils.getOrAddNode(versionNode, id, ARTIFACT_NODE_TYPE); 1641 if (!node.hasProperty("id")) { 1642 node.setProperty("id", id); 1643 } 1644 return node; 1645 } 1646 1647 private Node findArtifactNode(Session jcrSession, String namespace, String projectId, 1648 String projectVersion, String id) throws RepositoryException { 1649 1650 if (namespace==null || projectId==null||projectVersion==null||id==null) { 1651 return null; 1652 } 1653 Node root = jcrSession.getRootNode(); 1654 Node node = JcrUtils.getOrAddNode(root, "repositories"); 1655 for (Node n : JcrUtils.getChildNodes(node)) { 1656 String repositoryId = n.getName(); 1657 Node repo = getOrAddRepositoryContentNode(jcrSession, repositoryId); 1658 Node nsNode = JcrUtils.getNodeIfExists(repo, StringUtils.replaceChars(namespace, '.', '/')); 1659 if (nsNode!=null) { 1660 Node projNode = JcrUtils.getNodeIfExists(nsNode, projectId); 1661 if (projNode !=null ) { 1662 Node projVersionNode = JcrUtils.getNodeIfExists(projNode, projectVersion); 1663 if (projVersionNode != null) { 1664 return JcrUtils.getNodeIfExists(projVersionNode, id); 1665 } 1666 } 1667 } 1668 } 1669 1670 return null; 1671 } 1672 1673 private static Calendar createCalendar(ZonedDateTime time) { 1674 return GregorianCalendar.from(time); 1675 } 1676 1677 private String join(Collection<String> ids) { 1678 if (ids != null && !ids.isEmpty()) { 1679 StringBuilder s = new StringBuilder(); 1680 for (String id : ids) { 1681 s.append(id); 1682 s.append(","); 1683 } 1684 return s.substring(0, s.length() - 1); 1685 } 1686 return null; 1687 } 1688 1689 1690 @Override 1691 public void populateStatistics(RepositorySession repositorySession, MetadataRepository repository, String repositoryId, 1692 RepositoryStatistics repositoryStatistics) 1693 throws MetadataRepositoryException { 1694 if (!(repository instanceof JcrMetadataRepository)) { 1695 throw new MetadataRepositoryException( 1696 "The statistics population is only possible for JcrMetdataRepository implementations"); 1697 } 1698 Session session = getSession(repositorySession); 1699 // TODO: these may be best as running totals, maintained by observations on the properties in JCR 1700 1701 try { 1702 QueryManager queryManager = session.getWorkspace().getQueryManager(); 1703 1704 // TODO: Check, if this is still the case - Switched to Jackrabbit OAK with archiva 3.0 1705 // Former statement: JCR-SQL2 query will not complete on a large repo in Jackrabbit 2.2.0 - see JCR-2835 1706 // Using the JCR-SQL2 variants gives 1707 // "org.apache.lucene.search.BooleanQuery$TooManyClauses: maxClauseCount is set to 1024" 1708// String whereClause = "WHERE ISDESCENDANTNODE([/repositories/" + repositoryId + "/content])"; 1709// Query query = queryManager.createQuery( "SELECT size FROM [archiva:artifact] " + whereClause, 1710// Query.JCR_SQL2 ); 1711 String whereClause = "WHERE ISDESCENDANTNODE([/repositories/" + repositoryId + "/content])"; 1712 Query query = queryManager.createQuery("SELECT type,size FROM [" + ARTIFACT_NODE_TYPE + "] " + whereClause, Query.JCR_SQL2); 1713 1714 QueryResult queryResult = query.execute(); 1715 1716 Map<String, Integer> totalByType = new HashMap<>(); 1717 long totalSize = 0, totalArtifacts = 0; 1718 for (Row row : JcrUtils.getRows(queryResult)) { 1719 Node n = row.getNode(); 1720 log.debug("Result node {}", n); 1721 totalSize += row.getValue("size").getLong(); 1722 1723 String type; 1724 if (n.hasNode(MavenArtifactFacet.FACET_ID)) { 1725 Node facetNode = n.getNode(MavenArtifactFacet.FACET_ID); 1726 type = facetNode.getProperty("type").getString(); 1727 } else { 1728 type = "Other"; 1729 } 1730 Integer prev = totalByType.get(type); 1731 totalByType.put(type, prev != null ? prev + 1 : 1); 1732 1733 totalArtifacts++; 1734 } 1735 1736 repositoryStatistics.setTotalArtifactCount(totalArtifacts); 1737 repositoryStatistics.setTotalArtifactFileSize(totalSize); 1738 for (Map.Entry<String, Integer> entry : totalByType.entrySet()) { 1739 log.info("Setting count for type: {} = {}", entry.getKey(), entry.getValue()); 1740 repositoryStatistics.setTotalCountForType(entry.getKey(), entry.getValue()); 1741 } 1742 1743 // The query ordering is a trick to ensure that the size is correct, otherwise due to lazy init it will be -1 1744// query = queryManager.createQuery( "SELECT * FROM [archiva:project] " + whereClause, Query.JCR_SQL2 ); 1745 query = queryManager.createQuery("SELECT * FROM [archiva:project] " + whereClause + " ORDER BY [jcr:score]", 1746 Query.JCR_SQL2); 1747 repositoryStatistics.setTotalProjectCount(query.execute().getRows().getSize()); 1748 1749// query = queryManager.createQuery( 1750// "SELECT * FROM [archiva:namespace] " + whereClause + " AND namespace IS NOT NULL", Query.JCR_SQL2 ); 1751 query = queryManager.createQuery( 1752 "SELECT * FROM [archiva:namespace] " + whereClause + " AND namespace IS NOT NULL ORDER BY [jcr:score]", 1753 Query.JCR_SQL2); 1754 repositoryStatistics.setTotalGroupCount(query.execute().getRows().getSize()); 1755 } catch (RepositoryException e) { 1756 throw new MetadataRepositoryException(e.getMessage(), e); 1757 } 1758 } 1759 1760 1761 public Session login() throws RepositoryException { 1762 return repository.login(new SimpleCredentials("admin", "admin".toCharArray())); 1763 } 1764 1765 private static boolean isArtifactNodeType(Node n) { 1766 try { 1767 return n != null && n.isNodeType(ARTIFACT_NODE_TYPE); 1768 } catch (RepositoryException e) { 1769 return false; 1770 } 1771 } 1772 1773 private Optional<ArtifactMetadata> getArtifactOptional(final String repositoryId, final Node n) { 1774 try { 1775 return Optional.ofNullable(getArtifactFromNode(repositoryId, n)); 1776 } catch (RepositoryException e) { 1777 return Optional.empty(); 1778 } 1779 } 1780 1781 private Optional<ArtifactMetadata> getArtifactOptional(final String repositoryId, final Row row) { 1782 try { 1783 return Optional.of(getArtifactFromNode(repositoryId, row.getNode("artifact"))); 1784 } catch (RepositoryException e) { 1785 return Optional.empty(); 1786 } 1787 } 1788 1789 @Override 1790 public Stream<ArtifactMetadata> getArtifactStream(final RepositorySession session, final String repositoryId, 1791 final String namespace, final String projectId, final String projectVersion, 1792 final QueryParameter queryParameter) throws MetadataResolutionException { 1793 final Session jcrSession; 1794 try { 1795 jcrSession = getSession(session); 1796 } catch (MetadataRepositoryException e) { 1797 throw new MetadataResolutionException(e.getMessage()); 1798 } 1799 1800 try { 1801 Node root = jcrSession.getRootNode(); 1802 String path = getProjectVersionPath(repositoryId, namespace, projectId, projectVersion); 1803 1804 if (root.hasNode(path)) { 1805 Node node = root.getNode(path); 1806 return StreamSupport.stream(JcrUtils.getChildNodes(node).spliterator(), false).filter(JcrMetadataRepository::isArtifactNodeType) 1807 .map(n -> getArtifactOptional(repositoryId, n)) 1808 .map(Optional::get).skip(queryParameter.getOffset()).limit(queryParameter.getLimit()); 1809 } else { 1810 return Stream.empty(); 1811 } 1812 } catch (RepositoryException e) { 1813 throw new MetadataResolutionException(e.getMessage(), e); 1814 } 1815 } 1816 1817 @Override 1818 public Stream<ArtifactMetadata> getArtifactStream(final RepositorySession session, final String repositoryId, 1819 final QueryParameter queryParameter) throws MetadataResolutionException { 1820 final Session jcrSession; 1821 try { 1822 jcrSession = getSession(session); 1823 } catch (MetadataRepositoryException e) { 1824 throw new MetadataResolutionException(e.getMessage(), e); 1825 } 1826 List<ArtifactMetadata> artifacts; 1827 1828 String q = getArtifactQuery(repositoryId).toString(); 1829 1830 try { 1831 Query query = jcrSession.getWorkspace().getQueryManager().createQuery(q, Query.JCR_SQL2); 1832 QueryResult result = query.execute(); 1833 1834 return StreamSupport.stream(createResultSpliterator(result, getArtifactFromRowFunc(repositoryId)), false) 1835 .filter(Optional::isPresent).map(Optional::get) 1836 .skip(queryParameter.getOffset()).limit(queryParameter.getLimit()); 1837 1838 } catch (RepositoryException | MetadataRepositoryException e) { 1839 throw new MetadataResolutionException(e.getMessage(), e); 1840 } 1841 1842 } 1843}