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.PropertyIterator;
60 import javax.jcr.Repository;
61 import javax.jcr.RepositoryException;
62 import javax.jcr.Session;
63 import javax.jcr.SimpleCredentials;
64 import javax.jcr.ValueFactory;
65 import javax.jcr.Workspace;
66 import javax.jcr.nodetype.NodeTypeManager;
67 import javax.jcr.nodetype.NodeTypeTemplate;
68 import javax.jcr.query.Query;
69 import javax.jcr.query.QueryResult;
72 * @plexus.component role="org.apache.archiva.metadata.repository.MetadataRepository"
73 * @todo path construction should be centralised
74 * @todo review all methods for alternate implementations (use of queries)
75 * @todo below: exception handling
76 * @todo below: revise storage format for project version metadata
78 public class JcrMetadataRepository
79 implements MetadataRepository
82 * @plexus.requirement role="org.apache.archiva.metadata.model.MetadataFacetFactory"
84 private Map<String, MetadataFacetFactory> metadataFacetFactories;
86 private static final Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
88 private static Repository repository;
90 private Session session;
92 public JcrMetadataRepository()
94 // TODO: need to close this at the end - do we need to add it in the API?
98 // TODO: push this in from the test, and make it possible from the webapp
99 if ( repository == null )
101 repository = new TransientRepository( new File( "src/test/repository.xml" ), new File( "target/jcr" ) );
103 // TODO: shouldn't do this in constructor since it's a singleton
104 session = repository.login( new SimpleCredentials( "username", "password".toCharArray() ) );
106 Workspace workspace = session.getWorkspace();
107 workspace.getNamespaceRegistry().registerNamespace( "archiva", "http://archiva.apache.org/jcr" );
109 NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
110 NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
111 nodeType.setMixin( true );
112 nodeType.setName( "archiva:artifact" );
113 nodeTypeManager.registerNodeType( nodeType, false );
115 catch ( LoginException e )
118 throw new RuntimeException( e );
120 catch ( RepositoryException e )
123 throw new RuntimeException( e );
127 public void updateProject( String repositoryId, ProjectMetadata project )
129 updateProject( repositoryId, project.getNamespace(), project.getId() );
132 private void updateProject( String repositoryId, String namespace, String projectId )
134 updateNamespace( repositoryId, namespace );
138 getOrAddProjectNode( repositoryId, namespace, projectId );
140 catch ( RepositoryException e )
143 throw new RuntimeException( e );
147 public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
148 ArtifactMetadata artifactMeta )
150 updateNamespace( repositoryId, namespace );
154 Node node = getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion,
155 artifactMeta.getId() );
157 Calendar cal = Calendar.getInstance();
158 cal.setTime( artifactMeta.getFileLastModified() );
159 node.setProperty( "jcr:lastModified", cal );
161 cal = Calendar.getInstance();
162 cal.setTime( artifactMeta.getWhenGathered() );
163 node.setProperty( "whenGathered", cal );
165 node.setProperty( "size", artifactMeta.getSize() );
166 node.setProperty( "md5", artifactMeta.getMd5() );
167 node.setProperty( "sha1", artifactMeta.getSha1() );
169 node.setProperty( "version", artifactMeta.getVersion() );
171 // TODO: namespaced properties instead?
172 Node facetNode = JcrUtils.getOrAddNode( node, "facets" );
173 for ( MetadataFacet facet : artifactMeta.getFacetList() )
175 // TODO: need to clear it?
176 Node n = JcrUtils.getOrAddNode( facetNode, facet.getFacetId() );
178 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
180 n.setProperty( entry.getKey(), entry.getValue() );
185 catch ( RepositoryException e )
188 throw new RuntimeException( e );
192 public void updateProjectVersion( String repositoryId, String namespace, String projectId,
193 ProjectVersionMetadata versionMetadata )
195 updateProject( repositoryId, namespace, projectId );
199 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId,
200 versionMetadata.getId() );
202 versionNode.setProperty( "name", versionMetadata.getName() );
203 versionNode.setProperty( "description", versionMetadata.getDescription() );
204 versionNode.setProperty( "url", versionMetadata.getUrl() );
205 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
207 // TODO: decide how to treat these in the content repo
208 if ( versionMetadata.getScm() != null )
210 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
211 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
212 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
214 if ( versionMetadata.getCiManagement() != null )
216 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
217 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
219 if ( versionMetadata.getIssueManagement() != null )
221 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
222 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
224 if ( versionMetadata.getOrganization() != null )
226 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
227 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
230 for ( License license : versionMetadata.getLicenses() )
232 versionNode.setProperty( "license." + i + ".name", license.getName() );
233 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
237 for ( MailingList mailingList : versionMetadata.getMailingLists() )
239 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
240 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
241 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
242 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
243 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
244 versionNode.setProperty( "mailingList." + i + ".otherArchives", join(
245 mailingList.getOtherArchives() ) );
249 for ( Dependency dependency : versionMetadata.getDependencies() )
251 versionNode.setProperty( "dependency." + i + ".classifier", dependency.getClassifier() );
252 versionNode.setProperty( "dependency." + i + ".scope", dependency.getScope() );
253 versionNode.setProperty( "dependency." + i + ".systemPath", dependency.getSystemPath() );
254 versionNode.setProperty( "dependency." + i + ".artifactId", dependency.getArtifactId() );
255 versionNode.setProperty( "dependency." + i + ".groupId", dependency.getGroupId() );
256 versionNode.setProperty( "dependency." + i + ".version", dependency.getVersion() );
257 versionNode.setProperty( "dependency." + i + ".type", dependency.getType() );
261 // TODO: namespaced properties instead?
262 Node facetNode = JcrUtils.getOrAddNode( versionNode, "facets" );
263 for ( MetadataFacet facet : versionMetadata.getFacetList() )
265 // TODO: shouldn't need to recreate, just update
266 if ( facetNode.hasNode( facet.getFacetId() ) )
268 facetNode.getNode( facet.getFacetId() ).remove();
270 Node n = facetNode.addNode( facet.getFacetId() );
272 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
274 n.setProperty( entry.getKey(), entry.getValue() );
278 catch ( RepositoryException e )
281 throw new RuntimeException( e );
285 public void updateProjectReference( String repositoryId, String namespace, String projectId, String projectVersion,
286 ProjectVersionReference reference )
288 // TODO: try weak reference?
289 // TODO: is this tree the right way up? It differs from the content model
292 Node node = getOrAddRepositoryContentNode( repositoryId );
293 node = JcrUtils.getOrAddNode( node, namespace );
294 node = JcrUtils.getOrAddNode( node, projectId );
295 node = JcrUtils.getOrAddNode( node, projectVersion );
296 node = JcrUtils.getOrAddNode( node, "references" );
297 node = JcrUtils.getOrAddNode( node, reference.getNamespace() );
298 node = JcrUtils.getOrAddNode( node, reference.getProjectId() );
299 node = JcrUtils.getOrAddNode( node, reference.getProjectVersion() );
300 node.setProperty( "type", reference.getReferenceType().toString() );
302 catch ( RepositoryException e )
305 throw new RuntimeException( e );
309 public void updateNamespace( String repositoryId, String namespace )
313 // TODO: currently flat
314 Node node = getOrAddNamespaceNode( repositoryId, namespace );
315 node.setProperty( "namespace", namespace );
317 catch ( RepositoryException e )
320 throw new RuntimeException( e );
324 public List<String> getMetadataFacets( String repositoryId, String facetId )
326 List<String> facets = new ArrayList<String>();
330 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
331 // are paths themselves
332 Node node = session.getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
334 // TODO: could we simply query all nodes with no children? Or perhaps a specific nodetype?
335 // Might be better to review the purpose of this function - why is the list of paths helpful?
336 recurse( facets, "", node );
338 catch ( PathNotFoundException e )
340 // ignored - the facet doesn't exist, so return the empty list
342 catch ( RepositoryException e )
345 throw new RuntimeException( e );
350 private void recurse( List<String> facets, String prefix, Node node )
351 throws RepositoryException
353 for ( Node n : JcrUtils.getChildNodes( node ) )
355 String name = prefix + "/" + n.getName();
358 recurse( facets, name, n );
362 // strip leading / first
363 facets.add( name.substring( 1 ) );
368 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
370 MetadataFacet metadataFacet = null;
373 Node root = session.getRootNode();
374 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
376 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
377 if ( metadataFacetFactory != null )
379 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
380 Map<String, String> map = new HashMap<String, String>();
381 for ( Property property : JcrUtils.getProperties( node ) )
383 String p = property.getName();
384 if ( !p.startsWith( "jcr:" ) )
386 map.put( p, property.getString() );
389 metadataFacet.fromProperties( map );
392 catch ( PathNotFoundException e )
394 // ignored - the facet doesn't exist, so return null
396 catch ( RepositoryException e )
399 throw new RuntimeException( e );
401 return metadataFacet;
404 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
408 Node repo = getOrAddRepositoryNode( repositoryId );
409 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
411 String id = metadataFacet.getFacetId();
412 Node facetNode = JcrUtils.getOrAddNode( facets, id );
414 Node node = getOrCreatePath( facetNode, metadataFacet.getName() );
416 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
418 node.setProperty( entry.getKey(), entry.getValue() );
421 catch ( RepositoryException e )
424 throw new RuntimeException( e );
428 private Node getOrCreatePath( Node baseNode, String name )
429 throws RepositoryException
431 Node node = baseNode;
432 for ( String n : name.split( "/" ) )
434 node = JcrUtils.getOrAddNode( node, n );
439 public void removeMetadataFacets( String repositoryId, String facetId )
443 Node root = session.getRootNode();
444 String path = getFacetPath( repositoryId, facetId );
445 // TODO: exception if missing?
446 if ( root.hasNode( path ) )
448 root.getNode( path ).remove();
451 catch ( RepositoryException e )
454 throw new RuntimeException( e );
458 public void removeMetadataFacet( String repositoryId, String facetId, String name )
462 Node root = session.getRootNode();
463 String path = getFacetPath( repositoryId, facetId, name );
464 // TODO: exception if missing?
465 if ( root.hasNode( path ) )
467 Node node = root.getNode( path );
470 Node parent = node.getParent();
474 while ( !node.hasNodes() );
477 catch ( RepositoryException e )
480 throw new RuntimeException( e );
484 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
486 List<ArtifactMetadata> artifacts;
488 String q = "SELECT * FROM [archiva:artifact]";
490 String clause = " WHERE";
491 if ( startTime != null )
493 q += clause + " [whenGathered] >= $start";
496 if ( endTime != null )
498 q += clause + " [whenGathered] <= $end";
503 Query query = session.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
504 ValueFactory valueFactory = session.getValueFactory();
505 if ( startTime != null )
507 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
509 if ( endTime != null )
511 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
513 QueryResult result = query.execute();
515 artifacts = new ArrayList<ArtifactMetadata>();
516 for ( Node n : JcrUtils.getNodes( result ) )
518 artifacts.add( getArtifactFromNode( repoId, n ) );
521 catch ( RepositoryException e )
524 throw new RuntimeException( e );
529 public Collection<String> getRepositories()
531 List<String> repositories;
535 Node root = session.getRootNode();
536 if ( root.hasNode( "repositories" ) )
538 Node node = root.getNode( "repositories" );
540 repositories = new ArrayList<String>();
541 NodeIterator i = node.getNodes();
542 while ( i.hasNext() )
544 Node n = i.nextNode();
545 repositories.add( n.getName() );
550 repositories = Collections.emptyList();
553 catch ( RepositoryException e )
556 throw new RuntimeException( e );
561 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
563 // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
564 // of this information (eg. in Lucene, as before)
565 // alternatively, we could build a referential tree in the content repository, however it would need some levels
566 // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
568 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
569 for ( String ns : getRootNamespaces( repositoryId ) )
571 getArtifactsByChecksum( artifacts, repositoryId, ns, checksum );
576 private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns,
579 for ( String namespace : getNamespaces( repositoryId, ns ) )
581 getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum );
584 for ( String project : getProjects( repositoryId, ns ) )
586 for ( String version : getProjectVersions( repositoryId, ns, project ) )
588 for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) )
590 if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) )
592 artifacts.add( artifact );
599 public void deleteArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
604 Node root = session.getRootNode();
606 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion +
608 // TODO: exception if missing?
609 if ( root.hasNode( path ) )
611 root.getNode( path ).remove();
614 catch ( RepositoryException e )
617 throw new RuntimeException( e );
621 public void deleteRepository( String repositoryId )
625 Node root = session.getRootNode();
626 String path = "repositories/" + repositoryId;
627 // TODO: exception if missing?
628 if ( root.hasNode( path ) )
630 root.getNode( path ).remove();
633 catch ( RepositoryException e )
636 throw new RuntimeException( e );
640 public List<ArtifactMetadata> getArtifacts( String repositoryId )
642 // TODO: query faster?
643 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
644 for ( String ns : getRootNamespaces( repositoryId ) )
646 getArtifacts( artifacts, repositoryId, ns );
651 private void getArtifacts( List<ArtifactMetadata> artifacts, String repoId, String ns )
653 for ( String namespace : getNamespaces( repoId, ns ) )
655 getArtifacts( artifacts, repoId, ns + "." + namespace );
658 for ( String project : getProjects( repoId, ns ) )
660 for ( String version : getProjectVersions( repoId, ns, project ) )
662 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
664 artifacts.add( artifact );
670 public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
672 ProjectMetadata metadata = null;
676 Node root = session.getRootNode();
678 // basically just checking it exists
679 String path = "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId;
680 if ( root.hasNode( path ) )
682 metadata = new ProjectMetadata();
683 metadata.setId( projectId );
684 metadata.setNamespace( namespace );
687 catch ( RepositoryException e )
690 throw new RuntimeException( e );
696 public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
697 String projectVersion )
698 throws MetadataResolutionException
700 ProjectVersionMetadata versionMetadata;
704 Node root = session.getRootNode();
707 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion;
708 if ( !root.hasNode( path ) )
713 Node node = root.getNode( path );
715 versionMetadata = new ProjectVersionMetadata();
716 versionMetadata.setId( projectVersion );
717 versionMetadata.setName( getPropertyString( node, "name" ) );
718 versionMetadata.setDescription( getPropertyString( node, "description" ) );
719 versionMetadata.setUrl( getPropertyString( node, "url" ) );
720 versionMetadata.setIncomplete( node.hasProperty( "incomplete" ) && node.getProperty(
721 "incomplete" ).getBoolean() );
723 // TODO: decide how to treat these in the content repo
724 String scmConnection = getPropertyString( node, "scm.connection" );
725 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
726 String scmUrl = getPropertyString( node, "scm.url" );
727 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
730 scm.setConnection( scmConnection );
731 scm.setDeveloperConnection( scmDeveloperConnection );
732 scm.setUrl( scmUrl );
733 versionMetadata.setScm( scm );
736 String ciSystem = getPropertyString( node, "ci.system" );
737 String ciUrl = getPropertyString( node, "ci.url" );
738 if ( ciSystem != null || ciUrl != null )
740 CiManagement ci = new CiManagement();
741 ci.setSystem( ciSystem );
743 versionMetadata.setCiManagement( ci );
746 String issueSystem = getPropertyString( node, "issue.system" );
747 String issueUrl = getPropertyString( node, "issue.url" );
748 if ( issueSystem != null || issueUrl != null )
750 IssueManagement issueManagement = new IssueManagement();
751 issueManagement.setSystem( issueSystem );
752 issueManagement.setUrl( issueUrl );
753 versionMetadata.setIssueManagement( issueManagement );
756 String orgName = getPropertyString( node, "org.name" );
757 String orgUrl = getPropertyString( node, "org.url" );
758 if ( orgName != null || orgUrl != null )
760 Organization org = new Organization();
761 org.setName( orgName );
762 org.setUrl( orgUrl );
763 versionMetadata.setOrganization( org );
766 boolean done = false;
770 String licenseName = getPropertyString( node, "license." + i + ".name" );
771 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
772 if ( licenseName != null || licenseUrl != null )
774 License license = new License();
775 license.setName( licenseName );
776 license.setUrl( licenseUrl );
777 versionMetadata.addLicense( license );
790 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
791 if ( mailingListName != null )
793 MailingList mailingList = new MailingList();
794 mailingList.setName( mailingListName );
795 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
796 String n = "mailingList." + i + ".otherArchives";
797 if ( node.hasProperty( n ) )
799 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
803 mailingList.setOtherArchives( Collections.<String>emptyList() );
805 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
806 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
807 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
808 versionMetadata.addMailingList( mailingList );
821 String dependencyArtifactId = getPropertyString( node, "dependency." + i + ".artifactId" );
822 if ( dependencyArtifactId != null )
824 Dependency dependency = new Dependency();
825 dependency.setArtifactId( dependencyArtifactId );
826 dependency.setGroupId( getPropertyString( node, "dependency." + i + ".groupId" ) );
827 dependency.setClassifier( getPropertyString( node, "dependency." + i + ".classifier" ) );
828 dependency.setOptional( Boolean.valueOf( getPropertyString( node,
829 "dependency." + i + ".optional" ) ) );
830 dependency.setScope( getPropertyString( node, "dependency." + i + ".scope" ) );
831 dependency.setSystemPath( getPropertyString( node, "dependency." + i + ".systemPath" ) );
832 dependency.setType( getPropertyString( node, "dependency." + i + ".type" ) );
833 dependency.setVersion( getPropertyString( node, "dependency." + i + ".version" ) );
834 versionMetadata.addDependency( dependency );
843 if ( node.hasNode( "facets" ) )
845 NodeIterator j = node.getNode( "facets" ).getNodes();
847 while ( j.hasNext() )
849 Node facetNode = j.nextNode();
851 MetadataFacetFactory factory = metadataFacetFactories.get( facetNode.getName() );
852 if ( factory == null )
854 log.error( "Attempted to load unknown project version metadata facet: " + facetNode.getName() );
858 MetadataFacet facet = factory.createMetadataFacet();
859 Map<String, String> map = new HashMap<String, String>();
860 PropertyIterator iterator = facetNode.getProperties();
861 while ( iterator.hasNext() )
863 Property property = iterator.nextProperty();
864 String p = property.getName();
865 if ( !p.startsWith( "jcr:" ) )
867 map.put( p, property.getString() );
870 facet.fromProperties( map );
871 versionMetadata.addFacet( facet );
876 catch ( RepositoryException e )
879 throw new RuntimeException( e );
882 return versionMetadata;
885 private static String getPropertyString( Node node, String name )
886 throws RepositoryException
888 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
891 public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
892 String projectVersion )
894 Set<String> versions = new LinkedHashSet<String>();
898 Node root = session.getRootNode();
900 Node node = root.getNode(
901 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion );
903 NodeIterator iterator = node.getNodes();
904 while ( iterator.hasNext() )
906 Node n = iterator.nextNode();
908 versions.add( n.getProperty( "version" ).getString() );
911 catch ( PathNotFoundException e )
913 // ignore repo not found for now
914 // TODO: throw specific exception if repo doesn't exist
916 catch ( RepositoryException e )
919 throw new RuntimeException( e );
925 public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
926 String projectId, String projectVersion )
928 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
932 Node root = session.getRootNode();
935 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion +
937 if ( root.hasNode( path ) )
939 Node node = root.getNode( path );
941 // TODO: use query by reference type
942 NodeIterator i = node.getNodes();
943 while ( i.hasNext() )
945 Node ns = i.nextNode();
947 NodeIterator j = ns.getNodes();
949 while ( j.hasNext() )
951 Node project = j.nextNode();
953 NodeIterator k = project.getNodes();
955 while ( k.hasNext() )
957 Node version = k.nextNode();
959 ProjectVersionReference ref = new ProjectVersionReference();
960 ref.setNamespace( ns.getName() );
961 ref.setProjectId( project.getName() );
962 ref.setProjectVersion( version.getName() );
963 String type = version.getProperty( "type" ).getString();
964 ref.setReferenceType( ProjectVersionReference.ReferenceType.valueOf( type ) );
965 references.add( ref );
971 catch ( RepositoryException e )
974 throw new RuntimeException( e );
980 public Collection<String> getRootNamespaces( String repositoryId )
982 return getNamespaces( repositoryId, null );
985 private Collection<String> getNodeNames( String path )
987 List<String> names = new ArrayList<String>();
991 Node root = session.getRootNode();
993 Node repository = root.getNode( path );
995 NodeIterator nodes = repository.getNodes();
996 while ( nodes.hasNext() )
998 Node node = nodes.nextNode();
999 names.add( node.getName() );
1002 catch ( PathNotFoundException e )
1004 // ignore repo not found for now
1005 // TODO: throw specific exception if repo doesn't exist
1007 catch ( RepositoryException e )
1010 throw new RuntimeException( e );
1016 public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
1018 // TODO: could be simpler with pathed namespaces, rely on namespace property
1019 Collection<String> allNamespaces = getNodeNames( "repositories/" + repositoryId + "/content" );
1021 Set<String> namespaces = new LinkedHashSet<String>();
1022 int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
1023 for ( String namespace : allNamespaces )
1025 if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) )
1027 int i = namespace.indexOf( '.', fromIndex );
1030 namespaces.add( namespace.substring( fromIndex, i ) );
1034 namespaces.add( namespace.substring( fromIndex ) );
1038 return new ArrayList<String>( namespaces );
1041 public Collection<String> getProjects( String repositoryId, String namespace )
1043 // TODO: could be simpler with pathed namespaces, rely on namespace property
1044 return getNodeNames( "repositories/" + repositoryId + "/content/" + namespace );
1047 public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
1049 // TODO: could be simpler with pathed namespaces, rely on namespace property
1050 return getNodeNames( "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId );
1053 public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
1054 String projectVersion )
1056 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1060 Node root = session.getRootNode();
1062 "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion;
1064 if ( root.hasNode( path ) )
1066 Node node = root.getNode( path );
1068 NodeIterator iterator = node.getNodes();
1069 while ( iterator.hasNext() )
1071 Node artifactNode = iterator.nextNode();
1073 ArtifactMetadata artifact = getArtifactFromNode( repositoryId, artifactNode );
1074 artifacts.add( artifact );
1078 catch ( RepositoryException e )
1081 throw new RuntimeException( e );
1087 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1088 throws RepositoryException
1090 String id = artifactNode.getName();
1092 ArtifactMetadata artifact = new ArtifactMetadata();
1093 artifact.setId( id );
1094 artifact.setRepositoryId( repositoryId );
1096 Node projectVersionNode = artifactNode.getParent();
1097 Node projectNode = projectVersionNode.getParent();
1098 Node namespaceNode = projectNode.getParent();
1100 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1101 artifact.setProject( projectNode.getName() );
1102 artifact.setProjectVersion( projectVersionNode.getName() );
1103 artifact.setVersion( artifactNode.hasProperty( "version" )
1104 ? artifactNode.getProperty( "version" ).getString()
1105 : projectVersionNode.getName() );
1107 if ( artifactNode.hasProperty( "jcr:lastModified" ) )
1109 artifact.setFileLastModified( artifactNode.getProperty( "jcr:lastModified" ).getDate().getTimeInMillis() );
1112 if ( artifactNode.hasProperty( "whenGathered" ) )
1114 artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1117 if ( artifactNode.hasProperty( "size" ) )
1119 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1122 if ( artifactNode.hasProperty( "md5" ) )
1124 artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1127 if ( artifactNode.hasProperty( "sha1" ) )
1129 artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1132 if ( artifactNode.hasNode( "facets" ) )
1134 NodeIterator j = artifactNode.getNode( "facets" ).getNodes();
1136 while ( j.hasNext() )
1138 Node facetNode = j.nextNode();
1140 MetadataFacetFactory factory = metadataFacetFactories.get( facetNode.getName() );
1141 if ( factory == null )
1143 log.error( "Attempted to load unknown project version metadata facet: " + facetNode.getName() );
1147 MetadataFacet facet = factory.createMetadataFacet();
1148 Map<String, String> map = new HashMap<String, String>();
1149 PropertyIterator i = facetNode.getProperties();
1150 while ( i.hasNext() )
1152 Property p = i.nextProperty();
1153 String property = p.getName();
1154 map.put( property, p.getString() );
1156 facet.fromProperties( map );
1157 artifact.addFacet( facet );
1168 // TODO: this shouldn't be here! Repository may need a context
1171 catch ( RepositoryException e )
1174 throw new RuntimeException( e );
1179 public void setMetadataFacetFactories( Map<String, MetadataFacetFactory> metadataFacetFactories )
1181 this.metadataFacetFactories = metadataFacetFactories;
1183 // TODO: check if actually called by normal injection
1185 // for ( String facetId : metadataFacetFactories.keySet() )
1187 // // TODO: second arg should be a better URL for the namespace
1188 // session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
1192 private static String getFacetPath( String repositoryId, String facetId )
1194 return "repositories/" + repositoryId + "/facets/" + facetId;
1197 private static String getFacetPath( String repositoryId, String facetId, String name )
1199 return getFacetPath( repositoryId, facetId ) + "/" + name;
1202 private Node getOrAddRepositoryNode( String repositoryId )
1203 throws RepositoryException
1205 Node root = session.getRootNode();
1206 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1207 node = JcrUtils.getOrAddNode( node, repositoryId );
1211 private Node getOrAddRepositoryContentNode( String repositoryId )
1212 throws RepositoryException
1214 Node node = getOrAddRepositoryNode( repositoryId );
1215 return JcrUtils.getOrAddNode( node, "content" );
1218 private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1219 throws RepositoryException
1221 Node repo = getOrAddRepositoryContentNode( repositoryId );
1222 return JcrUtils.getOrAddNode( repo, namespace );
1225 private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1226 throws RepositoryException
1228 Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1229 return JcrUtils.getOrAddNode( namespaceNode, projectId );
1232 private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1233 String projectVersion )
1234 throws RepositoryException
1236 Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1237 return JcrUtils.getOrAddNode( projectNode, projectVersion );
1240 private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1242 throws RepositoryException
1244 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1245 Node node = JcrUtils.getOrAddNode( versionNode, id );
1246 node.addMixin( "archiva:artifact" );
1250 private static Calendar createCalendar( Date time )
1252 Calendar cal = Calendar.getInstance();
1253 cal.setTime( time );
1257 private String join( Collection<String> ids )
1259 if ( ids != null && !ids.isEmpty() )
1261 StringBuilder s = new StringBuilder();
1262 for ( String id : ids )
1267 return s.substring( 0, s.length() - 1 );
1272 public Session getJcrSession()