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