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.MetadataResolutionException;
37 import org.apache.jackrabbit.commons.JcrUtils;
38 import org.apache.jackrabbit.core.TransientRepository;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Calendar;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.Date;
49 import java.util.HashMap;
50 import java.util.LinkedHashSet;
51 import java.util.List;
54 import javax.jcr.LoginException;
55 import javax.jcr.Node;
56 import javax.jcr.NodeIterator;
57 import javax.jcr.PathNotFoundException;
58 import javax.jcr.Property;
59 import javax.jcr.Repository;
60 import javax.jcr.RepositoryException;
61 import javax.jcr.Session;
62 import javax.jcr.SimpleCredentials;
63 import javax.jcr.ValueFactory;
64 import javax.jcr.Workspace;
65 import javax.jcr.nodetype.NodeTypeManager;
66 import javax.jcr.nodetype.NodeTypeTemplate;
67 import javax.jcr.query.Query;
68 import javax.jcr.query.QueryResult;
71 * @plexus.component role="org.apache.archiva.metadata.repository.MetadataRepository"
72 * @todo path construction should be centralised
73 * @todo review all methods for alternate implementations (use of queries)
74 * @todo below: exception handling
75 * @todo below: revise storage format for project version metadata
77 public class JcrMetadataRepository
78 implements MetadataRepository
80 private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
82 private static final String ARTIFACT_NODE_TYPE = "archiva:artifact";
84 private static final String FACET_NODE_TYPE = "archiva:facet";
87 * @plexus.requirement role="org.apache.archiva.metadata.model.MetadataFacetFactory"
89 private Map<String, MetadataFacetFactory> metadataFacetFactories;
91 private static final Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
93 private static Repository repository;
95 private Session session;
97 public JcrMetadataRepository()
99 // TODO: need to close this at the end - do we need to add it in the API?
103 // TODO: push this in from the test, and make it possible from the webapp
104 if ( repository == null )
106 repository = new TransientRepository( new File( "src/test/repository.xml" ), new File( "target/jcr" ) );
108 // TODO: shouldn't do this in constructor since it's a singleton
109 session = repository.login( new SimpleCredentials( "username", "password".toCharArray() ) );
111 Workspace workspace = session.getWorkspace();
112 workspace.getNamespaceRegistry().registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
114 NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
115 registerMixinNodeType( nodeTypeManager, ARTIFACT_NODE_TYPE );
116 registerMixinNodeType( nodeTypeManager, FACET_NODE_TYPE );
118 catch ( LoginException e )
121 throw new RuntimeException( e );
123 catch ( RepositoryException e )
126 throw new RuntimeException( e );
130 private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
131 throws RepositoryException
133 NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
134 nodeType.setMixin( true );
135 nodeType.setName( name );
136 nodeTypeManager.registerNodeType( nodeType, false );
139 public void updateProject( String repositoryId, ProjectMetadata project )
141 updateProject( repositoryId, project.getNamespace(), project.getId() );
144 private void updateProject( String repositoryId, String namespace, String projectId )
146 updateNamespace( repositoryId, namespace );
150 getOrAddProjectNode( repositoryId, namespace, projectId );
152 catch ( RepositoryException e )
155 throw new RuntimeException( e );
159 public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
160 ArtifactMetadata artifactMeta )
162 updateNamespace( repositoryId, namespace );
166 Node node = getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion,
167 artifactMeta.getId() );
169 Calendar cal = Calendar.getInstance();
170 cal.setTime( artifactMeta.getFileLastModified() );
171 node.setProperty( JCR_LAST_MODIFIED, cal );
173 cal = Calendar.getInstance();
174 cal.setTime( artifactMeta.getWhenGathered() );
175 node.setProperty( "whenGathered", cal );
177 node.setProperty( "size", artifactMeta.getSize() );
178 node.setProperty( "md5", artifactMeta.getMd5() );
179 node.setProperty( "sha1", artifactMeta.getSha1() );
181 node.setProperty( "version", artifactMeta.getVersion() );
183 for ( MetadataFacet facet : artifactMeta.getFacetList() )
185 if ( node.hasNode( facet.getFacetId() ) )
187 node.getNode( facet.getFacetId() ).remove();
190 // recreate, to ensure properties are removed
191 Node n = node.addNode( facet.getFacetId() );
192 n.addMixin( FACET_NODE_TYPE );
194 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
196 n.setProperty( entry.getKey(), entry.getValue() );
199 // TODO: need some context around this so it can be done only when needed
202 catch ( RepositoryException e )
205 throw new RuntimeException( e );
209 public void updateProjectVersion( String repositoryId, String namespace, String projectId,
210 ProjectVersionMetadata versionMetadata )
212 updateProject( repositoryId, namespace, projectId );
216 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId,
217 versionMetadata.getId() );
219 versionNode.setProperty( "name", versionMetadata.getName() );
220 versionNode.setProperty( "description", versionMetadata.getDescription() );
221 versionNode.setProperty( "url", versionMetadata.getUrl() );
222 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
224 // TODO: decide how to treat these in the content repo
225 if ( versionMetadata.getScm() != null )
227 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
228 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
229 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
231 if ( versionMetadata.getCiManagement() != null )
233 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
234 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
236 if ( versionMetadata.getIssueManagement() != null )
238 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
239 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
241 if ( versionMetadata.getOrganization() != null )
243 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
244 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
247 for ( License license : versionMetadata.getLicenses() )
249 versionNode.setProperty( "license." + i + ".name", license.getName() );
250 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
254 for ( MailingList mailingList : versionMetadata.getMailingLists() )
256 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
257 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
258 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
259 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
260 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
261 versionNode.setProperty( "mailingList." + i + ".otherArchives", join(
262 mailingList.getOtherArchives() ) );
266 for ( Dependency dependency : versionMetadata.getDependencies() )
268 versionNode.setProperty( "dependency." + i + ".classifier", dependency.getClassifier() );
269 versionNode.setProperty( "dependency." + i + ".scope", dependency.getScope() );
270 versionNode.setProperty( "dependency." + i + ".systemPath", dependency.getSystemPath() );
271 versionNode.setProperty( "dependency." + i + ".artifactId", dependency.getArtifactId() );
272 versionNode.setProperty( "dependency." + i + ".groupId", dependency.getGroupId() );
273 versionNode.setProperty( "dependency." + i + ".version", dependency.getVersion() );
274 versionNode.setProperty( "dependency." + i + ".type", dependency.getType() );
278 for ( MetadataFacet facet : versionMetadata.getFacetList() )
280 // recreate, to ensure properties are removed
281 if ( versionNode.hasNode( facet.getFacetId() ) )
283 versionNode.getNode( facet.getFacetId() ).remove();
285 Node n = versionNode.addNode( facet.getFacetId() );
286 n.addMixin( FACET_NODE_TYPE );
288 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
290 n.setProperty( entry.getKey(), entry.getValue() );
294 catch ( RepositoryException e )
297 throw new RuntimeException( e );
301 public void updateProjectReference( String repositoryId, String namespace, String projectId, String projectVersion,
302 ProjectVersionReference reference )
304 // TODO: try weak reference?
305 // TODO: is this tree the right way up? It differs from the content model
308 Node node = getOrAddRepositoryContentNode( repositoryId );
309 node = JcrUtils.getOrAddNode( node, namespace );
310 node = JcrUtils.getOrAddNode( node, projectId );
311 node = JcrUtils.getOrAddNode( node, projectVersion );
312 node = JcrUtils.getOrAddNode( node, "references" );
313 node = JcrUtils.getOrAddNode( node, reference.getNamespace() );
314 node = JcrUtils.getOrAddNode( node, reference.getProjectId() );
315 node = JcrUtils.getOrAddNode( node, reference.getProjectVersion() );
316 node.setProperty( "type", reference.getReferenceType().toString() );
318 catch ( RepositoryException e )
321 throw new RuntimeException( e );
325 public void updateNamespace( String repositoryId, String namespace )
329 // TODO: currently flat
330 Node node = getOrAddNamespaceNode( repositoryId, namespace );
331 node.setProperty( "namespace", namespace );
333 catch ( RepositoryException e )
336 throw new RuntimeException( e );
340 public List<String> getMetadataFacets( String repositoryId, String facetId )
342 List<String> facets = new ArrayList<String>();
346 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
347 // are paths themselves
348 Node node = session.getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
350 // TODO: could we simply query all nodes with no children? Or perhaps a specific nodetype?
351 // Might be better to review the purpose of this function - why is the list of paths helpful?
352 recurse( facets, "", node );
354 catch ( PathNotFoundException e )
356 // ignored - the facet doesn't exist, so return the empty list
358 catch ( RepositoryException e )
361 throw new RuntimeException( e );
366 private void recurse( List<String> facets, String prefix, Node node )
367 throws RepositoryException
369 for ( Node n : JcrUtils.getChildNodes( node ) )
371 String name = prefix + "/" + n.getName();
374 recurse( facets, name, n );
378 // strip leading / first
379 facets.add( name.substring( 1 ) );
384 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
386 MetadataFacet metadataFacet = null;
389 Node root = session.getRootNode();
390 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
392 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
393 if ( metadataFacetFactory != null )
395 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
396 Map<String, String> map = new HashMap<String, String>();
397 for ( Property property : JcrUtils.getProperties( node ) )
399 String p = property.getName();
400 if ( !p.startsWith( "jcr:" ) )
402 map.put( p, property.getString() );
405 metadataFacet.fromProperties( map );
408 catch ( PathNotFoundException e )
410 // ignored - the facet doesn't exist, so return null
412 catch ( RepositoryException e )
415 throw new RuntimeException( e );
417 return metadataFacet;
420 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
424 Node repo = getOrAddRepositoryNode( repositoryId );
425 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
427 String id = metadataFacet.getFacetId();
428 Node facetNode = JcrUtils.getOrAddNode( facets, id );
430 Node node = getOrCreatePath( facetNode, metadataFacet.getName() );
432 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
434 node.setProperty( entry.getKey(), entry.getValue() );
437 catch ( RepositoryException e )
440 throw new RuntimeException( e );
444 private Node getOrCreatePath( Node baseNode, String name )
445 throws RepositoryException
447 Node node = baseNode;
448 for ( String n : name.split( "/" ) )
450 node = JcrUtils.getOrAddNode( node, n );
455 public void removeMetadataFacets( String repositoryId, String facetId )
459 Node root = session.getRootNode();
460 String path = getFacetPath( repositoryId, facetId );
461 // TODO: exception if missing?
462 if ( root.hasNode( path ) )
464 root.getNode( path ).remove();
467 catch ( RepositoryException e )
470 throw new RuntimeException( e );
474 public void removeMetadataFacet( String repositoryId, String facetId, String name )
478 Node root = session.getRootNode();
479 String path = getFacetPath( repositoryId, facetId, name );
480 // TODO: exception if missing?
481 if ( root.hasNode( path ) )
483 Node node = root.getNode( path );
486 Node parent = node.getParent();
490 while ( !node.hasNodes() );
493 catch ( RepositoryException e )
496 throw new RuntimeException( e );
500 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
502 List<ArtifactMetadata> artifacts;
504 String q = "SELECT * FROM [archiva:artifact]";
506 String clause = " WHERE";
507 if ( startTime != null )
509 q += clause + " [whenGathered] >= $start";
512 if ( endTime != null )
514 q += clause + " [whenGathered] <= $end";
519 Query query = session.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
520 ValueFactory valueFactory = session.getValueFactory();
521 if ( startTime != null )
523 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
525 if ( endTime != null )
527 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
529 QueryResult result = query.execute();
531 artifacts = new ArrayList<ArtifactMetadata>();
532 for ( Node n : JcrUtils.getNodes( result ) )
534 artifacts.add( getArtifactFromNode( repoId, n ) );
537 catch ( RepositoryException e )
540 throw new RuntimeException( e );
545 public Collection<String> getRepositories()
547 List<String> repositories;
551 Node root = session.getRootNode();
552 if ( root.hasNode( "repositories" ) )
554 Node node = root.getNode( "repositories" );
556 repositories = new ArrayList<String>();
557 NodeIterator i = node.getNodes();
558 while ( i.hasNext() )
560 Node n = i.nextNode();
561 repositories.add( n.getName() );
566 repositories = Collections.emptyList();
569 catch ( RepositoryException e )
572 throw new RuntimeException( e );
577 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
579 // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
580 // of this information (eg. in Lucene, as before)
581 // alternatively, we could build a referential tree in the content repository, however it would need some levels
582 // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
584 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
585 for ( String ns : getRootNamespaces( repositoryId ) )
587 getArtifactsByChecksum( artifacts, repositoryId, ns, checksum );
592 private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns,
595 for ( String namespace : getNamespaces( repositoryId, ns ) )
597 getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum );
600 for ( String project : getProjects( repositoryId, ns ) )
602 for ( String version : getProjectVersions( repositoryId, ns, project ) )
604 for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) )
606 if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) )
608 artifacts.add( artifact );
615 public void deleteArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
620 Node root = session.getRootNode();
622 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion +
624 // TODO: exception if missing?
625 if ( root.hasNode( path ) )
627 root.getNode( path ).remove();
630 catch ( RepositoryException e )
633 throw new RuntimeException( e );
637 public void deleteRepository( String repositoryId )
641 Node root = session.getRootNode();
642 String path = "repositories/" + repositoryId;
643 // TODO: exception if missing?
644 if ( root.hasNode( path ) )
646 root.getNode( path ).remove();
649 catch ( RepositoryException e )
652 throw new RuntimeException( e );
656 public List<ArtifactMetadata> getArtifacts( String repositoryId )
658 // TODO: query faster?
659 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
660 for ( String ns : getRootNamespaces( repositoryId ) )
662 getArtifacts( artifacts, repositoryId, ns );
667 private void getArtifacts( List<ArtifactMetadata> artifacts, String repoId, String ns )
669 for ( String namespace : getNamespaces( repoId, ns ) )
671 getArtifacts( artifacts, repoId, ns + "." + namespace );
674 for ( String project : getProjects( repoId, ns ) )
676 for ( String version : getProjectVersions( repoId, ns, project ) )
678 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
680 artifacts.add( artifact );
686 public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
688 ProjectMetadata metadata = null;
692 Node root = session.getRootNode();
694 // basically just checking it exists
695 String path = "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId;
696 if ( root.hasNode( path ) )
698 metadata = new ProjectMetadata();
699 metadata.setId( projectId );
700 metadata.setNamespace( namespace );
703 catch ( RepositoryException e )
706 throw new RuntimeException( e );
712 public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
713 String projectVersion )
714 throws MetadataResolutionException
716 ProjectVersionMetadata versionMetadata;
720 Node root = session.getRootNode();
723 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion;
724 if ( !root.hasNode( path ) )
729 Node node = root.getNode( path );
731 versionMetadata = new ProjectVersionMetadata();
732 versionMetadata.setId( projectVersion );
733 versionMetadata.setName( getPropertyString( node, "name" ) );
734 versionMetadata.setDescription( getPropertyString( node, "description" ) );
735 versionMetadata.setUrl( getPropertyString( node, "url" ) );
736 versionMetadata.setIncomplete( node.hasProperty( "incomplete" ) && node.getProperty(
737 "incomplete" ).getBoolean() );
739 // TODO: decide how to treat these in the content repo
740 String scmConnection = getPropertyString( node, "scm.connection" );
741 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
742 String scmUrl = getPropertyString( node, "scm.url" );
743 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
746 scm.setConnection( scmConnection );
747 scm.setDeveloperConnection( scmDeveloperConnection );
748 scm.setUrl( scmUrl );
749 versionMetadata.setScm( scm );
752 String ciSystem = getPropertyString( node, "ci.system" );
753 String ciUrl = getPropertyString( node, "ci.url" );
754 if ( ciSystem != null || ciUrl != null )
756 CiManagement ci = new CiManagement();
757 ci.setSystem( ciSystem );
759 versionMetadata.setCiManagement( ci );
762 String issueSystem = getPropertyString( node, "issue.system" );
763 String issueUrl = getPropertyString( node, "issue.url" );
764 if ( issueSystem != null || issueUrl != null )
766 IssueManagement issueManagement = new IssueManagement();
767 issueManagement.setSystem( issueSystem );
768 issueManagement.setUrl( issueUrl );
769 versionMetadata.setIssueManagement( issueManagement );
772 String orgName = getPropertyString( node, "org.name" );
773 String orgUrl = getPropertyString( node, "org.url" );
774 if ( orgName != null || orgUrl != null )
776 Organization org = new Organization();
777 org.setName( orgName );
778 org.setUrl( orgUrl );
779 versionMetadata.setOrganization( org );
782 boolean done = false;
786 String licenseName = getPropertyString( node, "license." + i + ".name" );
787 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
788 if ( licenseName != null || licenseUrl != null )
790 License license = new License();
791 license.setName( licenseName );
792 license.setUrl( licenseUrl );
793 versionMetadata.addLicense( license );
806 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
807 if ( mailingListName != null )
809 MailingList mailingList = new MailingList();
810 mailingList.setName( mailingListName );
811 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
812 String n = "mailingList." + i + ".otherArchives";
813 if ( node.hasProperty( n ) )
815 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
819 mailingList.setOtherArchives( Collections.<String>emptyList() );
821 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
822 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
823 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
824 versionMetadata.addMailingList( mailingList );
837 String dependencyArtifactId = getPropertyString( node, "dependency." + i + ".artifactId" );
838 if ( dependencyArtifactId != null )
840 Dependency dependency = new Dependency();
841 dependency.setArtifactId( dependencyArtifactId );
842 dependency.setGroupId( getPropertyString( node, "dependency." + i + ".groupId" ) );
843 dependency.setClassifier( getPropertyString( node, "dependency." + i + ".classifier" ) );
844 dependency.setOptional( Boolean.valueOf( getPropertyString( node,
845 "dependency." + i + ".optional" ) ) );
846 dependency.setScope( getPropertyString( node, "dependency." + i + ".scope" ) );
847 dependency.setSystemPath( getPropertyString( node, "dependency." + i + ".systemPath" ) );
848 dependency.setType( getPropertyString( node, "dependency." + i + ".type" ) );
849 dependency.setVersion( getPropertyString( node, "dependency." + i + ".version" ) );
850 versionMetadata.addDependency( dependency );
859 for ( Node n : JcrUtils.getChildNodes( node ) )
861 if ( n.isNodeType( FACET_NODE_TYPE ) )
863 String name = n.getName();
864 MetadataFacetFactory factory = metadataFacetFactories.get( name );
865 if ( factory == null )
867 log.error( "Attempted to load unknown project version metadata facet: " + name );
871 MetadataFacet facet = factory.createMetadataFacet();
872 Map<String, String> map = new HashMap<String, String>();
873 for ( Property property : JcrUtils.getProperties( n ) )
875 String p = property.getName();
876 if ( !p.startsWith( "jcr:" ) )
878 map.put( p, property.getString() );
881 facet.fromProperties( map );
882 versionMetadata.addFacet( facet );
887 catch ( RepositoryException e )
890 throw new RuntimeException( e );
893 return versionMetadata;
896 private static String getPropertyString( Node node, String name )
897 throws RepositoryException
899 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
902 public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
903 String projectVersion )
905 Set<String> versions = new LinkedHashSet<String>();
909 Node root = session.getRootNode();
911 Node node = root.getNode(
912 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion );
914 NodeIterator iterator = node.getNodes();
915 while ( iterator.hasNext() )
917 Node n = iterator.nextNode();
919 versions.add( n.getProperty( "version" ).getString() );
922 catch ( PathNotFoundException e )
924 // ignore repo not found for now
925 // TODO: throw specific exception if repo doesn't exist
927 catch ( RepositoryException e )
930 throw new RuntimeException( e );
936 public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
937 String projectId, String projectVersion )
939 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
943 Node root = session.getRootNode();
946 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion +
948 if ( root.hasNode( path ) )
950 Node node = root.getNode( path );
952 // TODO: use query by reference type
953 NodeIterator i = node.getNodes();
954 while ( i.hasNext() )
956 Node ns = i.nextNode();
958 NodeIterator j = ns.getNodes();
960 while ( j.hasNext() )
962 Node project = j.nextNode();
964 NodeIterator k = project.getNodes();
966 while ( k.hasNext() )
968 Node version = k.nextNode();
970 ProjectVersionReference ref = new ProjectVersionReference();
971 ref.setNamespace( ns.getName() );
972 ref.setProjectId( project.getName() );
973 ref.setProjectVersion( version.getName() );
974 String type = version.getProperty( "type" ).getString();
975 ref.setReferenceType( ProjectVersionReference.ReferenceType.valueOf( type ) );
976 references.add( ref );
982 catch ( RepositoryException e )
985 throw new RuntimeException( e );
991 public Collection<String> getRootNamespaces( String repositoryId )
993 return getNamespaces( repositoryId, null );
996 private Collection<String> getNodeNames( String path )
998 List<String> names = new ArrayList<String>();
1002 Node root = session.getRootNode();
1004 Node repository = root.getNode( path );
1006 NodeIterator nodes = repository.getNodes();
1007 while ( nodes.hasNext() )
1009 Node node = nodes.nextNode();
1010 names.add( node.getName() );
1013 catch ( PathNotFoundException e )
1015 // ignore repo not found for now
1016 // TODO: throw specific exception if repo doesn't exist
1018 catch ( RepositoryException e )
1021 throw new RuntimeException( e );
1027 public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
1029 // TODO: could be simpler with pathed namespaces, rely on namespace property
1030 Collection<String> allNamespaces = getNodeNames( "repositories/" + repositoryId + "/content" );
1032 Set<String> namespaces = new LinkedHashSet<String>();
1033 int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
1034 for ( String namespace : allNamespaces )
1036 if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) )
1038 int i = namespace.indexOf( '.', fromIndex );
1041 namespaces.add( namespace.substring( fromIndex, i ) );
1045 namespaces.add( namespace.substring( fromIndex ) );
1049 return new ArrayList<String>( namespaces );
1052 public Collection<String> getProjects( String repositoryId, String namespace )
1054 // TODO: could be simpler with pathed namespaces, rely on namespace property
1055 return getNodeNames( "repositories/" + repositoryId + "/content/" + namespace );
1058 public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
1060 // TODO: could be simpler with pathed namespaces, rely on namespace property
1061 return getNodeNames( "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId );
1064 public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
1065 String projectVersion )
1067 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1071 Node root = session.getRootNode();
1073 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion;
1075 if ( root.hasNode( path ) )
1077 Node node = root.getNode( path );
1079 NodeIterator iterator = node.getNodes();
1080 while ( iterator.hasNext() )
1082 Node artifactNode = iterator.nextNode();
1084 ArtifactMetadata artifact = getArtifactFromNode( repositoryId, artifactNode );
1085 artifacts.add( artifact );
1089 catch ( RepositoryException e )
1092 throw new RuntimeException( e );
1098 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1099 throws RepositoryException
1101 String id = artifactNode.getName();
1103 ArtifactMetadata artifact = new ArtifactMetadata();
1104 artifact.setId( id );
1105 artifact.setRepositoryId( repositoryId );
1107 Node projectVersionNode = artifactNode.getParent();
1108 Node projectNode = projectVersionNode.getParent();
1109 Node namespaceNode = projectNode.getParent();
1111 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1112 artifact.setProject( projectNode.getName() );
1113 artifact.setProjectVersion( projectVersionNode.getName() );
1114 artifact.setVersion( artifactNode.hasProperty( "version" )
1115 ? artifactNode.getProperty( "version" ).getString()
1116 : projectVersionNode.getName() );
1118 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1120 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1123 if ( artifactNode.hasProperty( "whenGathered" ) )
1125 artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1128 if ( artifactNode.hasProperty( "size" ) )
1130 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1133 if ( artifactNode.hasProperty( "md5" ) )
1135 artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1138 if ( artifactNode.hasProperty( "sha1" ) )
1140 artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1143 for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1145 if ( n.isNodeType( FACET_NODE_TYPE ) )
1147 String name = n.getName();
1148 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1149 if ( factory == null )
1151 log.error( "Attempted to load unknown project version metadata facet: " + name );
1155 MetadataFacet facet = factory.createMetadataFacet();
1156 Map<String, String> map = new HashMap<String, String>();
1157 for ( Property p : JcrUtils.getProperties( n ) )
1159 String property = p.getName();
1160 if ( !property.startsWith( "jcr:" ) )
1162 map.put( property, p.getString() );
1165 facet.fromProperties( map );
1166 artifact.addFacet( facet );
1177 // TODO: this shouldn't be here! Repository may need a context
1180 catch ( RepositoryException e )
1183 throw new RuntimeException( e );
1188 public void setMetadataFacetFactories( Map<String, MetadataFacetFactory> metadataFacetFactories )
1190 this.metadataFacetFactories = metadataFacetFactories;
1192 // TODO: check if actually called by normal injection
1194 // for ( String facetId : metadataFacetFactories.keySet() )
1196 // // TODO: second arg should be a better URL for the namespace
1197 // session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
1201 private static String getFacetPath( String repositoryId, String facetId )
1203 return "repositories/" + repositoryId + "/facets/" + facetId;
1206 private static String getFacetPath( String repositoryId, String facetId, String name )
1208 return getFacetPath( repositoryId, facetId ) + "/" + name;
1211 private Node getOrAddRepositoryNode( String repositoryId )
1212 throws RepositoryException
1214 Node root = session.getRootNode();
1215 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1216 node = JcrUtils.getOrAddNode( node, repositoryId );
1220 private Node getOrAddRepositoryContentNode( String repositoryId )
1221 throws RepositoryException
1223 Node node = getOrAddRepositoryNode( repositoryId );
1224 return JcrUtils.getOrAddNode( node, "content" );
1227 private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1228 throws RepositoryException
1230 Node repo = getOrAddRepositoryContentNode( repositoryId );
1231 return JcrUtils.getOrAddNode( repo, namespace );
1234 private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1235 throws RepositoryException
1237 Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1238 return JcrUtils.getOrAddNode( namespaceNode, projectId );
1241 private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1242 String projectVersion )
1243 throws RepositoryException
1245 Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1246 return JcrUtils.getOrAddNode( projectNode, projectVersion );
1249 private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1251 throws RepositoryException
1253 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1254 Node node = JcrUtils.getOrAddNode( versionNode, id );
1255 node.addMixin( ARTIFACT_NODE_TYPE );
1259 private static Calendar createCalendar( Date time )
1261 Calendar cal = Calendar.getInstance();
1262 cal.setTime( time );
1266 private String join( Collection<String> ids )
1268 if ( ids != null && !ids.isEmpty() )
1270 StringBuilder s = new StringBuilder();
1271 for ( String id : ids )
1276 return s.substring( 0, s.length() - 1 );
1281 public Session getJcrSession()