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.jackrabbit.commons.JcrUtils;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
42 import javax.jcr.NamespaceRegistry;
43 import javax.jcr.Node;
44 import javax.jcr.NodeIterator;
45 import javax.jcr.PathNotFoundException;
46 import javax.jcr.Property;
47 import javax.jcr.Repository;
48 import javax.jcr.RepositoryException;
49 import javax.jcr.Session;
50 import javax.jcr.SimpleCredentials;
51 import javax.jcr.ValueFactory;
52 import javax.jcr.Workspace;
53 import javax.jcr.nodetype.NodeTypeManager;
54 import javax.jcr.nodetype.NodeTypeTemplate;
55 import javax.jcr.query.Query;
56 import javax.jcr.query.QueryResult;
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.Calendar;
60 import java.util.Collection;
61 import java.util.Collections;
62 import java.util.Date;
63 import java.util.HashMap;
64 import java.util.LinkedHashSet;
65 import java.util.List;
70 * @todo below: revise storage format for project version metadata
71 * @todo revise reference storage
73 public class JcrMetadataRepository
74 implements MetadataRepository
76 private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
78 static final String NAMESPACE_NODE_TYPE = "archiva:namespace";
80 static final String PROJECT_NODE_TYPE = "archiva:project";
82 static final String PROJECT_VERSION_NODE_TYPE = "archiva:projectVersion";
84 static final String ARTIFACT_NODE_TYPE = "archiva:artifact";
86 static final String FACET_NODE_TYPE = "archiva:facet";
88 private static final String DEPENDENCY_NODE_TYPE = "archiva:dependency";
90 private final Map<String, MetadataFacetFactory> metadataFacetFactories;
92 private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
94 private Repository repository;
96 private Session jcrSession;
98 public JcrMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories, Repository repository )
99 throws RepositoryException
101 this.metadataFacetFactories = metadataFacetFactories;
102 this.repository = repository;
104 //session = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
108 static void initialize( Session session )
109 throws RepositoryException
111 // TODO: consider using namespaces for facets instead of the current approach:
112 // (if used, check if actually called by normal injection)
113 // for ( String facetId : metadataFacetFactories.keySet() )
115 // session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
118 Workspace workspace = session.getWorkspace();
119 NamespaceRegistry registry = workspace.getNamespaceRegistry();
121 if ( !Arrays.asList( registry.getPrefixes() ).contains( "archiva" ) )
123 registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
126 NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
127 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.NAMESPACE_NODE_TYPE );
128 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_NODE_TYPE );
129 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_VERSION_NODE_TYPE );
130 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.ARTIFACT_NODE_TYPE );
131 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.FACET_NODE_TYPE );
132 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.DEPENDENCY_NODE_TYPE );
135 private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
136 throws RepositoryException
138 NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
139 nodeType.setMixin( true );
140 nodeType.setName( name );
142 // for now just don't re-create - but in future if we change the definition, make sure to remove first as an
144 if ( !nodeTypeManager.hasNodeType( name ) )
146 nodeTypeManager.registerNodeType( nodeType, false );
150 public void updateProject( String repositoryId, ProjectMetadata project )
151 throws MetadataRepositoryException
153 updateProject( repositoryId, project.getNamespace(), project.getId() );
156 private void updateProject( String repositoryId, String namespace, String projectId )
157 throws MetadataRepositoryException
159 updateNamespace( repositoryId, namespace );
163 getOrAddProjectNode( repositoryId, namespace, projectId );
165 catch ( RepositoryException e )
167 throw new MetadataRepositoryException( e.getMessage(), e );
171 public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
172 ArtifactMetadata artifactMeta )
173 throws MetadataRepositoryException
175 updateNamespace( repositoryId, namespace );
180 getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion, artifactMeta.getId() );
182 Calendar cal = Calendar.getInstance();
183 cal.setTime( artifactMeta.getFileLastModified() );
184 node.setProperty( JCR_LAST_MODIFIED, cal );
186 cal = Calendar.getInstance();
187 cal.setTime( artifactMeta.getWhenGathered() );
188 node.setProperty( "whenGathered", cal );
190 node.setProperty( "size", artifactMeta.getSize() );
191 node.setProperty( "md5", artifactMeta.getMd5() );
192 node.setProperty( "sha1", artifactMeta.getSha1() );
194 node.setProperty( "version", artifactMeta.getVersion() );
196 for ( MetadataFacet facet : artifactMeta.getFacetList() )
198 if ( node.hasNode( facet.getFacetId() ) )
200 node.getNode( facet.getFacetId() ).remove();
203 // recreate, to ensure properties are removed
204 Node n = node.addNode( facet.getFacetId() );
205 n.addMixin( FACET_NODE_TYPE );
207 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
209 n.setProperty( entry.getKey(), entry.getValue() );
213 catch ( RepositoryException e )
215 throw new MetadataRepositoryException( e.getMessage(), e );
219 public void updateProjectVersion( String repositoryId, String namespace, String projectId,
220 ProjectVersionMetadata versionMetadata )
221 throws MetadataRepositoryException
223 updateProject( repositoryId, namespace, projectId );
228 getOrAddProjectVersionNode( repositoryId, namespace, projectId, versionMetadata.getId() );
230 versionNode.setProperty( "name", versionMetadata.getName() );
231 versionNode.setProperty( "description", versionMetadata.getDescription() );
232 versionNode.setProperty( "url", versionMetadata.getUrl() );
233 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
235 // FIXME: decide how to treat these in the content repo
236 if ( versionMetadata.getScm() != null )
238 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
239 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
240 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
242 if ( versionMetadata.getCiManagement() != null )
244 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
245 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
247 if ( versionMetadata.getIssueManagement() != null )
249 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
250 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
252 if ( versionMetadata.getOrganization() != null )
254 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
255 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
258 for ( License license : versionMetadata.getLicenses() )
260 versionNode.setProperty( "license." + i + ".name", license.getName() );
261 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
265 for ( MailingList mailingList : versionMetadata.getMailingLists() )
267 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
268 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
269 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
270 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
271 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
272 versionNode.setProperty( "mailingList." + i + ".otherArchives",
273 join( mailingList.getOtherArchives() ) );
277 if ( !versionMetadata.getDependencies().isEmpty() )
279 Node dependenciesNode = JcrUtils.getOrAddNode( versionNode, "dependencies" );
281 for ( Dependency dependency : versionMetadata.getDependencies() )
283 // Note that we deliberately don't alter the namespace path - not enough dependencies for
284 // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each
285 // component of the ID as that creates extra depth and causes a great cost in space and memory
287 // FIXME: change group ID to namespace
288 // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now.
289 // This won't support types where the extension doesn't match the type.
290 // (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM)
292 dependency.getGroupId() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion();
293 if ( dependency.getClassifier() != null )
295 id += "-" + dependency.getClassifier();
297 id += "." + dependency.getType();
299 Node n = JcrUtils.getOrAddNode( dependenciesNode, id );
300 n.addMixin( DEPENDENCY_NODE_TYPE );
302 // FIXME: remove temp code just to make it keep working
303 n.setProperty( "groupId", dependency.getGroupId() );
304 n.setProperty( "artifactId", dependency.getArtifactId() );
305 n.setProperty( "version", dependency.getVersion() );
306 n.setProperty( "type", dependency.getType() );
307 n.setProperty( "classifier", dependency.getClassifier() );
308 n.setProperty( "scope", dependency.getScope() );
309 n.setProperty( "systemPath", dependency.getSystemPath() );
310 n.setProperty( "optional", dependency.isOptional() );
312 // node has no native content at this time, just facets
313 // no need to list a type as it's implied by the path. Parents are Maven specific.
315 // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet
316 // (should also have been added to the Dependency)
318 // TODO: add a property that is a weak reference to the originating artifact, creating it if
319 // necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an
320 // artifact, which gives a different meaning to "incomplete" which is a known local project
321 // that doesn't have metadata yet but has artifacts). (Though we may want to give it the
322 // artifact mixin and another property to identify all non-local artifacts for the closure
327 for ( MetadataFacet facet : versionMetadata.getFacetList() )
329 // recreate, to ensure properties are removed
330 if ( versionNode.hasNode( facet.getFacetId() ) )
332 versionNode.getNode( facet.getFacetId() ).remove();
334 Node n = versionNode.addNode( facet.getFacetId() );
335 n.addMixin( FACET_NODE_TYPE );
337 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
339 n.setProperty( entry.getKey(), entry.getValue() );
343 catch ( RepositoryException e )
345 throw new MetadataRepositoryException( e.getMessage(), e );
349 public void updateNamespace( String repositoryId, String namespace )
350 throws MetadataRepositoryException
354 Node node = getOrAddNamespaceNode( repositoryId, namespace );
355 node.setProperty( "namespace", namespace );
357 catch ( RepositoryException e )
359 throw new MetadataRepositoryException( e.getMessage(), e );
363 public List<String> getMetadataFacets( String repositoryId, String facetId )
364 throws MetadataRepositoryException
366 List<String> facets = new ArrayList<String>();
370 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
371 // are paths themselves
372 Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
374 // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
376 recurse( facets, "", node );
378 catch ( PathNotFoundException e )
380 // ignored - the facet doesn't exist, so return the empty list
382 catch ( RepositoryException e )
384 throw new MetadataRepositoryException( e.getMessage(), e );
389 private void recurse( List<String> facets, String prefix, Node node )
390 throws RepositoryException
392 for ( Node n : JcrUtils.getChildNodes( node ) )
394 String name = prefix + "/" + n.getName();
397 recurse( facets, name, n );
401 // strip leading / first
402 facets.add( name.substring( 1 ) );
407 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
408 throws MetadataRepositoryException
410 MetadataFacet metadataFacet = null;
413 Node root = getJcrSession().getRootNode();
414 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
416 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
417 if ( metadataFacetFactory != null )
419 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
420 Map<String, String> map = new HashMap<String, String>();
421 for ( Property property : JcrUtils.getProperties( node ) )
423 String p = property.getName();
424 if ( !p.startsWith( "jcr:" ) )
426 map.put( p, property.getString() );
429 metadataFacet.fromProperties( map );
432 catch ( PathNotFoundException e )
434 // ignored - the facet doesn't exist, so return null
436 catch ( RepositoryException e )
438 throw new MetadataRepositoryException( e.getMessage(), e );
440 return metadataFacet;
443 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
444 throws MetadataRepositoryException
448 Node repo = getOrAddRepositoryNode( repositoryId );
449 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
451 String id = metadataFacet.getFacetId();
452 Node facetNode = JcrUtils.getOrAddNode( facets, id );
454 Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
456 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
458 node.setProperty( entry.getKey(), entry.getValue() );
461 catch ( RepositoryException e )
463 throw new MetadataRepositoryException( e.getMessage(), e );
467 public void removeMetadataFacets( String repositoryId, String facetId )
468 throws MetadataRepositoryException
472 Node root = getJcrSession().getRootNode();
473 String path = getFacetPath( repositoryId, facetId );
474 if ( root.hasNode( path ) )
476 root.getNode( path ).remove();
479 catch ( RepositoryException e )
481 throw new MetadataRepositoryException( e.getMessage(), e );
485 public void removeMetadataFacet( String repositoryId, String facetId, String name )
486 throws MetadataRepositoryException
490 Node root = getJcrSession().getRootNode();
491 String path = getFacetPath( repositoryId, facetId, name );
492 if ( root.hasNode( path ) )
494 Node node = root.getNode( path );
497 // also remove empty container nodes
498 Node parent = node.getParent();
502 while ( !node.hasNodes() );
505 catch ( RepositoryException e )
507 throw new MetadataRepositoryException( e.getMessage(), e );
511 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
512 throws MetadataRepositoryException
514 List<ArtifactMetadata> artifacts;
516 String q = getArtifactQuery( repoId );
518 if ( startTime != null )
520 q += " AND [whenGathered] >= $start";
522 if ( endTime != null )
524 q += " AND [whenGathered] <= $end";
529 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
530 ValueFactory valueFactory = getJcrSession().getValueFactory();
531 if ( startTime != null )
533 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
535 if ( endTime != null )
537 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
539 QueryResult result = query.execute();
541 artifacts = new ArrayList<ArtifactMetadata>();
542 for ( Node n : JcrUtils.getNodes( result ) )
544 artifacts.add( getArtifactFromNode( repoId, n ) );
547 catch ( RepositoryException e )
549 throw new MetadataRepositoryException( e.getMessage(), e );
554 public Collection<String> getRepositories()
555 throws MetadataRepositoryException
557 List<String> repositories;
561 Node root = getJcrSession().getRootNode();
562 if ( root.hasNode( "repositories" ) )
564 Node node = root.getNode( "repositories" );
566 repositories = new ArrayList<String>();
567 NodeIterator i = node.getNodes();
568 while ( i.hasNext() )
570 Node n = i.nextNode();
571 repositories.add( n.getName() );
576 repositories = Collections.emptyList();
579 catch ( RepositoryException e )
581 throw new MetadataRepositoryException( e.getMessage(), e );
586 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
587 throws MetadataRepositoryException
589 List<ArtifactMetadata> artifacts;
591 String q = getArtifactQuery( repositoryId ) + " AND ([sha1] = $checksum OR [md5] = $checksum)";
595 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
596 ValueFactory valueFactory = getJcrSession().getValueFactory();
597 query.bindValue( "checksum", valueFactory.createValue( checksum ) );
598 QueryResult result = query.execute();
600 artifacts = new ArrayList<ArtifactMetadata>();
601 for ( Node n : JcrUtils.getNodes( result ) )
603 artifacts.add( getArtifactFromNode( repositoryId, n ) );
606 catch ( RepositoryException e )
608 throw new MetadataRepositoryException( e.getMessage(), e );
613 public void removeArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
615 throws MetadataRepositoryException
619 Node root = getJcrSession().getRootNode();
620 String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
621 if ( root.hasNode( path ) )
623 root.getNode( path ).remove();
626 catch ( RepositoryException e )
628 throw new MetadataRepositoryException( e.getMessage(), e );
632 public void removeRepository( String repositoryId )
633 throws MetadataRepositoryException
637 Node root = getJcrSession().getRootNode();
638 String path = getRepositoryPath( repositoryId );
639 if ( root.hasNode( path ) )
641 root.getNode( path ).remove();
644 catch ( RepositoryException e )
646 throw new MetadataRepositoryException( e.getMessage(), e );
650 public List<ArtifactMetadata> getArtifacts( String repositoryId )
651 throws MetadataRepositoryException
653 List<ArtifactMetadata> artifacts;
655 String q = getArtifactQuery( repositoryId );
659 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
660 QueryResult result = query.execute();
662 artifacts = new ArrayList<ArtifactMetadata>();
663 for ( Node n : JcrUtils.getNodes( result ) )
665 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
667 artifacts.add( getArtifactFromNode( repositoryId, n ) );
671 catch ( RepositoryException e )
673 throw new MetadataRepositoryException( e.getMessage(), e );
678 private static String getArtifactQuery( String repositoryId )
680 return "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/" +
681 getRepositoryContentPath( repositoryId ) + "')";
684 public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
685 throws MetadataResolutionException
687 ProjectMetadata metadata = null;
691 Node root = getJcrSession().getRootNode();
693 // basically just checking it exists
694 String path = getProjectPath( repositoryId, namespace, projectId );
695 if ( root.hasNode( path ) )
697 metadata = new ProjectMetadata();
698 metadata.setId( projectId );
699 metadata.setNamespace( namespace );
702 catch ( RepositoryException e )
704 throw new MetadataResolutionException( e.getMessage(), e );
710 public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
711 String projectVersion )
712 throws MetadataResolutionException
714 ProjectVersionMetadata versionMetadata;
718 Node root = getJcrSession().getRootNode();
720 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
721 if ( !root.hasNode( path ) )
726 Node node = root.getNode( path );
728 versionMetadata = new ProjectVersionMetadata();
729 versionMetadata.setId( projectVersion );
730 versionMetadata.setName( getPropertyString( node, "name" ) );
731 versionMetadata.setDescription( getPropertyString( node, "description" ) );
732 versionMetadata.setUrl( getPropertyString( node, "url" ) );
733 versionMetadata.setIncomplete(
734 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
736 // FIXME: decide how to treat these in the content repo
737 String scmConnection = getPropertyString( node, "scm.connection" );
738 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
739 String scmUrl = getPropertyString( node, "scm.url" );
740 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
743 scm.setConnection( scmConnection );
744 scm.setDeveloperConnection( scmDeveloperConnection );
745 scm.setUrl( scmUrl );
746 versionMetadata.setScm( scm );
749 String ciSystem = getPropertyString( node, "ci.system" );
750 String ciUrl = getPropertyString( node, "ci.url" );
751 if ( ciSystem != null || ciUrl != null )
753 CiManagement ci = new CiManagement();
754 ci.setSystem( ciSystem );
756 versionMetadata.setCiManagement( ci );
759 String issueSystem = getPropertyString( node, "issue.system" );
760 String issueUrl = getPropertyString( node, "issue.url" );
761 if ( issueSystem != null || issueUrl != null )
763 IssueManagement issueManagement = new IssueManagement();
764 issueManagement.setSystem( issueSystem );
765 issueManagement.setUrl( issueUrl );
766 versionMetadata.setIssueManagement( issueManagement );
769 String orgName = getPropertyString( node, "org.name" );
770 String orgUrl = getPropertyString( node, "org.url" );
771 if ( orgName != null || orgUrl != null )
773 Organization org = new Organization();
774 org.setName( orgName );
775 org.setUrl( orgUrl );
776 versionMetadata.setOrganization( org );
779 boolean done = false;
783 String licenseName = getPropertyString( node, "license." + i + ".name" );
784 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
785 if ( licenseName != null || licenseUrl != null )
787 License license = new License();
788 license.setName( licenseName );
789 license.setUrl( licenseUrl );
790 versionMetadata.addLicense( license );
803 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
804 if ( mailingListName != null )
806 MailingList mailingList = new MailingList();
807 mailingList.setName( mailingListName );
808 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
809 String n = "mailingList." + i + ".otherArchives";
810 if ( node.hasProperty( n ) )
812 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
816 mailingList.setOtherArchives( Collections.<String>emptyList() );
818 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
819 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
820 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
821 versionMetadata.addMailingList( mailingList );
830 if ( node.hasNode( "dependencies" ) )
832 Node dependenciesNode = node.getNode( "dependencies" );
833 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
835 if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
837 Dependency dependency = new Dependency();
838 // FIXME: correct these properties
839 dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
840 dependency.setGroupId( getPropertyString( n, "groupId" ) );
841 dependency.setClassifier( getPropertyString( n, "classifier" ) );
842 dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
843 dependency.setScope( getPropertyString( n, "scope" ) );
844 dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
845 dependency.setType( getPropertyString( n, "type" ) );
846 dependency.setVersion( getPropertyString( n, "version" ) );
847 versionMetadata.addDependency( dependency );
852 for ( Node n : JcrUtils.getChildNodes( node ) )
854 if ( n.isNodeType( FACET_NODE_TYPE ) )
856 String name = n.getName();
857 MetadataFacetFactory factory = metadataFacetFactories.get( name );
858 if ( factory == null )
860 log.error( "Attempted to load unknown project version metadata facet: {}", name );
864 MetadataFacet facet = factory.createMetadataFacet();
865 Map<String, String> map = new HashMap<String, String>();
866 for ( Property property : JcrUtils.getProperties( n ) )
868 String p = property.getName();
869 if ( !p.startsWith( "jcr:" ) )
871 map.put( p, property.getString() );
874 facet.fromProperties( map );
875 versionMetadata.addFacet( facet );
880 catch ( RepositoryException e )
882 throw new MetadataResolutionException( e.getMessage(), e );
885 return versionMetadata;
888 public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
889 String projectVersion )
890 throws MetadataResolutionException
892 Set<String> versions = new LinkedHashSet<String>();
896 Node root = getJcrSession().getRootNode();
898 Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
900 for ( Node n : JcrUtils.getChildNodes( node ) )
902 versions.add( n.getProperty( "version" ).getString() );
905 catch ( PathNotFoundException e )
907 // ignore repo not found for now
909 catch ( RepositoryException e )
911 throw new MetadataResolutionException( e.getMessage(), e );
917 public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
918 String projectId, String projectVersion )
919 throws MetadataResolutionException
921 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
923 // TODO: bind variables instead
924 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId +
925 "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
926 if ( projectVersion != null )
928 q += " AND [version]='" + projectVersion + "'";
932 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
933 QueryResult result = query.execute();
935 for ( Node n : JcrUtils.getNodes( result ) )
937 n = n.getParent(); // dependencies grouping element
939 n = n.getParent(); // project version
940 String usedByProjectVersion = n.getName();
942 n = n.getParent(); // project
943 String usedByProject = n.getName();
945 n = n.getParent(); // namespace
946 String usedByNamespace = n.getProperty( "namespace" ).getString();
948 ProjectVersionReference ref = new ProjectVersionReference();
949 ref.setNamespace( usedByNamespace );
950 ref.setProjectId( usedByProject );
951 ref.setProjectVersion( usedByProjectVersion );
952 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
953 references.add( ref );
956 catch ( RepositoryException e )
958 throw new MetadataResolutionException( e.getMessage(), e );
964 public Collection<String> getRootNamespaces( String repositoryId )
965 throws MetadataResolutionException
967 return getNamespaces( repositoryId, null );
970 public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
971 throws MetadataResolutionException
973 String path = baseNamespace != null
974 ? getNamespacePath( repositoryId, baseNamespace )
975 : getRepositoryContentPath( repositoryId );
977 return getNodeNames( path, NAMESPACE_NODE_TYPE );
980 public Collection<String> getProjects( String repositoryId, String namespace )
981 throws MetadataResolutionException
983 return getNodeNames( getNamespacePath( repositoryId, namespace ), PROJECT_NODE_TYPE );
986 public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
987 throws MetadataResolutionException
989 return getNodeNames( getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
992 public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
993 String projectVersion )
994 throws MetadataResolutionException
996 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1000 Node root = getJcrSession().getRootNode();
1001 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1003 if ( root.hasNode( path ) )
1005 Node node = root.getNode( path );
1007 for ( Node n : JcrUtils.getChildNodes( node ) )
1009 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1011 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1016 catch ( RepositoryException e )
1018 throw new MetadataResolutionException( e.getMessage(), e );
1028 getJcrSession().save();
1030 catch ( RepositoryException e )
1032 throw new RuntimeException( e.getMessage(), e );
1036 public void revert()
1040 getJcrSession().refresh( false );
1042 catch ( RepositoryException e )
1044 throw new RuntimeException( e.getMessage(), e );
1048 public boolean canObtainAccess( Class<?> aClass )
1050 return aClass == Session.class;
1053 public Object obtainAccess( Class<?> aClass )
1054 throws MetadataRepositoryException
1056 if ( aClass == Session.class )
1060 return getJcrSession();
1062 catch ( RepositoryException e )
1064 log.error( e.getMessage(), e );
1065 throw new MetadataRepositoryException( e.getMessage(), e );
1068 throw new IllegalArgumentException(
1069 "Access using " + aClass + " is not supported on the JCR metadata storage" );
1073 throws MetadataRepositoryException
1077 if ( getJcrSession().isLive() )
1079 getJcrSession().logout();
1082 catch ( RepositoryException e )
1084 log.error( e.getMessage(), e );
1085 throw new MetadataRepositoryException( e.getMessage(), e );
1089 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1090 throws RepositoryException
1092 String id = artifactNode.getName();
1094 ArtifactMetadata artifact = new ArtifactMetadata();
1095 artifact.setId( id );
1096 artifact.setRepositoryId( repositoryId );
1098 Node projectVersionNode = artifactNode.getParent();
1099 Node projectNode = projectVersionNode.getParent();
1100 Node namespaceNode = projectNode.getParent();
1102 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1103 artifact.setProject( projectNode.getName() );
1104 artifact.setProjectVersion( projectVersionNode.getName() );
1105 artifact.setVersion( artifactNode.hasProperty( "version" )
1106 ? artifactNode.getProperty( "version" ).getString()
1107 : projectVersionNode.getName() );
1109 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1111 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1114 if ( artifactNode.hasProperty( "whenGathered" ) )
1116 artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1119 if ( artifactNode.hasProperty( "size" ) )
1121 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1124 if ( artifactNode.hasProperty( "md5" ) )
1126 artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1129 if ( artifactNode.hasProperty( "sha1" ) )
1131 artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1134 for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1136 if ( n.isNodeType( FACET_NODE_TYPE ) )
1138 String name = n.getName();
1139 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1140 if ( factory == null )
1142 log.error( "Attempted to load unknown project version metadata facet: " + name );
1146 MetadataFacet facet = factory.createMetadataFacet();
1147 Map<String, String> map = new HashMap<String, String>();
1148 for ( Property p : JcrUtils.getProperties( n ) )
1150 String property = p.getName();
1151 if ( !property.startsWith( "jcr:" ) )
1153 map.put( property, p.getString() );
1156 facet.fromProperties( map );
1157 artifact.addFacet( facet );
1164 private static String getPropertyString( Node node, String name )
1165 throws RepositoryException
1167 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1170 private Collection<String> getNodeNames( String path, String nodeType )
1171 throws MetadataResolutionException
1173 List<String> names = new ArrayList<String>();
1177 Node root = getJcrSession().getRootNode();
1179 Node nodeAtPath = root.getNode( path );
1181 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1183 if ( node.isNodeType( nodeType ) )
1185 names.add( node.getName() );
1189 catch ( PathNotFoundException e )
1191 // ignore repo not found for now
1193 catch ( RepositoryException e )
1195 throw new MetadataResolutionException( e.getMessage(), e );
1201 private static String getRepositoryPath( String repositoryId )
1203 return "repositories/" + repositoryId;
1206 private static String getRepositoryContentPath( String repositoryId )
1208 return getRepositoryPath( repositoryId ) + "/content/";
1211 private static String getFacetPath( String repositoryId, String facetId )
1213 return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1216 private static String getNamespacePath( String repositoryId, String namespace )
1218 return getRepositoryContentPath( repositoryId ) + namespace.replace( '.', '/' );
1221 private static String getProjectPath( String repositoryId, String namespace, String projectId )
1223 return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1226 private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1227 String projectVersion )
1229 return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1232 private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1233 String projectVersion, String id )
1235 return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1238 private Node getOrAddNodeByPath( Node baseNode, String name )
1239 throws RepositoryException
1241 return getOrAddNodeByPath( baseNode, name, null );
1244 private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1245 throws RepositoryException
1247 Node node = baseNode;
1248 for ( String n : name.split( "/" ) )
1250 node = JcrUtils.getOrAddNode( node, n );
1251 if ( nodeType != null )
1253 node.addMixin( nodeType );
1259 private static String getFacetPath( String repositoryId, String facetId, String name )
1261 return getFacetPath( repositoryId, facetId ) + "/" + name;
1264 private Node getOrAddRepositoryNode( String repositoryId )
1265 throws RepositoryException
1267 Node root = getJcrSession().getRootNode();
1268 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1269 node = JcrUtils.getOrAddNode( node, repositoryId );
1273 private Node getOrAddRepositoryContentNode( String repositoryId )
1274 throws RepositoryException
1276 Node node = getOrAddRepositoryNode( repositoryId );
1277 return JcrUtils.getOrAddNode( node, "content" );
1280 private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1281 throws RepositoryException
1283 Node repo = getOrAddRepositoryContentNode( repositoryId );
1284 return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1287 private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1288 throws RepositoryException
1290 Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1291 Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1292 node.addMixin( PROJECT_NODE_TYPE );
1296 private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1297 String projectVersion )
1298 throws RepositoryException
1300 Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1301 Node node = JcrUtils.getOrAddNode( projectNode, projectVersion );
1302 node.addMixin( PROJECT_VERSION_NODE_TYPE );
1306 private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1308 throws RepositoryException
1310 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1311 Node node = JcrUtils.getOrAddNode( versionNode, id );
1312 node.addMixin( ARTIFACT_NODE_TYPE );
1316 private static Calendar createCalendar( Date time )
1318 Calendar cal = Calendar.getInstance();
1319 cal.setTime( time );
1323 private String join( Collection<String> ids )
1325 if ( ids != null && !ids.isEmpty() )
1327 StringBuilder s = new StringBuilder();
1328 for ( String id : ids )
1333 return s.substring( 0, s.length() - 1 );
1338 public Session getJcrSession()
1339 throws RepositoryException
1341 if ( this.jcrSession == null || !this.jcrSession.isLive() )
1344 jcrSession = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
1347 return this.jcrSession;