1 package org.apache.archiva.metadata.repository.jcr;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.archiva.metadata.model.ArtifactMetadata;
23 import org.apache.archiva.metadata.model.CiManagement;
24 import org.apache.archiva.metadata.model.Dependency;
25 import org.apache.archiva.metadata.model.IssueManagement;
26 import org.apache.archiva.metadata.model.License;
27 import org.apache.archiva.metadata.model.MailingList;
28 import org.apache.archiva.metadata.model.MetadataFacet;
29 import org.apache.archiva.metadata.model.MetadataFacetFactory;
30 import org.apache.archiva.metadata.model.Organization;
31 import org.apache.archiva.metadata.model.ProjectMetadata;
32 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
33 import org.apache.archiva.metadata.model.ProjectVersionReference;
34 import org.apache.archiva.metadata.model.Scm;
35 import org.apache.archiva.metadata.repository.MetadataRepository;
36 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
37 import org.apache.archiva.metadata.repository.MetadataResolutionException;
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.jackrabbit.commons.JcrUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import javax.jcr.NamespaceRegistry;
44 import javax.jcr.Node;
45 import javax.jcr.NodeIterator;
46 import javax.jcr.PathNotFoundException;
47 import javax.jcr.Property;
48 import javax.jcr.Repository;
49 import javax.jcr.RepositoryException;
50 import javax.jcr.Session;
51 import javax.jcr.SimpleCredentials;
52 import javax.jcr.ValueFactory;
53 import javax.jcr.Workspace;
54 import javax.jcr.nodetype.NodeTypeManager;
55 import javax.jcr.nodetype.NodeTypeTemplate;
56 import javax.jcr.query.Query;
57 import javax.jcr.query.QueryResult;
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.Calendar;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.Date;
64 import java.util.HashMap;
65 import java.util.LinkedHashSet;
66 import java.util.List;
71 * @todo below: revise storage format for project version metadata
72 * @todo revise reference storage
74 public class JcrMetadataRepository
75 implements MetadataRepository
77 private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
79 static final String NAMESPACE_NODE_TYPE = "archiva:namespace";
81 static final String PROJECT_NODE_TYPE = "archiva:project";
83 static final String PROJECT_VERSION_NODE_TYPE = "archiva:projectVersion";
85 static final String ARTIFACT_NODE_TYPE = "archiva:artifact";
87 static final String FACET_NODE_TYPE = "archiva:facet";
89 private static final String DEPENDENCY_NODE_TYPE = "archiva:dependency";
91 private final Map<String, MetadataFacetFactory> metadataFacetFactories;
93 private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
95 private Repository repository;
97 private Session jcrSession;
99 public JcrMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories, Repository repository )
100 throws RepositoryException
102 this.metadataFacetFactories = metadataFacetFactories;
103 this.repository = repository;
107 static void initialize( Session session )
108 throws RepositoryException
110 // TODO: consider using namespaces for facets instead of the current approach:
111 // (if used, check if actually called by normal injection)
112 // for ( String facetId : metadataFacetFactories.keySet() )
114 // session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
117 Workspace workspace = session.getWorkspace();
118 NamespaceRegistry registry = workspace.getNamespaceRegistry();
120 if ( !Arrays.asList( registry.getPrefixes() ).contains( "archiva" ) )
122 registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
125 NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
126 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.NAMESPACE_NODE_TYPE );
127 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_NODE_TYPE );
128 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_VERSION_NODE_TYPE );
129 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.ARTIFACT_NODE_TYPE );
130 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.FACET_NODE_TYPE );
131 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.DEPENDENCY_NODE_TYPE );
134 private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
135 throws RepositoryException
137 NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
138 nodeType.setMixin( true );
139 nodeType.setName( name );
141 // for now just don't re-create - but in future if we change the definition, make sure to remove first as an
143 if ( !nodeTypeManager.hasNodeType( name ) )
145 nodeTypeManager.registerNodeType( nodeType, false );
149 public void updateProject( String repositoryId, ProjectMetadata project )
150 throws MetadataRepositoryException
152 updateProject( repositoryId, project.getNamespace(), project.getId() );
155 private void updateProject( String repositoryId, String namespace, String projectId )
156 throws MetadataRepositoryException
158 updateNamespace( repositoryId, namespace );
162 getOrAddProjectNode( repositoryId, namespace, projectId );
164 catch ( RepositoryException e )
166 throw new MetadataRepositoryException( e.getMessage(), e );
170 public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
171 ArtifactMetadata artifactMeta )
172 throws MetadataRepositoryException
174 updateNamespace( repositoryId, namespace );
179 getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion, artifactMeta.getId() );
181 Calendar cal = Calendar.getInstance();
182 cal.setTime( artifactMeta.getFileLastModified() );
183 node.setProperty( JCR_LAST_MODIFIED, cal );
185 cal = Calendar.getInstance();
186 cal.setTime( artifactMeta.getWhenGathered() );
187 node.setProperty( "whenGathered", cal );
189 node.setProperty( "size", artifactMeta.getSize() );
190 node.setProperty( "md5", artifactMeta.getMd5() );
191 node.setProperty( "sha1", artifactMeta.getSha1() );
193 node.setProperty( "version", artifactMeta.getVersion() );
195 // iterate over available facets to update/add/remove from the artifactMetadata
196 for ( String facetId : metadataFacetFactories.keySet() )
198 MetadataFacet metadataFacet = artifactMeta.getFacet( facetId );
199 if ( metadataFacet == null )
203 if ( node.hasNode( facetId ) )
205 node.getNode( facetId ).remove();
207 if ( metadataFacet != null )
209 // recreate, to ensure properties are removed
210 Node n = node.addNode( facetId );
211 n.addMixin( FACET_NODE_TYPE );
213 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
215 n.setProperty( entry.getKey(), entry.getValue() );
220 for ( MetadataFacet facet : artifactMeta.getFacetList() )
222 if ( node.hasNode( facet.getFacetId() ) )
224 node.getNode( facet.getFacetId() ).remove();
227 // recreate, to ensure properties are removed
228 Node n = node.addNode( facet.getFacetId() );
229 n.addMixin( FACET_NODE_TYPE );
231 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
233 n.setProperty( entry.getKey(), entry.getValue() );
238 catch ( RepositoryException e )
240 throw new MetadataRepositoryException( e.getMessage(), e );
244 public void updateProjectVersion( String repositoryId, String namespace, String projectId,
245 ProjectVersionMetadata versionMetadata )
246 throws MetadataRepositoryException
248 updateProject( repositoryId, namespace, projectId );
253 getOrAddProjectVersionNode( repositoryId, namespace, projectId, versionMetadata.getId() );
255 versionNode.setProperty( "name", versionMetadata.getName() );
256 versionNode.setProperty( "description", versionMetadata.getDescription() );
257 versionNode.setProperty( "url", versionMetadata.getUrl() );
258 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
260 // FIXME: decide how to treat these in the content repo
261 if ( versionMetadata.getScm() != null )
263 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
264 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
265 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
267 if ( versionMetadata.getCiManagement() != null )
269 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
270 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
272 if ( versionMetadata.getIssueManagement() != null )
274 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
275 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
277 if ( versionMetadata.getOrganization() != null )
279 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
280 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
283 for ( License license : versionMetadata.getLicenses() )
285 versionNode.setProperty( "license." + i + ".name", license.getName() );
286 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
290 for ( MailingList mailingList : versionMetadata.getMailingLists() )
292 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
293 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
294 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
295 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
296 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
297 versionNode.setProperty( "mailingList." + i + ".otherArchives",
298 join( mailingList.getOtherArchives() ) );
302 if ( !versionMetadata.getDependencies().isEmpty() )
304 Node dependenciesNode = JcrUtils.getOrAddNode( versionNode, "dependencies" );
306 for ( Dependency dependency : versionMetadata.getDependencies() )
308 // Note that we deliberately don't alter the namespace path - not enough dependencies for
309 // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each
310 // component of the ID as that creates extra depth and causes a great cost in space and memory
312 // FIXME: change group ID to namespace
313 // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now.
314 // This won't support types where the extension doesn't match the type.
315 // (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM)
317 dependency.getGroupId() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion();
318 if ( dependency.getClassifier() != null )
320 id += "-" + dependency.getClassifier();
322 id += "." + dependency.getType();
324 Node n = JcrUtils.getOrAddNode( dependenciesNode, id );
325 n.addMixin( DEPENDENCY_NODE_TYPE );
327 // FIXME: remove temp code just to make it keep working
328 n.setProperty( "groupId", dependency.getGroupId() );
329 n.setProperty( "artifactId", dependency.getArtifactId() );
330 n.setProperty( "version", dependency.getVersion() );
331 n.setProperty( "type", dependency.getType() );
332 n.setProperty( "classifier", dependency.getClassifier() );
333 n.setProperty( "scope", dependency.getScope() );
334 n.setProperty( "systemPath", dependency.getSystemPath() );
335 n.setProperty( "optional", dependency.isOptional() );
337 // node has no native content at this time, just facets
338 // no need to list a type as it's implied by the path. Parents are Maven specific.
340 // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet
341 // (should also have been added to the Dependency)
343 // TODO: add a property that is a weak reference to the originating artifact, creating it if
344 // necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an
345 // artifact, which gives a different meaning to "incomplete" which is a known local project
346 // that doesn't have metadata yet but has artifacts). (Though we may want to give it the
347 // artifact mixin and another property to identify all non-local artifacts for the closure
352 for ( MetadataFacet facet : versionMetadata.getFacetList() )
354 // recreate, to ensure properties are removed
355 if ( versionNode.hasNode( facet.getFacetId() ) )
357 versionNode.getNode( facet.getFacetId() ).remove();
359 Node n = versionNode.addNode( facet.getFacetId() );
360 n.addMixin( FACET_NODE_TYPE );
362 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
364 n.setProperty( entry.getKey(), entry.getValue() );
368 catch ( RepositoryException e )
370 throw new MetadataRepositoryException( e.getMessage(), e );
374 public void updateNamespace( String repositoryId, String namespace )
375 throws MetadataRepositoryException
379 Node node = getOrAddNamespaceNode( repositoryId, namespace );
380 node.setProperty( "namespace", namespace );
382 catch ( RepositoryException e )
384 throw new MetadataRepositoryException( e.getMessage(), e );
388 public List<String> getMetadataFacets( String repositoryId, String facetId )
389 throws MetadataRepositoryException
391 List<String> facets = new ArrayList<String>();
395 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
396 // are paths themselves
397 Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
399 // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
401 recurse( facets, "", node );
403 catch ( PathNotFoundException e )
405 // ignored - the facet doesn't exist, so return the empty list
407 catch ( RepositoryException e )
409 throw new MetadataRepositoryException( e.getMessage(), e );
414 private void recurse( List<String> facets, String prefix, Node node )
415 throws RepositoryException
417 for ( Node n : JcrUtils.getChildNodes( node ) )
419 String name = prefix + "/" + n.getName();
422 recurse( facets, name, n );
426 // strip leading / first
427 facets.add( name.substring( 1 ) );
432 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
433 throws MetadataRepositoryException
435 MetadataFacet metadataFacet = null;
438 Node root = getJcrSession().getRootNode();
439 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
441 if ( metadataFacetFactories == null )
443 return metadataFacet;
446 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
447 if ( metadataFacetFactory != null )
449 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
450 Map<String, String> map = new HashMap<String, String>();
451 for ( Property property : JcrUtils.getProperties( node ) )
453 String p = property.getName();
454 if ( !p.startsWith( "jcr:" ) )
456 map.put( p, property.getString() );
459 metadataFacet.fromProperties( map );
462 catch ( PathNotFoundException e )
464 // ignored - the facet doesn't exist, so return null
466 catch ( RepositoryException e )
468 throw new MetadataRepositoryException( e.getMessage(), e );
470 return metadataFacet;
473 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
474 throws MetadataRepositoryException
478 Node repo = getOrAddRepositoryNode( repositoryId );
479 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
481 String id = metadataFacet.getFacetId();
482 Node facetNode = JcrUtils.getOrAddNode( facets, id );
484 Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
486 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
488 node.setProperty( entry.getKey(), entry.getValue() );
491 catch ( RepositoryException e )
493 throw new MetadataRepositoryException( e.getMessage(), e );
497 public void removeMetadataFacets( String repositoryId, String facetId )
498 throws MetadataRepositoryException
502 Node root = getJcrSession().getRootNode();
503 String path = getFacetPath( repositoryId, facetId );
504 if ( root.hasNode( path ) )
506 root.getNode( path ).remove();
509 catch ( RepositoryException e )
511 throw new MetadataRepositoryException( e.getMessage(), e );
515 public void removeMetadataFacet( String repositoryId, String facetId, String name )
516 throws MetadataRepositoryException
520 Node root = getJcrSession().getRootNode();
521 String path = getFacetPath( repositoryId, facetId, name );
522 if ( root.hasNode( path ) )
524 Node node = root.getNode( path );
527 // also remove empty container nodes
528 Node parent = node.getParent();
532 while ( !node.hasNodes() );
535 catch ( RepositoryException e )
537 throw new MetadataRepositoryException( e.getMessage(), e );
541 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
542 throws MetadataRepositoryException
544 List<ArtifactMetadata> artifacts;
546 String q = getArtifactQuery( repoId );
548 if ( startTime != null )
550 q += " AND [whenGathered] >= $start";
552 if ( endTime != null )
554 q += " AND [whenGathered] <= $end";
559 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
560 ValueFactory valueFactory = getJcrSession().getValueFactory();
561 if ( startTime != null )
563 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
565 if ( endTime != null )
567 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
569 QueryResult result = query.execute();
571 artifacts = new ArrayList<ArtifactMetadata>();
572 for ( Node n : JcrUtils.getNodes( result ) )
574 artifacts.add( getArtifactFromNode( repoId, n ) );
577 catch ( RepositoryException e )
579 throw new MetadataRepositoryException( e.getMessage(), e );
584 public Collection<String> getRepositories()
585 throws MetadataRepositoryException
587 List<String> repositories;
591 Node root = getJcrSession().getRootNode();
592 if ( root.hasNode( "repositories" ) )
594 Node node = root.getNode( "repositories" );
596 repositories = new ArrayList<String>();
597 NodeIterator i = node.getNodes();
598 while ( i.hasNext() )
600 Node n = i.nextNode();
601 repositories.add( n.getName() );
606 repositories = Collections.emptyList();
609 catch ( RepositoryException e )
611 throw new MetadataRepositoryException( e.getMessage(), e );
616 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
617 throws MetadataRepositoryException
619 List<ArtifactMetadata> artifacts;
621 String q = getArtifactQuery( repositoryId ) + " AND ([sha1] = $checksum OR [md5] = $checksum)";
625 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
626 ValueFactory valueFactory = getJcrSession().getValueFactory();
627 query.bindValue( "checksum", valueFactory.createValue( checksum ) );
628 QueryResult result = query.execute();
630 artifacts = new ArrayList<ArtifactMetadata>();
631 for ( Node n : JcrUtils.getNodes( result ) )
633 artifacts.add( getArtifactFromNode( repositoryId, n ) );
636 catch ( RepositoryException e )
638 throw new MetadataRepositoryException( e.getMessage(), e );
644 public void removeRepository( String repositoryId )
645 throws MetadataRepositoryException
649 Node root = getJcrSession().getRootNode();
650 String path = getRepositoryPath( repositoryId );
651 if ( root.hasNode( path ) )
653 root.getNode( path ).remove();
656 catch ( RepositoryException e )
658 throw new MetadataRepositoryException( e.getMessage(), e );
662 public List<ArtifactMetadata> getArtifacts( String repositoryId )
663 throws MetadataRepositoryException
665 List<ArtifactMetadata> artifacts;
667 String q = getArtifactQuery( repositoryId );
671 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
672 QueryResult result = query.execute();
674 artifacts = new ArrayList<ArtifactMetadata>();
675 for ( Node n : JcrUtils.getNodes( result ) )
677 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
679 artifacts.add( getArtifactFromNode( repositoryId, n ) );
683 catch ( RepositoryException e )
685 throw new MetadataRepositoryException( e.getMessage(), e );
690 private static String getArtifactQuery( String repositoryId )
692 return "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/" +
693 getRepositoryContentPath( repositoryId ) + "')";
696 public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
697 throws MetadataResolutionException
699 ProjectMetadata metadata = null;
703 Node root = getJcrSession().getRootNode();
705 // basically just checking it exists
706 String path = getProjectPath( repositoryId, namespace, projectId );
707 if ( root.hasNode( path ) )
709 metadata = new ProjectMetadata();
710 metadata.setId( projectId );
711 metadata.setNamespace( namespace );
714 catch ( RepositoryException e )
716 throw new MetadataResolutionException( e.getMessage(), e );
722 public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
723 String projectVersion )
724 throws MetadataResolutionException
726 ProjectVersionMetadata versionMetadata;
730 Node root = getJcrSession().getRootNode();
732 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
733 if ( !root.hasNode( path ) )
738 Node node = root.getNode( path );
740 versionMetadata = new ProjectVersionMetadata();
741 versionMetadata.setId( projectVersion );
742 versionMetadata.setName( getPropertyString( node, "name" ) );
743 versionMetadata.setDescription( getPropertyString( node, "description" ) );
744 versionMetadata.setUrl( getPropertyString( node, "url" ) );
745 versionMetadata.setIncomplete(
746 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
748 // FIXME: decide how to treat these in the content repo
749 String scmConnection = getPropertyString( node, "scm.connection" );
750 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
751 String scmUrl = getPropertyString( node, "scm.url" );
752 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
755 scm.setConnection( scmConnection );
756 scm.setDeveloperConnection( scmDeveloperConnection );
757 scm.setUrl( scmUrl );
758 versionMetadata.setScm( scm );
761 String ciSystem = getPropertyString( node, "ci.system" );
762 String ciUrl = getPropertyString( node, "ci.url" );
763 if ( ciSystem != null || ciUrl != null )
765 CiManagement ci = new CiManagement();
766 ci.setSystem( ciSystem );
768 versionMetadata.setCiManagement( ci );
771 String issueSystem = getPropertyString( node, "issue.system" );
772 String issueUrl = getPropertyString( node, "issue.url" );
773 if ( issueSystem != null || issueUrl != null )
775 IssueManagement issueManagement = new IssueManagement();
776 issueManagement.setSystem( issueSystem );
777 issueManagement.setUrl( issueUrl );
778 versionMetadata.setIssueManagement( issueManagement );
781 String orgName = getPropertyString( node, "org.name" );
782 String orgUrl = getPropertyString( node, "org.url" );
783 if ( orgName != null || orgUrl != null )
785 Organization org = new Organization();
786 org.setName( orgName );
787 org.setUrl( orgUrl );
788 versionMetadata.setOrganization( org );
791 boolean done = false;
795 String licenseName = getPropertyString( node, "license." + i + ".name" );
796 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
797 if ( licenseName != null || licenseUrl != null )
799 License license = new License();
800 license.setName( licenseName );
801 license.setUrl( licenseUrl );
802 versionMetadata.addLicense( license );
815 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
816 if ( mailingListName != null )
818 MailingList mailingList = new MailingList();
819 mailingList.setName( mailingListName );
820 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
821 String n = "mailingList." + i + ".otherArchives";
822 if ( node.hasProperty( n ) )
824 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
828 mailingList.setOtherArchives( Collections.<String>emptyList() );
830 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
831 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
832 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
833 versionMetadata.addMailingList( mailingList );
842 if ( node.hasNode( "dependencies" ) )
844 Node dependenciesNode = node.getNode( "dependencies" );
845 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
847 if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
849 Dependency dependency = new Dependency();
850 // FIXME: correct these properties
851 dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
852 dependency.setGroupId( getPropertyString( n, "groupId" ) );
853 dependency.setClassifier( getPropertyString( n, "classifier" ) );
854 dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
855 dependency.setScope( getPropertyString( n, "scope" ) );
856 dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
857 dependency.setType( getPropertyString( n, "type" ) );
858 dependency.setVersion( getPropertyString( n, "version" ) );
859 versionMetadata.addDependency( dependency );
864 for ( Node n : JcrUtils.getChildNodes( node ) )
866 if ( n.isNodeType( FACET_NODE_TYPE ) )
868 String name = n.getName();
869 MetadataFacetFactory factory = metadataFacetFactories.get( name );
870 if ( factory == null )
872 log.error( "Attempted to load unknown project version metadata facet: {}", name );
876 MetadataFacet facet = factory.createMetadataFacet();
877 Map<String, String> map = new HashMap<String, String>();
878 for ( Property property : JcrUtils.getProperties( n ) )
880 String p = property.getName();
881 if ( !p.startsWith( "jcr:" ) )
883 map.put( p, property.getString() );
886 facet.fromProperties( map );
887 versionMetadata.addFacet( facet );
892 catch ( RepositoryException e )
894 throw new MetadataResolutionException( e.getMessage(), e );
897 return versionMetadata;
900 public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
901 String projectVersion )
902 throws MetadataResolutionException
904 Set<String> versions = new LinkedHashSet<String>();
908 Node root = getJcrSession().getRootNode();
910 Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
912 for ( Node n : JcrUtils.getChildNodes( node ) )
914 versions.add( n.getProperty( "version" ).getString() );
917 catch ( PathNotFoundException e )
919 // ignore repo not found for now
921 catch ( RepositoryException e )
923 throw new MetadataResolutionException( e.getMessage(), e );
929 public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
930 String projectId, String projectVersion )
931 throws MetadataResolutionException
933 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
935 // TODO: bind variables instead
936 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId +
937 "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
938 if ( projectVersion != null )
940 q += " AND [version]='" + projectVersion + "'";
944 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
945 QueryResult result = query.execute();
947 for ( Node n : JcrUtils.getNodes( result ) )
949 n = n.getParent(); // dependencies grouping element
951 n = n.getParent(); // project version
952 String usedByProjectVersion = n.getName();
954 n = n.getParent(); // project
955 String usedByProject = n.getName();
957 n = n.getParent(); // namespace
958 String usedByNamespace = n.getProperty( "namespace" ).getString();
960 ProjectVersionReference ref = new ProjectVersionReference();
961 ref.setNamespace( usedByNamespace );
962 ref.setProjectId( usedByProject );
963 ref.setProjectVersion( usedByProjectVersion );
964 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
965 references.add( ref );
968 catch ( RepositoryException e )
970 throw new MetadataResolutionException( e.getMessage(), e );
976 public Collection<String> getRootNamespaces( String repositoryId )
977 throws MetadataResolutionException
979 return getNamespaces( repositoryId, null );
982 public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
983 throws MetadataResolutionException
985 String path = baseNamespace != null
986 ? getNamespacePath( repositoryId, baseNamespace )
987 : getRepositoryContentPath( repositoryId );
989 return getNodeNames( path, NAMESPACE_NODE_TYPE );
992 public Collection<String> getProjects( String repositoryId, String namespace )
993 throws MetadataResolutionException
995 return getNodeNames( getNamespacePath( repositoryId, namespace ), PROJECT_NODE_TYPE );
998 public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
999 throws MetadataResolutionException
1001 return getNodeNames( getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
1004 public void removeArtifact( ArtifactMetadata artifactMetadata, String baseVersion )
1005 throws MetadataRepositoryException
1008 String repositoryId = artifactMetadata.getRepositoryId();
1012 Node root = getJcrSession().getRootNode();
1014 getProjectVersionPath( repositoryId, artifactMetadata.getNamespace(), artifactMetadata.getProject(),
1017 if ( root.hasNode( path ) )
1019 Node node = root.getNode( path );
1021 for ( Node n : JcrUtils.getChildNodes( node ) )
1023 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1025 if ( n.hasProperty( "version" ) )
1027 String version = n.getProperty( "version" ).getString();
1028 if ( StringUtils.equals( version, artifactMetadata.getVersion() ) )
1038 catch ( RepositoryException e )
1040 throw new MetadataRepositoryException( e.getMessage(), e );
1046 public void removeArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
1048 throws MetadataRepositoryException
1052 Node root = getJcrSession().getRootNode();
1053 String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
1054 if ( root.hasNode( path ) )
1056 root.getNode( path ).remove();
1061 path = getProjectPath( repositoryId, namespace, projectId );
1063 Node nodeAtPath = root.getNode( path );
1065 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1067 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( node.getName(),
1074 catch ( RepositoryException e )
1076 throw new MetadataRepositoryException( e.getMessage(), e );
1080 public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion,
1081 MetadataFacet metadataFacet )
1082 throws MetadataRepositoryException
1086 Node root = getJcrSession().getRootNode();
1087 String path = getProjectVersionPath( repositoryId, namespace, project, projectVersion );
1089 if ( root.hasNode( path ) )
1091 Node node = root.getNode( path );
1093 for ( Node n : JcrUtils.getChildNodes( node ) )
1095 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1097 ArtifactMetadata artifactMetadata = getArtifactFromNode( repositoryId, n );
1098 log.debug( "artifactMetadata: {}", artifactMetadata );
1099 MetadataFacet metadataFacetToRemove = artifactMetadata.getFacet( metadataFacet.getFacetId() );
1100 if ( metadataFacetToRemove != null && metadataFacet.equals( metadataFacetToRemove ) )
1108 catch ( RepositoryException e )
1110 throw new MetadataRepositoryException( e.getMessage(), e );
1114 public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
1115 String projectVersion )
1116 throws MetadataResolutionException
1118 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1122 Node root = getJcrSession().getRootNode();
1123 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1125 if ( root.hasNode( path ) )
1127 Node node = root.getNode( path );
1129 for ( Node n : JcrUtils.getChildNodes( node ) )
1131 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1133 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1138 catch ( RepositoryException e )
1140 throw new MetadataResolutionException( e.getMessage(), e );
1150 getJcrSession().save();
1152 catch ( RepositoryException e )
1154 throw new RuntimeException( e.getMessage(), e );
1158 public void revert()
1162 getJcrSession().refresh( false );
1164 catch ( RepositoryException e )
1166 throw new RuntimeException( e.getMessage(), e );
1170 public boolean canObtainAccess( Class<?> aClass )
1172 return aClass == Session.class;
1175 public Object obtainAccess( Class<?> aClass )
1176 throws MetadataRepositoryException
1178 if ( aClass == Session.class )
1182 return getJcrSession();
1184 catch ( RepositoryException e )
1186 log.error( e.getMessage(), e );
1187 throw new MetadataRepositoryException( e.getMessage(), e );
1190 throw new IllegalArgumentException(
1191 "Access using " + aClass + " is not supported on the JCR metadata storage" );
1195 throws MetadataRepositoryException
1199 if ( getJcrSession().isLive() )
1201 getJcrSession().logout();
1204 catch ( RepositoryException e )
1206 log.error( e.getMessage(), e );
1207 throw new MetadataRepositoryException( e.getMessage(), e );
1211 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1212 throws RepositoryException
1214 String id = artifactNode.getName();
1216 ArtifactMetadata artifact = new ArtifactMetadata();
1217 artifact.setId( id );
1218 artifact.setRepositoryId( repositoryId );
1220 Node projectVersionNode = artifactNode.getParent();
1221 Node projectNode = projectVersionNode.getParent();
1222 Node namespaceNode = projectNode.getParent();
1224 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1225 artifact.setProject( projectNode.getName() );
1226 artifact.setProjectVersion( projectVersionNode.getName() );
1227 artifact.setVersion( artifactNode.hasProperty( "version" )
1228 ? artifactNode.getProperty( "version" ).getString()
1229 : projectVersionNode.getName() );
1231 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1233 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1236 if ( artifactNode.hasProperty( "whenGathered" ) )
1238 artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1241 if ( artifactNode.hasProperty( "size" ) )
1243 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1246 if ( artifactNode.hasProperty( "md5" ) )
1248 artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1251 if ( artifactNode.hasProperty( "sha1" ) )
1253 artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1256 for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1258 if ( n.isNodeType( FACET_NODE_TYPE ) )
1260 String name = n.getName();
1261 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1262 if ( factory == null )
1264 log.error( "Attempted to load unknown project version metadata facet: " + name );
1268 MetadataFacet facet = factory.createMetadataFacet();
1269 Map<String, String> map = new HashMap<String, String>();
1270 for ( Property p : JcrUtils.getProperties( n ) )
1272 String property = p.getName();
1273 if ( !property.startsWith( "jcr:" ) )
1275 map.put( property, p.getString() );
1278 facet.fromProperties( map );
1279 artifact.addFacet( facet );
1286 private static String getPropertyString( Node node, String name )
1287 throws RepositoryException
1289 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1292 private Collection<String> getNodeNames( String path, String nodeType )
1293 throws MetadataResolutionException
1295 List<String> names = new ArrayList<String>();
1299 Node root = getJcrSession().getRootNode();
1301 Node nodeAtPath = root.getNode( path );
1303 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1305 if ( node.isNodeType( nodeType ) )
1307 names.add( node.getName() );
1311 catch ( PathNotFoundException e )
1313 // ignore repo not found for now
1315 catch ( RepositoryException e )
1317 throw new MetadataResolutionException( e.getMessage(), e );
1323 private static String getRepositoryPath( String repositoryId )
1325 return "repositories/" + repositoryId;
1328 private static String getRepositoryContentPath( String repositoryId )
1330 return getRepositoryPath( repositoryId ) + "/content/";
1333 private static String getFacetPath( String repositoryId, String facetId )
1335 return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1338 private static String getNamespacePath( String repositoryId, String namespace )
1340 return getRepositoryContentPath( repositoryId ) + namespace.replace( '.', '/' );
1343 private static String getProjectPath( String repositoryId, String namespace, String projectId )
1345 return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1348 private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1349 String projectVersion )
1351 return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1354 private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1355 String projectVersion, String id )
1357 return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1360 private Node getOrAddNodeByPath( Node baseNode, String name )
1361 throws RepositoryException
1363 return getOrAddNodeByPath( baseNode, name, null );
1366 private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1367 throws RepositoryException
1369 Node node = baseNode;
1370 for ( String n : name.split( "/" ) )
1372 node = JcrUtils.getOrAddNode( node, n );
1373 if ( nodeType != null )
1375 node.addMixin( nodeType );
1381 private static String getFacetPath( String repositoryId, String facetId, String name )
1383 return getFacetPath( repositoryId, facetId ) + "/" + name;
1386 private Node getOrAddRepositoryNode( String repositoryId )
1387 throws RepositoryException
1389 Node root = getJcrSession().getRootNode();
1390 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1391 node = JcrUtils.getOrAddNode( node, repositoryId );
1395 private Node getOrAddRepositoryContentNode( String repositoryId )
1396 throws RepositoryException
1398 Node node = getOrAddRepositoryNode( repositoryId );
1399 return JcrUtils.getOrAddNode( node, "content" );
1402 private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1403 throws RepositoryException
1405 Node repo = getOrAddRepositoryContentNode( repositoryId );
1406 return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1409 private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1410 throws RepositoryException
1412 Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1413 Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1414 node.addMixin( PROJECT_NODE_TYPE );
1418 private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1419 String projectVersion )
1420 throws RepositoryException
1422 Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1423 Node node = JcrUtils.getOrAddNode( projectNode, projectVersion );
1424 node.addMixin( PROJECT_VERSION_NODE_TYPE );
1428 private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1430 throws RepositoryException
1432 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1433 Node node = JcrUtils.getOrAddNode( versionNode, id );
1434 node.addMixin( ARTIFACT_NODE_TYPE );
1438 private static Calendar createCalendar( Date time )
1440 Calendar cal = Calendar.getInstance();
1441 cal.setTime( time );
1445 private String join( Collection<String> ids )
1447 if ( ids != null && !ids.isEmpty() )
1449 StringBuilder s = new StringBuilder();
1450 for ( String id : ids )
1455 return s.substring( 0, s.length() - 1 );
1460 public Session getJcrSession()
1461 throws RepositoryException
1463 if ( this.jcrSession == null || !this.jcrSession.isLive() )
1465 jcrSession = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
1467 return this.jcrSession;