1 package org.apache.archiva.metadata.repository.jcr;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.archiva.metadata.model.ArtifactMetadata;
23 import org.apache.archiva.metadata.model.CiManagement;
24 import org.apache.archiva.metadata.model.Dependency;
25 import org.apache.archiva.metadata.model.IssueManagement;
26 import org.apache.archiva.metadata.model.License;
27 import org.apache.archiva.metadata.model.MailingList;
28 import org.apache.archiva.metadata.model.MetadataFacet;
29 import org.apache.archiva.metadata.model.MetadataFacetFactory;
30 import org.apache.archiva.metadata.model.Organization;
31 import org.apache.archiva.metadata.model.ProjectMetadata;
32 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
33 import org.apache.archiva.metadata.model.ProjectVersionReference;
34 import org.apache.archiva.metadata.model.Scm;
35 import org.apache.archiva.metadata.repository.MetadataRepository;
36 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
37 import org.apache.archiva.metadata.repository.MetadataResolutionException;
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.jackrabbit.commons.JcrUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import javax.jcr.NamespaceRegistry;
44 import javax.jcr.Node;
45 import javax.jcr.NodeIterator;
46 import javax.jcr.PathNotFoundException;
47 import javax.jcr.Property;
48 import javax.jcr.Repository;
49 import javax.jcr.RepositoryException;
50 import javax.jcr.Session;
51 import javax.jcr.SimpleCredentials;
52 import javax.jcr.ValueFactory;
53 import javax.jcr.Workspace;
54 import javax.jcr.nodetype.NodeTypeManager;
55 import javax.jcr.nodetype.NodeTypeTemplate;
56 import javax.jcr.query.Query;
57 import javax.jcr.query.QueryResult;
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.Calendar;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.Date;
64 import java.util.HashMap;
65 import java.util.Iterator;
66 import java.util.LinkedHashSet;
67 import java.util.List;
72 * TODO below: revise storage format for project version metadata
73 * TODO revise reference storage
75 public class JcrMetadataRepository
76 implements MetadataRepository
79 private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
81 static final String NAMESPACE_NODE_TYPE = "archiva:namespace";
83 static final String PROJECT_NODE_TYPE = "archiva:project";
85 static final String PROJECT_VERSION_NODE_TYPE = "archiva:projectVersion";
87 static final String ARTIFACT_NODE_TYPE = "archiva:artifact";
89 static final String FACET_NODE_TYPE = "archiva:facet";
91 private static final String DEPENDENCY_NODE_TYPE = "archiva:dependency";
93 private final Map<String, MetadataFacetFactory> metadataFacetFactories;
95 private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
97 private Repository repository;
99 private Session jcrSession;
101 public JcrMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories, Repository repository )
102 throws RepositoryException
104 this.metadataFacetFactories = metadataFacetFactories;
105 this.repository = repository;
109 static void initialize( Session session )
110 throws RepositoryException
113 // TODO: consider using namespaces for facets instead of the current approach:
114 // (if used, check if actually called by normal injection)
115 // for ( String facetId : metadataFacetFactories.keySet() )
117 // session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
119 Workspace workspace = session.getWorkspace();
120 NamespaceRegistry registry = workspace.getNamespaceRegistry();
122 if ( !Arrays.asList( registry.getPrefixes() ).contains( "archiva" ) )
124 registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
127 NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
128 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.NAMESPACE_NODE_TYPE );
129 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_NODE_TYPE );
130 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_VERSION_NODE_TYPE );
131 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.ARTIFACT_NODE_TYPE );
132 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.FACET_NODE_TYPE );
133 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.DEPENDENCY_NODE_TYPE );
137 private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
138 throws RepositoryException
140 NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
141 nodeType.setMixin( true );
142 nodeType.setName( name );
144 // for now just don't re-create - but in future if we change the definition, make sure to remove first as an
146 if ( !nodeTypeManager.hasNodeType( name ) )
148 nodeTypeManager.registerNodeType( nodeType, false );
153 public void updateProject( String repositoryId, ProjectMetadata project )
154 throws MetadataRepositoryException
156 updateProject( repositoryId, project.getNamespace(), project.getId() );
159 private void updateProject( String repositoryId, String namespace, String projectId )
160 throws MetadataRepositoryException
162 updateNamespace( repositoryId, namespace );
166 getOrAddProjectNode( repositoryId, namespace, projectId );
168 catch ( RepositoryException e )
170 throw new MetadataRepositoryException( e.getMessage(), e );
175 public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
176 ArtifactMetadata artifactMeta )
177 throws MetadataRepositoryException
179 updateNamespace( repositoryId, namespace );
184 getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion, artifactMeta.getId() );
186 Calendar cal = Calendar.getInstance();
187 cal.setTime( artifactMeta.getFileLastModified() );
188 node.setProperty( JCR_LAST_MODIFIED, cal );
190 cal = Calendar.getInstance();
191 cal.setTime( artifactMeta.getWhenGathered() );
192 node.setProperty( "whenGathered", cal );
194 node.setProperty( "size", artifactMeta.getSize() );
195 node.setProperty( "md5", artifactMeta.getMd5() );
196 node.setProperty( "sha1", artifactMeta.getSha1() );
198 node.setProperty( "version", artifactMeta.getVersion() );
200 // iterate over available facets to update/add/remove from the artifactMetadata
201 for ( String facetId : metadataFacetFactories.keySet() )
203 MetadataFacet metadataFacet = artifactMeta.getFacet( facetId );
204 if ( metadataFacet == null )
208 if ( node.hasNode( facetId ) )
210 node.getNode( facetId ).remove();
212 if ( metadataFacet != null )
214 // recreate, to ensure properties are removed
215 Node n = node.addNode( facetId );
216 n.addMixin( FACET_NODE_TYPE );
218 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
220 n.setProperty( entry.getKey(), entry.getValue() );
225 catch ( RepositoryException e )
227 throw new MetadataRepositoryException( e.getMessage(), e );
232 public void updateProjectVersion( String repositoryId, String namespace, String projectId,
233 ProjectVersionMetadata versionMetadata )
234 throws MetadataRepositoryException
236 updateProject( repositoryId, namespace, projectId );
241 getOrAddProjectVersionNode( repositoryId, namespace, projectId, versionMetadata.getId() );
243 versionNode.setProperty( "name", versionMetadata.getName() );
244 versionNode.setProperty( "description", versionMetadata.getDescription() );
245 versionNode.setProperty( "url", versionMetadata.getUrl() );
246 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
248 // FIXME: decide how to treat these in the content repo
249 if ( versionMetadata.getScm() != null )
251 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
252 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
253 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
255 if ( versionMetadata.getCiManagement() != null )
257 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
258 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
260 if ( versionMetadata.getIssueManagement() != null )
262 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
263 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
265 if ( versionMetadata.getOrganization() != null )
267 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
268 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
271 for ( License license : versionMetadata.getLicenses() )
273 versionNode.setProperty( "license." + i + ".name", license.getName() );
274 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
278 for ( MailingList mailingList : versionMetadata.getMailingLists() )
280 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
281 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
282 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
283 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
284 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
285 versionNode.setProperty( "mailingList." + i + ".otherArchives",
286 join( mailingList.getOtherArchives() ) );
290 if ( !versionMetadata.getDependencies().isEmpty() )
292 Node dependenciesNode = JcrUtils.getOrAddNode( versionNode, "dependencies" );
294 for ( Dependency dependency : versionMetadata.getDependencies() )
296 // Note that we deliberately don't alter the namespace path - not enough dependencies for
297 // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each
298 // component of the ID as that creates extra depth and causes a great cost in space and memory
300 // FIXME: change group ID to namespace
301 // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now.
302 // This won't support types where the extension doesn't match the type.
303 // (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM)
305 dependency.getGroupId() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion();
306 if ( dependency.getClassifier() != null )
308 id += "-" + dependency.getClassifier();
310 id += "." + dependency.getType();
312 Node n = JcrUtils.getOrAddNode( dependenciesNode, id );
313 n.addMixin( DEPENDENCY_NODE_TYPE );
315 // FIXME: remove temp code just to make it keep working
316 n.setProperty( "groupId", dependency.getGroupId() );
317 n.setProperty( "artifactId", dependency.getArtifactId() );
318 n.setProperty( "version", dependency.getVersion() );
319 n.setProperty( "type", dependency.getType() );
320 n.setProperty( "classifier", dependency.getClassifier() );
321 n.setProperty( "scope", dependency.getScope() );
322 n.setProperty( "systemPath", dependency.getSystemPath() );
323 n.setProperty( "optional", dependency.isOptional() );
325 // node has no native content at this time, just facets
326 // no need to list a type as it's implied by the path. Parents are Maven specific.
328 // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet
329 // (should also have been added to the Dependency)
331 // TODO: add a property that is a weak reference to the originating artifact, creating it if
332 // necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an
333 // artifact, which gives a different meaning to "incomplete" which is a known local project
334 // that doesn't have metadata yet but has artifacts). (Though we may want to give it the
335 // artifact mixin and another property to identify all non-local artifacts for the closure
340 for ( MetadataFacet facet : versionMetadata.getFacetList() )
342 // recreate, to ensure properties are removed
343 if ( versionNode.hasNode( facet.getFacetId() ) )
345 versionNode.getNode( facet.getFacetId() ).remove();
347 Node n = versionNode.addNode( facet.getFacetId() );
348 n.addMixin( FACET_NODE_TYPE );
350 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
352 n.setProperty( entry.getKey(), entry.getValue() );
356 catch ( RepositoryException e )
358 throw new MetadataRepositoryException( e.getMessage(), e );
363 public void updateNamespace( String repositoryId, String namespace )
364 throws MetadataRepositoryException
368 Node node = getOrAddNamespaceNode( repositoryId, namespace );
369 node.setProperty( "namespace", namespace );
371 catch ( RepositoryException e )
373 throw new MetadataRepositoryException( e.getMessage(), e );
378 public void removeProject( String repositoryId, String namespace, String projectId )
379 throws MetadataRepositoryException
383 Node root = getJcrSession().getRootNode();
384 String namespacePath = getNamespacePath( repositoryId, namespace );
386 if ( root.hasNode( namespacePath ) )
388 Iterator<Node> nodeIterator = JcrUtils.getChildNodes( root.getNode( namespacePath ) ).iterator();
389 while ( nodeIterator.hasNext() )
391 Node node = nodeIterator.next();
392 if ( node.isNodeType( PROJECT_NODE_TYPE ) && projectId.equals( node.getName() ) )
400 catch ( RepositoryException e )
402 throw new MetadataRepositoryException( e.getMessage(), e );
409 public boolean hasMetadataFacet( String repositoryId, String facetId )
410 throws MetadataRepositoryException
414 Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
415 return node.getNodes().hasNext();
417 catch ( PathNotFoundException e )
419 // ignored - the facet doesn't exist, so return false
422 catch ( RepositoryException e )
424 throw new MetadataRepositoryException( e.getMessage(), e );
429 public List<String> getMetadataFacets( String repositoryId, String facetId )
430 throws MetadataRepositoryException
432 List<String> facets = new ArrayList<>();
436 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
437 // are paths themselves
438 Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
440 // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
442 recurse( facets, "", node );
444 catch ( PathNotFoundException e )
446 // ignored - the facet doesn't exist, so return the empty list
448 catch ( RepositoryException e )
450 throw new MetadataRepositoryException( e.getMessage(), e );
455 private void recurse( List<String> facets, String prefix, Node node )
456 throws RepositoryException
458 for ( Node n : JcrUtils.getChildNodes( node ) )
460 String name = prefix + "/" + n.getName();
463 recurse( facets, name, n );
467 // strip leading / first
468 facets.add( name.substring( 1 ) );
474 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
475 throws MetadataRepositoryException
477 MetadataFacet metadataFacet = null;
480 Node root = getJcrSession().getRootNode();
481 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
483 if ( metadataFacetFactories == null )
485 return metadataFacet;
488 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
489 if ( metadataFacetFactory != null )
491 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
492 Map<String, String> map = new HashMap<>();
493 for ( Property property : JcrUtils.getProperties( node ) )
495 String p = property.getName();
496 if ( !p.startsWith( "jcr:" ) )
498 map.put( p, property.getString() );
501 metadataFacet.fromProperties( map );
504 catch ( PathNotFoundException e )
506 // ignored - the facet doesn't exist, so return null
508 catch ( RepositoryException e )
510 throw new MetadataRepositoryException( e.getMessage(), e );
512 return metadataFacet;
516 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
517 throws MetadataRepositoryException
521 Node repo = getOrAddRepositoryNode( repositoryId );
522 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
524 String id = metadataFacet.getFacetId();
525 Node facetNode = JcrUtils.getOrAddNode( facets, id );
527 Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
529 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
531 node.setProperty( entry.getKey(), entry.getValue() );
534 catch ( RepositoryException e )
536 throw new MetadataRepositoryException( e.getMessage(), e );
541 public void removeNamespace( String repositoryId, String projectId )
542 throws MetadataRepositoryException
546 Node root = getJcrSession().getRootNode();
547 String path = getNamespacePath( repositoryId, projectId );
548 if ( root.hasNode( path ) )
550 Node node = root.getNode( path );
551 if ( node.isNodeType( NAMESPACE_NODE_TYPE ) )
557 catch ( RepositoryException e )
559 throw new MetadataRepositoryException( e.getMessage(), e );
564 public void removeMetadataFacets( String repositoryId, String facetId )
565 throws MetadataRepositoryException
569 Node root = getJcrSession().getRootNode();
570 String path = getFacetPath( repositoryId, facetId );
571 if ( root.hasNode( path ) )
573 root.getNode( path ).remove();
576 catch ( RepositoryException e )
578 throw new MetadataRepositoryException( e.getMessage(), e );
583 public void removeMetadataFacet( String repositoryId, String facetId, String name )
584 throws MetadataRepositoryException
588 Node root = getJcrSession().getRootNode();
589 String path = getFacetPath( repositoryId, facetId, name );
590 if ( root.hasNode( path ) )
592 Node node = root.getNode( path );
595 // also remove empty container nodes
596 Node parent = node.getParent();
600 while ( !node.hasNodes() );
603 catch ( RepositoryException e )
605 throw new MetadataRepositoryException( e.getMessage(), e );
610 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
611 throws MetadataRepositoryException
613 List<ArtifactMetadata> artifacts;
615 String q = getArtifactQuery( repoId );
617 if ( startTime != null )
619 q += " AND [whenGathered] >= $start";
621 if ( endTime != null )
623 q += " AND [whenGathered] <= $end";
628 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
629 ValueFactory valueFactory = getJcrSession().getValueFactory();
630 if ( startTime != null )
632 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
634 if ( endTime != null )
636 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
638 QueryResult result = query.execute();
640 artifacts = new ArrayList<>();
641 for ( Node n : JcrUtils.getNodes( result ) )
643 artifacts.add( getArtifactFromNode( repoId, n ) );
646 catch ( RepositoryException e )
648 throw new MetadataRepositoryException( e.getMessage(), e );
654 public Collection<String> getRepositories()
655 throws MetadataRepositoryException
657 List<String> repositories;
661 Node root = getJcrSession().getRootNode();
662 if ( root.hasNode( "repositories" ) )
664 Node node = root.getNode( "repositories" );
666 repositories = new ArrayList<>();
667 NodeIterator i = node.getNodes();
668 while ( i.hasNext() )
670 Node n = i.nextNode();
671 repositories.add( n.getName() );
676 repositories = Collections.emptyList();
679 catch ( RepositoryException e )
681 throw new MetadataRepositoryException( e.getMessage(), e );
687 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
688 throws MetadataRepositoryException
690 List<ArtifactMetadata> artifacts;
692 String q = getArtifactQuery( repositoryId ) + " AND ([sha1] = $checksum OR [md5] = $checksum)";
696 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
697 ValueFactory valueFactory = getJcrSession().getValueFactory();
698 query.bindValue( "checksum", valueFactory.createValue( checksum ) );
699 QueryResult result = query.execute();
701 artifacts = new ArrayList<>();
702 for ( Node n : JcrUtils.getNodes( result ) )
704 artifacts.add( getArtifactFromNode( repositoryId, n ) );
707 catch ( RepositoryException e )
709 throw new MetadataRepositoryException( e.getMessage(), e );
716 public void removeRepository( String repositoryId )
717 throws MetadataRepositoryException
721 Node root = getJcrSession().getRootNode();
722 String path = getRepositoryPath( repositoryId );
723 if ( root.hasNode( path ) )
725 root.getNode( path ).remove();
728 catch ( RepositoryException e )
730 throw new MetadataRepositoryException( e.getMessage(), e );
735 public List<ArtifactMetadata> getArtifacts( String repositoryId )
736 throws MetadataRepositoryException
738 List<ArtifactMetadata> artifacts;
740 String q = getArtifactQuery( repositoryId );
744 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
745 QueryResult result = query.execute();
747 artifacts = new ArrayList<>();
748 for ( Node n : JcrUtils.getNodes( result ) )
750 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
752 artifacts.add( getArtifactFromNode( repositoryId, n ) );
756 catch ( RepositoryException e )
758 throw new MetadataRepositoryException( e.getMessage(), e );
763 private static String getArtifactQuery( String repositoryId )
765 return "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/" +
766 getRepositoryContentPath( repositoryId ) + "')";
770 public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
771 throws MetadataResolutionException
773 ProjectMetadata metadata = null;
777 Node root = getJcrSession().getRootNode();
779 // basically just checking it exists
780 String path = getProjectPath( repositoryId, namespace, projectId );
781 if ( root.hasNode( path ) )
783 metadata = new ProjectMetadata();
784 metadata.setId( projectId );
785 metadata.setNamespace( namespace );
788 catch ( RepositoryException e )
790 throw new MetadataResolutionException( e.getMessage(), e );
797 public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
798 String projectVersion )
799 throws MetadataResolutionException
801 ProjectVersionMetadata versionMetadata;
805 Node root = getJcrSession().getRootNode();
807 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
808 if ( !root.hasNode( path ) )
813 Node node = root.getNode( path );
815 versionMetadata = new ProjectVersionMetadata();
816 versionMetadata.setId( projectVersion );
817 versionMetadata.setName( getPropertyString( node, "name" ) );
818 versionMetadata.setDescription( getPropertyString( node, "description" ) );
819 versionMetadata.setUrl( getPropertyString( node, "url" ) );
820 versionMetadata.setIncomplete(
821 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
823 // FIXME: decide how to treat these in the content repo
824 String scmConnection = getPropertyString( node, "scm.connection" );
825 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
826 String scmUrl = getPropertyString( node, "scm.url" );
827 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
830 scm.setConnection( scmConnection );
831 scm.setDeveloperConnection( scmDeveloperConnection );
832 scm.setUrl( scmUrl );
833 versionMetadata.setScm( scm );
836 String ciSystem = getPropertyString( node, "ci.system" );
837 String ciUrl = getPropertyString( node, "ci.url" );
838 if ( ciSystem != null || ciUrl != null )
840 CiManagement ci = new CiManagement();
841 ci.setSystem( ciSystem );
843 versionMetadata.setCiManagement( ci );
846 String issueSystem = getPropertyString( node, "issue.system" );
847 String issueUrl = getPropertyString( node, "issue.url" );
848 if ( issueSystem != null || issueUrl != null )
850 IssueManagement issueManagement = new IssueManagement();
851 issueManagement.setSystem( issueSystem );
852 issueManagement.setUrl( issueUrl );
853 versionMetadata.setIssueManagement( issueManagement );
856 String orgName = getPropertyString( node, "org.name" );
857 String orgUrl = getPropertyString( node, "org.url" );
858 if ( orgName != null || orgUrl != null )
860 Organization org = new Organization();
861 org.setName( orgName );
862 org.setUrl( orgUrl );
863 versionMetadata.setOrganization( org );
866 boolean done = false;
870 String licenseName = getPropertyString( node, "license." + i + ".name" );
871 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
872 if ( licenseName != null || licenseUrl != null )
874 License license = new License();
875 license.setName( licenseName );
876 license.setUrl( licenseUrl );
877 versionMetadata.addLicense( license );
890 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
891 if ( mailingListName != null )
893 MailingList mailingList = new MailingList();
894 mailingList.setName( mailingListName );
895 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
896 String n = "mailingList." + i + ".otherArchives";
897 if ( node.hasProperty( n ) )
899 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
903 mailingList.setOtherArchives( Collections.<String>emptyList() );
905 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
906 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
907 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
908 versionMetadata.addMailingList( mailingList );
917 if ( node.hasNode( "dependencies" ) )
919 Node dependenciesNode = node.getNode( "dependencies" );
920 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
922 if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
924 Dependency dependency = new Dependency();
925 // FIXME: correct these properties
926 dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
927 dependency.setGroupId( getPropertyString( n, "groupId" ) );
928 dependency.setClassifier( getPropertyString( n, "classifier" ) );
929 dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
930 dependency.setScope( getPropertyString( n, "scope" ) );
931 dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
932 dependency.setType( getPropertyString( n, "type" ) );
933 dependency.setVersion( getPropertyString( n, "version" ) );
934 versionMetadata.addDependency( dependency );
939 for ( Node n : JcrUtils.getChildNodes( node ) )
941 if ( n.isNodeType( FACET_NODE_TYPE ) )
943 String name = n.getName();
944 MetadataFacetFactory factory = metadataFacetFactories.get( name );
945 if ( factory == null )
947 log.error( "Attempted to load unknown project version metadata facet: {}", name );
951 MetadataFacet facet = factory.createMetadataFacet();
952 Map<String, String> map = new HashMap<>();
953 for ( Property property : JcrUtils.getProperties( n ) )
955 String p = property.getName();
956 if ( !p.startsWith( "jcr:" ) )
958 map.put( p, property.getString() );
961 facet.fromProperties( map );
962 versionMetadata.addFacet( facet );
967 catch ( RepositoryException e )
969 throw new MetadataResolutionException( e.getMessage(), e );
972 return versionMetadata;
976 public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
977 String projectVersion )
978 throws MetadataResolutionException
980 Set<String> versions = new LinkedHashSet<String>();
984 Node root = getJcrSession().getRootNode();
986 Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
988 for ( Node n : JcrUtils.getChildNodes( node ) )
990 versions.add( n.getProperty( "version" ).getString() );
993 catch ( PathNotFoundException e )
995 // ignore repo not found for now
997 catch ( RepositoryException e )
999 throw new MetadataResolutionException( e.getMessage(), e );
1006 public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
1007 String projectId, String projectVersion )
1008 throws MetadataResolutionException
1011 List<ProjectVersionReference> references = new ArrayList<>();
1013 // TODO: bind variables instead
1014 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId +
1015 "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
1016 if ( projectVersion != null )
1018 q += " AND [version]='" + projectVersion + "'";
1022 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
1023 QueryResult result = query.execute();
1025 for ( Node n : JcrUtils.getNodes( result ) )
1027 n = n.getParent(); // dependencies grouping element
1029 n = n.getParent(); // project version
1030 String usedByProjectVersion = n.getName();
1032 n = n.getParent(); // project
1033 String usedByProject = n.getName();
1035 n = n.getParent(); // namespace
1036 String usedByNamespace = n.getProperty( "namespace" ).getString();
1038 ProjectVersionReference ref = new ProjectVersionReference();
1039 ref.setNamespace( usedByNamespace );
1040 ref.setProjectId( usedByProject );
1041 ref.setProjectVersion( usedByProjectVersion );
1042 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
1043 references.add( ref );
1046 catch ( RepositoryException e )
1048 throw new MetadataResolutionException( e.getMessage(), e );
1055 public Collection<String> getRootNamespaces( String repositoryId )
1056 throws MetadataResolutionException
1058 return getNamespaces( repositoryId, null );
1062 public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
1063 throws MetadataResolutionException
1065 String path = baseNamespace != null
1066 ? getNamespacePath( repositoryId, baseNamespace )
1067 : getRepositoryContentPath( repositoryId );
1069 return getNodeNames( path, NAMESPACE_NODE_TYPE );
1073 public Collection<String> getProjects( String repositoryId, String namespace )
1074 throws MetadataResolutionException
1076 return getNodeNames( getNamespacePath( repositoryId, namespace ), PROJECT_NODE_TYPE );
1080 public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
1081 throws MetadataResolutionException
1083 return getNodeNames( getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
1087 public void removeArtifact( ArtifactMetadata artifactMetadata, String baseVersion )
1088 throws MetadataRepositoryException
1091 String repositoryId = artifactMetadata.getRepositoryId();
1095 Node root = getJcrSession().getRootNode();
1097 getProjectVersionPath( repositoryId, artifactMetadata.getNamespace(), artifactMetadata.getProject(),
1100 if ( root.hasNode( path ) )
1102 Node node = root.getNode( path );
1104 for ( Node n : JcrUtils.getChildNodes( node ) )
1106 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1108 if ( n.hasProperty( "version" ) )
1110 String version = n.getProperty( "version" ).getString();
1111 if ( StringUtils.equals( version, artifactMetadata.getVersion() ) )
1121 catch ( RepositoryException e )
1123 throw new MetadataRepositoryException( e.getMessage(), e );
1131 public void removeProjectVersion( String repoId, String namespace, String projectId, String projectVersion )
1132 throws MetadataRepositoryException
1137 String path = getProjectPath( repoId, namespace, projectId );
1138 Node root = getJcrSession().getRootNode();
1140 Node nodeAtPath = root.getNode( path );
1142 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1144 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( projectVersion,
1151 catch ( RepositoryException e )
1153 throw new MetadataRepositoryException( e.getMessage(), e );
1158 public void removeArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
1160 throws MetadataRepositoryException
1164 Node root = getJcrSession().getRootNode();
1165 String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
1166 if ( root.hasNode( path ) )
1168 root.getNode( path ).remove();
1173 path = getProjectPath( repositoryId, namespace, projectId );
1175 Node nodeAtPath = root.getNode( path );
1177 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1179 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) //
1180 && StringUtils.equals( node.getName(), projectVersion ) )
1186 catch ( RepositoryException e )
1188 throw new MetadataRepositoryException( e.getMessage(), e );
1193 public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion,
1194 MetadataFacet metadataFacet )
1195 throws MetadataRepositoryException
1199 Node root = getJcrSession().getRootNode();
1200 String path = getProjectVersionPath( repositoryId, namespace, project, projectVersion );
1202 if ( root.hasNode( path ) )
1204 Node node = root.getNode( path );
1206 for ( Node n : JcrUtils.getChildNodes( node ) )
1208 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1210 ArtifactMetadata artifactMetadata = getArtifactFromNode( repositoryId, n );
1211 log.debug( "artifactMetadata: {}", artifactMetadata );
1212 MetadataFacet metadataFacetToRemove = artifactMetadata.getFacet( metadataFacet.getFacetId() );
1213 if ( metadataFacetToRemove != null && metadataFacet.equals( metadataFacetToRemove ) )
1221 catch ( RepositoryException e )
1223 throw new MetadataRepositoryException( e.getMessage(), e );
1228 public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
1229 String projectVersion )
1230 throws MetadataResolutionException
1232 List<ArtifactMetadata> artifacts = new ArrayList<>();
1236 Node root = getJcrSession().getRootNode();
1237 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1239 if ( root.hasNode( path ) )
1241 Node node = root.getNode( path );
1243 for ( Node n : JcrUtils.getChildNodes( node ) )
1245 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1247 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1252 catch ( RepositoryException e )
1254 throw new MetadataResolutionException( e.getMessage(), e );
1265 getJcrSession().save();
1267 catch ( RepositoryException e )
1269 throw new RuntimeException( e.getMessage(), e );
1274 public void revert()
1278 getJcrSession().refresh( false );
1280 catch ( RepositoryException e )
1282 throw new RuntimeException( e.getMessage(), e );
1287 public boolean canObtainAccess( Class<?> aClass )
1289 return aClass == Session.class;
1293 public <T>T obtainAccess( Class<T> aClass )
1294 throws MetadataRepositoryException
1296 if ( aClass == Session.class )
1300 return (T) getJcrSession();
1302 catch ( RepositoryException e )
1304 log.error( e.getMessage(), e );
1305 throw new MetadataRepositoryException( e.getMessage(), e );
1308 throw new IllegalArgumentException(
1309 "Access using " + aClass + " is not supported on the JCR metadata storage" );
1314 throws MetadataRepositoryException
1316 if ( jcrSession != null && jcrSession.isLive() )
1318 jcrSession.logout();
1322 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1323 throws RepositoryException
1325 String id = artifactNode.getName();
1327 ArtifactMetadata artifact = new ArtifactMetadata();
1328 artifact.setId( id );
1329 artifact.setRepositoryId( repositoryId );
1331 Node projectVersionNode = artifactNode.getParent();
1332 Node projectNode = projectVersionNode.getParent();
1333 Node namespaceNode = projectNode.getParent();
1335 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1336 artifact.setProject( projectNode.getName() );
1337 artifact.setProjectVersion( projectVersionNode.getName() );
1338 artifact.setVersion( artifactNode.hasProperty( "version" )
1339 ? artifactNode.getProperty( "version" ).getString()
1340 : projectVersionNode.getName() );
1342 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1344 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1347 if ( artifactNode.hasProperty( "whenGathered" ) )
1349 artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1352 if ( artifactNode.hasProperty( "size" ) )
1354 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1357 if ( artifactNode.hasProperty( "md5" ) )
1359 artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1362 if ( artifactNode.hasProperty( "sha1" ) )
1364 artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1367 for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1369 if ( n.isNodeType( FACET_NODE_TYPE ) )
1371 String name = n.getName();
1372 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1373 if ( factory == null )
1375 log.error( "Attempted to load unknown project version metadata facet: " + name );
1379 MetadataFacet facet = factory.createMetadataFacet();
1380 Map<String, String> map = new HashMap<>();
1381 for ( Property p : JcrUtils.getProperties( n ) )
1383 String property = p.getName();
1384 if ( !property.startsWith( "jcr:" ) )
1386 map.put( property, p.getString() );
1389 facet.fromProperties( map );
1390 artifact.addFacet( facet );
1397 private static String getPropertyString( Node node, String name )
1398 throws RepositoryException
1400 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1403 private Collection<String> getNodeNames( String path, String nodeType )
1404 throws MetadataResolutionException
1406 List<String> names = new ArrayList<>();
1410 Node root = getJcrSession().getRootNode();
1412 Node nodeAtPath = root.getNode( path );
1414 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1416 if ( node.isNodeType( nodeType ) )
1418 names.add( node.getName() );
1422 catch ( PathNotFoundException e )
1424 // ignore repo not found for now
1426 catch ( RepositoryException e )
1428 throw new MetadataResolutionException( e.getMessage(), e );
1434 private static String getRepositoryPath( String repositoryId )
1436 return "repositories/" + repositoryId;
1439 private static String getRepositoryContentPath( String repositoryId )
1441 return getRepositoryPath( repositoryId ) + "/content/";
1444 private static String getFacetPath( String repositoryId, String facetId )
1446 return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1449 private static String getNamespacePath( String repositoryId, String namespace )
1451 return getRepositoryContentPath( repositoryId ) + namespace.replace( '.', '/' );
1454 private static String getProjectPath( String repositoryId, String namespace, String projectId )
1456 return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1459 private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1460 String projectVersion )
1462 return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1465 private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1466 String projectVersion, String id )
1468 return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1471 private Node getOrAddNodeByPath( Node baseNode, String name )
1472 throws RepositoryException
1474 return getOrAddNodeByPath( baseNode, name, null );
1477 private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1478 throws RepositoryException
1480 Node node = baseNode;
1481 for ( String n : name.split( "/" ) )
1483 node = JcrUtils.getOrAddNode( node, n );
1484 if ( nodeType != null )
1486 node.addMixin( nodeType );
1492 private static String getFacetPath( String repositoryId, String facetId, String name )
1494 return getFacetPath( repositoryId, facetId ) + "/" + name;
1497 private Node getOrAddRepositoryNode( String repositoryId )
1498 throws RepositoryException
1500 Node root = getJcrSession().getRootNode();
1501 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1502 node = JcrUtils.getOrAddNode( node, repositoryId );
1506 private Node getOrAddRepositoryContentNode( String repositoryId )
1507 throws RepositoryException
1509 Node node = getOrAddRepositoryNode( repositoryId );
1510 return JcrUtils.getOrAddNode( node, "content" );
1513 private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1514 throws RepositoryException
1516 Node repo = getOrAddRepositoryContentNode( repositoryId );
1517 return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1520 private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1521 throws RepositoryException
1523 Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1524 Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1525 node.addMixin( PROJECT_NODE_TYPE );
1529 private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1530 String projectVersion )
1531 throws RepositoryException
1533 Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1534 Node node = JcrUtils.getOrAddNode( projectNode, projectVersion );
1535 node.addMixin( PROJECT_VERSION_NODE_TYPE );
1539 private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1541 throws RepositoryException
1543 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1544 Node node = JcrUtils.getOrAddNode( versionNode, id );
1545 node.addMixin( ARTIFACT_NODE_TYPE );
1549 private static Calendar createCalendar( Date time )
1551 Calendar cal = Calendar.getInstance();
1552 cal.setTime( time );
1556 private String join( Collection<String> ids )
1558 if ( ids != null && !ids.isEmpty() )
1560 StringBuilder s = new StringBuilder();
1561 for ( String id : ids )
1566 return s.substring( 0, s.length() - 1 );
1571 public Session getJcrSession()
1572 throws RepositoryException
1574 if ( this.jcrSession == null || !this.jcrSession.isLive() )
1576 jcrSession = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
1578 return this.jcrSession;