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 if ( metadataFacetFactories == null )
418 return metadataFacet;
421 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
422 if ( metadataFacetFactory != null )
424 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
425 Map<String, String> map = new HashMap<String, String>();
426 for ( Property property : JcrUtils.getProperties( node ) )
428 String p = property.getName();
429 if ( !p.startsWith( "jcr:" ) )
431 map.put( p, property.getString() );
434 metadataFacet.fromProperties( map );
437 catch ( PathNotFoundException e )
439 // ignored - the facet doesn't exist, so return null
441 catch ( RepositoryException e )
443 throw new MetadataRepositoryException( e.getMessage(), e );
445 return metadataFacet;
448 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
449 throws MetadataRepositoryException
453 Node repo = getOrAddRepositoryNode( repositoryId );
454 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
456 String id = metadataFacet.getFacetId();
457 Node facetNode = JcrUtils.getOrAddNode( facets, id );
459 Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
461 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
463 node.setProperty( entry.getKey(), entry.getValue() );
466 catch ( RepositoryException e )
468 throw new MetadataRepositoryException( e.getMessage(), e );
472 public void removeMetadataFacets( String repositoryId, String facetId )
473 throws MetadataRepositoryException
477 Node root = getJcrSession().getRootNode();
478 String path = getFacetPath( repositoryId, facetId );
479 if ( root.hasNode( path ) )
481 root.getNode( path ).remove();
484 catch ( RepositoryException e )
486 throw new MetadataRepositoryException( e.getMessage(), e );
490 public void removeMetadataFacet( String repositoryId, String facetId, String name )
491 throws MetadataRepositoryException
495 Node root = getJcrSession().getRootNode();
496 String path = getFacetPath( repositoryId, facetId, name );
497 if ( root.hasNode( path ) )
499 Node node = root.getNode( path );
502 // also remove empty container nodes
503 Node parent = node.getParent();
507 while ( !node.hasNodes() );
510 catch ( RepositoryException e )
512 throw new MetadataRepositoryException( e.getMessage(), e );
516 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
517 throws MetadataRepositoryException
519 List<ArtifactMetadata> artifacts;
521 String q = getArtifactQuery( repoId );
523 if ( startTime != null )
525 q += " AND [whenGathered] >= $start";
527 if ( endTime != null )
529 q += " AND [whenGathered] <= $end";
534 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
535 ValueFactory valueFactory = getJcrSession().getValueFactory();
536 if ( startTime != null )
538 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
540 if ( endTime != null )
542 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
544 QueryResult result = query.execute();
546 artifacts = new ArrayList<ArtifactMetadata>();
547 for ( Node n : JcrUtils.getNodes( result ) )
549 artifacts.add( getArtifactFromNode( repoId, n ) );
552 catch ( RepositoryException e )
554 throw new MetadataRepositoryException( e.getMessage(), e );
559 public Collection<String> getRepositories()
560 throws MetadataRepositoryException
562 List<String> repositories;
566 Node root = getJcrSession().getRootNode();
567 if ( root.hasNode( "repositories" ) )
569 Node node = root.getNode( "repositories" );
571 repositories = new ArrayList<String>();
572 NodeIterator i = node.getNodes();
573 while ( i.hasNext() )
575 Node n = i.nextNode();
576 repositories.add( n.getName() );
581 repositories = Collections.emptyList();
584 catch ( RepositoryException e )
586 throw new MetadataRepositoryException( e.getMessage(), e );
591 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
592 throws MetadataRepositoryException
594 List<ArtifactMetadata> artifacts;
596 String q = getArtifactQuery( repositoryId ) + " AND ([sha1] = $checksum OR [md5] = $checksum)";
600 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
601 ValueFactory valueFactory = getJcrSession().getValueFactory();
602 query.bindValue( "checksum", valueFactory.createValue( checksum ) );
603 QueryResult result = query.execute();
605 artifacts = new ArrayList<ArtifactMetadata>();
606 for ( Node n : JcrUtils.getNodes( result ) )
608 artifacts.add( getArtifactFromNode( repositoryId, n ) );
611 catch ( RepositoryException e )
613 throw new MetadataRepositoryException( e.getMessage(), e );
618 public void removeArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
620 throws MetadataRepositoryException
624 Node root = getJcrSession().getRootNode();
625 String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
626 if ( root.hasNode( path ) )
628 root.getNode( path ).remove();
631 catch ( RepositoryException e )
633 throw new MetadataRepositoryException( e.getMessage(), e );
637 public void removeRepository( String repositoryId )
638 throws MetadataRepositoryException
642 Node root = getJcrSession().getRootNode();
643 String path = getRepositoryPath( repositoryId );
644 if ( root.hasNode( path ) )
646 root.getNode( path ).remove();
649 catch ( RepositoryException e )
651 throw new MetadataRepositoryException( e.getMessage(), e );
655 public List<ArtifactMetadata> getArtifacts( String repositoryId )
656 throws MetadataRepositoryException
658 List<ArtifactMetadata> artifacts;
660 String q = getArtifactQuery( repositoryId );
664 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
665 QueryResult result = query.execute();
667 artifacts = new ArrayList<ArtifactMetadata>();
668 for ( Node n : JcrUtils.getNodes( result ) )
670 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
672 artifacts.add( getArtifactFromNode( repositoryId, n ) );
676 catch ( RepositoryException e )
678 throw new MetadataRepositoryException( e.getMessage(), e );
683 private static String getArtifactQuery( String repositoryId )
685 return "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/" +
686 getRepositoryContentPath( repositoryId ) + "')";
689 public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
690 throws MetadataResolutionException
692 ProjectMetadata metadata = null;
696 Node root = getJcrSession().getRootNode();
698 // basically just checking it exists
699 String path = getProjectPath( repositoryId, namespace, projectId );
700 if ( root.hasNode( path ) )
702 metadata = new ProjectMetadata();
703 metadata.setId( projectId );
704 metadata.setNamespace( namespace );
707 catch ( RepositoryException e )
709 throw new MetadataResolutionException( e.getMessage(), e );
715 public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
716 String projectVersion )
717 throws MetadataResolutionException
719 ProjectVersionMetadata versionMetadata;
723 Node root = getJcrSession().getRootNode();
725 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
726 if ( !root.hasNode( path ) )
731 Node node = root.getNode( path );
733 versionMetadata = new ProjectVersionMetadata();
734 versionMetadata.setId( projectVersion );
735 versionMetadata.setName( getPropertyString( node, "name" ) );
736 versionMetadata.setDescription( getPropertyString( node, "description" ) );
737 versionMetadata.setUrl( getPropertyString( node, "url" ) );
738 versionMetadata.setIncomplete(
739 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
741 // FIXME: decide how to treat these in the content repo
742 String scmConnection = getPropertyString( node, "scm.connection" );
743 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
744 String scmUrl = getPropertyString( node, "scm.url" );
745 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
748 scm.setConnection( scmConnection );
749 scm.setDeveloperConnection( scmDeveloperConnection );
750 scm.setUrl( scmUrl );
751 versionMetadata.setScm( scm );
754 String ciSystem = getPropertyString( node, "ci.system" );
755 String ciUrl = getPropertyString( node, "ci.url" );
756 if ( ciSystem != null || ciUrl != null )
758 CiManagement ci = new CiManagement();
759 ci.setSystem( ciSystem );
761 versionMetadata.setCiManagement( ci );
764 String issueSystem = getPropertyString( node, "issue.system" );
765 String issueUrl = getPropertyString( node, "issue.url" );
766 if ( issueSystem != null || issueUrl != null )
768 IssueManagement issueManagement = new IssueManagement();
769 issueManagement.setSystem( issueSystem );
770 issueManagement.setUrl( issueUrl );
771 versionMetadata.setIssueManagement( issueManagement );
774 String orgName = getPropertyString( node, "org.name" );
775 String orgUrl = getPropertyString( node, "org.url" );
776 if ( orgName != null || orgUrl != null )
778 Organization org = new Organization();
779 org.setName( orgName );
780 org.setUrl( orgUrl );
781 versionMetadata.setOrganization( org );
784 boolean done = false;
788 String licenseName = getPropertyString( node, "license." + i + ".name" );
789 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
790 if ( licenseName != null || licenseUrl != null )
792 License license = new License();
793 license.setName( licenseName );
794 license.setUrl( licenseUrl );
795 versionMetadata.addLicense( license );
808 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
809 if ( mailingListName != null )
811 MailingList mailingList = new MailingList();
812 mailingList.setName( mailingListName );
813 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
814 String n = "mailingList." + i + ".otherArchives";
815 if ( node.hasProperty( n ) )
817 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
821 mailingList.setOtherArchives( Collections.<String>emptyList() );
823 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
824 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
825 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
826 versionMetadata.addMailingList( mailingList );
835 if ( node.hasNode( "dependencies" ) )
837 Node dependenciesNode = node.getNode( "dependencies" );
838 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
840 if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
842 Dependency dependency = new Dependency();
843 // FIXME: correct these properties
844 dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
845 dependency.setGroupId( getPropertyString( n, "groupId" ) );
846 dependency.setClassifier( getPropertyString( n, "classifier" ) );
847 dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
848 dependency.setScope( getPropertyString( n, "scope" ) );
849 dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
850 dependency.setType( getPropertyString( n, "type" ) );
851 dependency.setVersion( getPropertyString( n, "version" ) );
852 versionMetadata.addDependency( dependency );
857 for ( Node n : JcrUtils.getChildNodes( node ) )
859 if ( n.isNodeType( FACET_NODE_TYPE ) )
861 String name = n.getName();
862 MetadataFacetFactory factory = metadataFacetFactories.get( name );
863 if ( factory == null )
865 log.error( "Attempted to load unknown project version metadata facet: {}", name );
869 MetadataFacet facet = factory.createMetadataFacet();
870 Map<String, String> map = new HashMap<String, String>();
871 for ( Property property : JcrUtils.getProperties( n ) )
873 String p = property.getName();
874 if ( !p.startsWith( "jcr:" ) )
876 map.put( p, property.getString() );
879 facet.fromProperties( map );
880 versionMetadata.addFacet( facet );
885 catch ( RepositoryException e )
887 throw new MetadataResolutionException( e.getMessage(), e );
890 return versionMetadata;
893 public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
894 String projectVersion )
895 throws MetadataResolutionException
897 Set<String> versions = new LinkedHashSet<String>();
901 Node root = getJcrSession().getRootNode();
903 Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
905 for ( Node n : JcrUtils.getChildNodes( node ) )
907 versions.add( n.getProperty( "version" ).getString() );
910 catch ( PathNotFoundException e )
912 // ignore repo not found for now
914 catch ( RepositoryException e )
916 throw new MetadataResolutionException( e.getMessage(), e );
922 public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
923 String projectId, String projectVersion )
924 throws MetadataResolutionException
926 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
928 // TODO: bind variables instead
929 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId +
930 "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
931 if ( projectVersion != null )
933 q += " AND [version]='" + projectVersion + "'";
937 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
938 QueryResult result = query.execute();
940 for ( Node n : JcrUtils.getNodes( result ) )
942 n = n.getParent(); // dependencies grouping element
944 n = n.getParent(); // project version
945 String usedByProjectVersion = n.getName();
947 n = n.getParent(); // project
948 String usedByProject = n.getName();
950 n = n.getParent(); // namespace
951 String usedByNamespace = n.getProperty( "namespace" ).getString();
953 ProjectVersionReference ref = new ProjectVersionReference();
954 ref.setNamespace( usedByNamespace );
955 ref.setProjectId( usedByProject );
956 ref.setProjectVersion( usedByProjectVersion );
957 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
958 references.add( ref );
961 catch ( RepositoryException e )
963 throw new MetadataResolutionException( e.getMessage(), e );
969 public Collection<String> getRootNamespaces( String repositoryId )
970 throws MetadataResolutionException
972 return getNamespaces( repositoryId, null );
975 public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
976 throws MetadataResolutionException
978 String path = baseNamespace != null
979 ? getNamespacePath( repositoryId, baseNamespace )
980 : getRepositoryContentPath( repositoryId );
982 return getNodeNames( path, NAMESPACE_NODE_TYPE );
985 public Collection<String> getProjects( String repositoryId, String namespace )
986 throws MetadataResolutionException
988 return getNodeNames( getNamespacePath( repositoryId, namespace ), PROJECT_NODE_TYPE );
991 public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
992 throws MetadataResolutionException
994 return getNodeNames( getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
997 public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
998 String projectVersion )
999 throws MetadataResolutionException
1001 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1005 Node root = getJcrSession().getRootNode();
1006 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1008 if ( root.hasNode( path ) )
1010 Node node = root.getNode( path );
1012 for ( Node n : JcrUtils.getChildNodes( node ) )
1014 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1016 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1021 catch ( RepositoryException e )
1023 throw new MetadataResolutionException( e.getMessage(), e );
1033 getJcrSession().save();
1035 catch ( RepositoryException e )
1037 throw new RuntimeException( e.getMessage(), e );
1041 public void revert()
1045 getJcrSession().refresh( false );
1047 catch ( RepositoryException e )
1049 throw new RuntimeException( e.getMessage(), e );
1053 public boolean canObtainAccess( Class<?> aClass )
1055 return aClass == Session.class;
1058 public Object obtainAccess( Class<?> aClass )
1059 throws MetadataRepositoryException
1061 if ( aClass == Session.class )
1065 return getJcrSession();
1067 catch ( RepositoryException e )
1069 log.error( e.getMessage(), e );
1070 throw new MetadataRepositoryException( e.getMessage(), e );
1073 throw new IllegalArgumentException(
1074 "Access using " + aClass + " is not supported on the JCR metadata storage" );
1078 throws MetadataRepositoryException
1082 if ( getJcrSession().isLive() )
1084 getJcrSession().logout();
1087 catch ( RepositoryException e )
1089 log.error( e.getMessage(), e );
1090 throw new MetadataRepositoryException( e.getMessage(), e );
1094 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1095 throws RepositoryException
1097 String id = artifactNode.getName();
1099 ArtifactMetadata artifact = new ArtifactMetadata();
1100 artifact.setId( id );
1101 artifact.setRepositoryId( repositoryId );
1103 Node projectVersionNode = artifactNode.getParent();
1104 Node projectNode = projectVersionNode.getParent();
1105 Node namespaceNode = projectNode.getParent();
1107 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1108 artifact.setProject( projectNode.getName() );
1109 artifact.setProjectVersion( projectVersionNode.getName() );
1110 artifact.setVersion( artifactNode.hasProperty( "version" )
1111 ? artifactNode.getProperty( "version" ).getString()
1112 : projectVersionNode.getName() );
1114 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1116 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1119 if ( artifactNode.hasProperty( "whenGathered" ) )
1121 artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1124 if ( artifactNode.hasProperty( "size" ) )
1126 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1129 if ( artifactNode.hasProperty( "md5" ) )
1131 artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1134 if ( artifactNode.hasProperty( "sha1" ) )
1136 artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1139 for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1141 if ( n.isNodeType( FACET_NODE_TYPE ) )
1143 String name = n.getName();
1144 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1145 if ( factory == null )
1147 log.error( "Attempted to load unknown project version metadata facet: " + name );
1151 MetadataFacet facet = factory.createMetadataFacet();
1152 Map<String, String> map = new HashMap<String, String>();
1153 for ( Property p : JcrUtils.getProperties( n ) )
1155 String property = p.getName();
1156 if ( !property.startsWith( "jcr:" ) )
1158 map.put( property, p.getString() );
1161 facet.fromProperties( map );
1162 artifact.addFacet( facet );
1169 private static String getPropertyString( Node node, String name )
1170 throws RepositoryException
1172 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1175 private Collection<String> getNodeNames( String path, String nodeType )
1176 throws MetadataResolutionException
1178 List<String> names = new ArrayList<String>();
1182 Node root = getJcrSession().getRootNode();
1184 Node nodeAtPath = root.getNode( path );
1186 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1188 if ( node.isNodeType( nodeType ) )
1190 names.add( node.getName() );
1194 catch ( PathNotFoundException e )
1196 // ignore repo not found for now
1198 catch ( RepositoryException e )
1200 throw new MetadataResolutionException( e.getMessage(), e );
1206 private static String getRepositoryPath( String repositoryId )
1208 return "repositories/" + repositoryId;
1211 private static String getRepositoryContentPath( String repositoryId )
1213 return getRepositoryPath( repositoryId ) + "/content/";
1216 private static String getFacetPath( String repositoryId, String facetId )
1218 return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1221 private static String getNamespacePath( String repositoryId, String namespace )
1223 return getRepositoryContentPath( repositoryId ) + namespace.replace( '.', '/' );
1226 private static String getProjectPath( String repositoryId, String namespace, String projectId )
1228 return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1231 private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1232 String projectVersion )
1234 return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1237 private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1238 String projectVersion, String id )
1240 return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1243 private Node getOrAddNodeByPath( Node baseNode, String name )
1244 throws RepositoryException
1246 return getOrAddNodeByPath( baseNode, name, null );
1249 private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1250 throws RepositoryException
1252 Node node = baseNode;
1253 for ( String n : name.split( "/" ) )
1255 node = JcrUtils.getOrAddNode( node, n );
1256 if ( nodeType != null )
1258 node.addMixin( nodeType );
1264 private static String getFacetPath( String repositoryId, String facetId, String name )
1266 return getFacetPath( repositoryId, facetId ) + "/" + name;
1269 private Node getOrAddRepositoryNode( String repositoryId )
1270 throws RepositoryException
1272 Node root = getJcrSession().getRootNode();
1273 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1274 node = JcrUtils.getOrAddNode( node, repositoryId );
1278 private Node getOrAddRepositoryContentNode( String repositoryId )
1279 throws RepositoryException
1281 Node node = getOrAddRepositoryNode( repositoryId );
1282 return JcrUtils.getOrAddNode( node, "content" );
1285 private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1286 throws RepositoryException
1288 Node repo = getOrAddRepositoryContentNode( repositoryId );
1289 return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1292 private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1293 throws RepositoryException
1295 Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1296 Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1297 node.addMixin( PROJECT_NODE_TYPE );
1301 private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1302 String projectVersion )
1303 throws RepositoryException
1305 Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1306 Node node = JcrUtils.getOrAddNode( projectNode, projectVersion );
1307 node.addMixin( PROJECT_VERSION_NODE_TYPE );
1311 private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1313 throws RepositoryException
1315 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1316 Node node = JcrUtils.getOrAddNode( versionNode, id );
1317 node.addMixin( ARTIFACT_NODE_TYPE );
1321 private static Calendar createCalendar( Date time )
1323 Calendar cal = Calendar.getInstance();
1324 cal.setTime( time );
1328 private String join( Collection<String> ids )
1330 if ( ids != null && !ids.isEmpty() )
1332 StringBuilder s = new StringBuilder();
1333 for ( String id : ids )
1338 return s.substring( 0, s.length() - 1 );
1343 public Session getJcrSession()
1344 throws RepositoryException
1346 if ( this.jcrSession == null || !this.jcrSession.isLive() )
1349 jcrSession = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
1352 return this.jcrSession;