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;
105 //session = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
109 static void initialize( Session session )
110 throws RepositoryException
112 // TODO: consider using namespaces for facets instead of the current approach:
113 // (if used, check if actually called by normal injection)
114 // for ( String facetId : metadataFacetFactories.keySet() )
116 // session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
119 Workspace workspace = session.getWorkspace();
120 NamespaceRegistry registry = workspace.getNamespaceRegistry();
122 if ( !Arrays.asList( registry.getPrefixes() ).contains( "archiva" ) )
124 registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
127 NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
128 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.NAMESPACE_NODE_TYPE );
129 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_NODE_TYPE );
130 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_VERSION_NODE_TYPE );
131 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.ARTIFACT_NODE_TYPE );
132 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.FACET_NODE_TYPE );
133 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.DEPENDENCY_NODE_TYPE );
136 private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
137 throws RepositoryException
139 NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
140 nodeType.setMixin( true );
141 nodeType.setName( name );
143 // for now just don't re-create - but in future if we change the definition, make sure to remove first as an
145 if ( !nodeTypeManager.hasNodeType( name ) )
147 nodeTypeManager.registerNodeType( nodeType, false );
151 public void updateProject( String repositoryId, ProjectMetadata project )
152 throws MetadataRepositoryException
154 updateProject( repositoryId, project.getNamespace(), project.getId() );
157 private void updateProject( String repositoryId, String namespace, String projectId )
158 throws MetadataRepositoryException
160 updateNamespace( repositoryId, namespace );
164 getOrAddProjectNode( repositoryId, namespace, projectId );
166 catch ( RepositoryException e )
168 throw new MetadataRepositoryException( e.getMessage(), e );
172 public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
173 ArtifactMetadata artifactMeta )
174 throws MetadataRepositoryException
176 updateNamespace( repositoryId, namespace );
181 getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion, artifactMeta.getId() );
183 Calendar cal = Calendar.getInstance();
184 cal.setTime( artifactMeta.getFileLastModified() );
185 node.setProperty( JCR_LAST_MODIFIED, cal );
187 cal = Calendar.getInstance();
188 cal.setTime( artifactMeta.getWhenGathered() );
189 node.setProperty( "whenGathered", cal );
191 node.setProperty( "size", artifactMeta.getSize() );
192 node.setProperty( "md5", artifactMeta.getMd5() );
193 node.setProperty( "sha1", artifactMeta.getSha1() );
195 node.setProperty( "version", artifactMeta.getVersion() );
197 // iterate over available facets to update/add/remove from the artifactMetadata
198 for ( String facetId : metadataFacetFactories.keySet() )
200 MetadataFacet metadataFacet = artifactMeta.getFacet( facetId );
201 if ( metadataFacet == null )
205 if ( node.hasNode( facetId ) )
207 node.getNode( facetId ).remove();
209 if ( metadataFacet != null )
211 // recreate, to ensure properties are removed
212 Node n = node.addNode( facetId );
213 n.addMixin( FACET_NODE_TYPE );
215 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
217 n.setProperty( entry.getKey(), entry.getValue() );
222 for ( MetadataFacet facet : artifactMeta.getFacetList() )
224 if ( node.hasNode( facet.getFacetId() ) )
226 node.getNode( facet.getFacetId() ).remove();
229 // recreate, to ensure properties are removed
230 Node n = node.addNode( facet.getFacetId() );
231 n.addMixin( FACET_NODE_TYPE );
233 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
235 n.setProperty( entry.getKey(), entry.getValue() );
240 catch ( RepositoryException e )
242 throw new MetadataRepositoryException( e.getMessage(), e );
246 public void updateProjectVersion( String repositoryId, String namespace, String projectId,
247 ProjectVersionMetadata versionMetadata )
248 throws MetadataRepositoryException
250 updateProject( repositoryId, namespace, projectId );
255 getOrAddProjectVersionNode( repositoryId, namespace, projectId, versionMetadata.getId() );
257 versionNode.setProperty( "name", versionMetadata.getName() );
258 versionNode.setProperty( "description", versionMetadata.getDescription() );
259 versionNode.setProperty( "url", versionMetadata.getUrl() );
260 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
262 // FIXME: decide how to treat these in the content repo
263 if ( versionMetadata.getScm() != null )
265 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
266 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
267 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
269 if ( versionMetadata.getCiManagement() != null )
271 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
272 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
274 if ( versionMetadata.getIssueManagement() != null )
276 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
277 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
279 if ( versionMetadata.getOrganization() != null )
281 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
282 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
285 for ( License license : versionMetadata.getLicenses() )
287 versionNode.setProperty( "license." + i + ".name", license.getName() );
288 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
292 for ( MailingList mailingList : versionMetadata.getMailingLists() )
294 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
295 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
296 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
297 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
298 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
299 versionNode.setProperty( "mailingList." + i + ".otherArchives",
300 join( mailingList.getOtherArchives() ) );
304 if ( !versionMetadata.getDependencies().isEmpty() )
306 Node dependenciesNode = JcrUtils.getOrAddNode( versionNode, "dependencies" );
308 for ( Dependency dependency : versionMetadata.getDependencies() )
310 // Note that we deliberately don't alter the namespace path - not enough dependencies for
311 // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each
312 // component of the ID as that creates extra depth and causes a great cost in space and memory
314 // FIXME: change group ID to namespace
315 // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now.
316 // This won't support types where the extension doesn't match the type.
317 // (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM)
319 dependency.getGroupId() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion();
320 if ( dependency.getClassifier() != null )
322 id += "-" + dependency.getClassifier();
324 id += "." + dependency.getType();
326 Node n = JcrUtils.getOrAddNode( dependenciesNode, id );
327 n.addMixin( DEPENDENCY_NODE_TYPE );
329 // FIXME: remove temp code just to make it keep working
330 n.setProperty( "groupId", dependency.getGroupId() );
331 n.setProperty( "artifactId", dependency.getArtifactId() );
332 n.setProperty( "version", dependency.getVersion() );
333 n.setProperty( "type", dependency.getType() );
334 n.setProperty( "classifier", dependency.getClassifier() );
335 n.setProperty( "scope", dependency.getScope() );
336 n.setProperty( "systemPath", dependency.getSystemPath() );
337 n.setProperty( "optional", dependency.isOptional() );
339 // node has no native content at this time, just facets
340 // no need to list a type as it's implied by the path. Parents are Maven specific.
342 // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet
343 // (should also have been added to the Dependency)
345 // TODO: add a property that is a weak reference to the originating artifact, creating it if
346 // necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an
347 // artifact, which gives a different meaning to "incomplete" which is a known local project
348 // that doesn't have metadata yet but has artifacts). (Though we may want to give it the
349 // artifact mixin and another property to identify all non-local artifacts for the closure
354 for ( MetadataFacet facet : versionMetadata.getFacetList() )
356 // recreate, to ensure properties are removed
357 if ( versionNode.hasNode( facet.getFacetId() ) )
359 versionNode.getNode( facet.getFacetId() ).remove();
361 Node n = versionNode.addNode( facet.getFacetId() );
362 n.addMixin( FACET_NODE_TYPE );
364 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
366 n.setProperty( entry.getKey(), entry.getValue() );
370 catch ( RepositoryException e )
372 throw new MetadataRepositoryException( e.getMessage(), e );
376 public void updateNamespace( String repositoryId, String namespace )
377 throws MetadataRepositoryException
381 Node node = getOrAddNamespaceNode( repositoryId, namespace );
382 node.setProperty( "namespace", namespace );
384 catch ( RepositoryException e )
386 throw new MetadataRepositoryException( e.getMessage(), e );
390 public List<String> getMetadataFacets( String repositoryId, String facetId )
391 throws MetadataRepositoryException
393 List<String> facets = new ArrayList<String>();
397 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
398 // are paths themselves
399 Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
401 // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
403 recurse( facets, "", node );
405 catch ( PathNotFoundException e )
407 // ignored - the facet doesn't exist, so return the empty list
409 catch ( RepositoryException e )
411 throw new MetadataRepositoryException( e.getMessage(), e );
416 private void recurse( List<String> facets, String prefix, Node node )
417 throws RepositoryException
419 for ( Node n : JcrUtils.getChildNodes( node ) )
421 String name = prefix + "/" + n.getName();
424 recurse( facets, name, n );
428 // strip leading / first
429 facets.add( name.substring( 1 ) );
434 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
435 throws MetadataRepositoryException
437 MetadataFacet metadataFacet = null;
440 Node root = getJcrSession().getRootNode();
441 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
443 if ( metadataFacetFactories == null )
445 return metadataFacet;
448 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
449 if ( metadataFacetFactory != null )
451 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
452 Map<String, String> map = new HashMap<String, String>();
453 for ( Property property : JcrUtils.getProperties( node ) )
455 String p = property.getName();
456 if ( !p.startsWith( "jcr:" ) )
458 map.put( p, property.getString() );
461 metadataFacet.fromProperties( map );
464 catch ( PathNotFoundException e )
466 // ignored - the facet doesn't exist, so return null
468 catch ( RepositoryException e )
470 throw new MetadataRepositoryException( e.getMessage(), e );
472 return metadataFacet;
475 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
476 throws MetadataRepositoryException
480 Node repo = getOrAddRepositoryNode( repositoryId );
481 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
483 String id = metadataFacet.getFacetId();
484 Node facetNode = JcrUtils.getOrAddNode( facets, id );
486 Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
488 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
490 node.setProperty( entry.getKey(), entry.getValue() );
493 catch ( RepositoryException e )
495 throw new MetadataRepositoryException( e.getMessage(), e );
499 public void removeMetadataFacets( String repositoryId, String facetId )
500 throws MetadataRepositoryException
504 Node root = getJcrSession().getRootNode();
505 String path = getFacetPath( repositoryId, facetId );
506 if ( root.hasNode( path ) )
508 root.getNode( path ).remove();
511 catch ( RepositoryException e )
513 throw new MetadataRepositoryException( e.getMessage(), e );
517 public void removeMetadataFacet( String repositoryId, String facetId, String name )
518 throws MetadataRepositoryException
522 Node root = getJcrSession().getRootNode();
523 String path = getFacetPath( repositoryId, facetId, name );
524 if ( root.hasNode( path ) )
526 Node node = root.getNode( path );
529 // also remove empty container nodes
530 Node parent = node.getParent();
534 while ( !node.hasNodes() );
537 catch ( RepositoryException e )
539 throw new MetadataRepositoryException( e.getMessage(), e );
543 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
544 throws MetadataRepositoryException
546 List<ArtifactMetadata> artifacts;
548 String q = getArtifactQuery( repoId );
550 if ( startTime != null )
552 q += " AND [whenGathered] >= $start";
554 if ( endTime != null )
556 q += " AND [whenGathered] <= $end";
561 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
562 ValueFactory valueFactory = getJcrSession().getValueFactory();
563 if ( startTime != null )
565 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
567 if ( endTime != null )
569 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
571 QueryResult result = query.execute();
573 artifacts = new ArrayList<ArtifactMetadata>();
574 for ( Node n : JcrUtils.getNodes( result ) )
576 artifacts.add( getArtifactFromNode( repoId, n ) );
579 catch ( RepositoryException e )
581 throw new MetadataRepositoryException( e.getMessage(), e );
586 public Collection<String> getRepositories()
587 throws MetadataRepositoryException
589 List<String> repositories;
593 Node root = getJcrSession().getRootNode();
594 if ( root.hasNode( "repositories" ) )
596 Node node = root.getNode( "repositories" );
598 repositories = new ArrayList<String>();
599 NodeIterator i = node.getNodes();
600 while ( i.hasNext() )
602 Node n = i.nextNode();
603 repositories.add( n.getName() );
608 repositories = Collections.emptyList();
611 catch ( RepositoryException e )
613 throw new MetadataRepositoryException( e.getMessage(), e );
618 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
619 throws MetadataRepositoryException
621 List<ArtifactMetadata> artifacts;
623 String q = getArtifactQuery( repositoryId ) + " AND ([sha1] = $checksum OR [md5] = $checksum)";
627 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
628 ValueFactory valueFactory = getJcrSession().getValueFactory();
629 query.bindValue( "checksum", valueFactory.createValue( checksum ) );
630 QueryResult result = query.execute();
632 artifacts = new ArrayList<ArtifactMetadata>();
633 for ( Node n : JcrUtils.getNodes( result ) )
635 artifacts.add( getArtifactFromNode( repositoryId, n ) );
638 catch ( RepositoryException e )
640 throw new MetadataRepositoryException( e.getMessage(), e );
646 public void removeRepository( String repositoryId )
647 throws MetadataRepositoryException
651 Node root = getJcrSession().getRootNode();
652 String path = getRepositoryPath( repositoryId );
653 if ( root.hasNode( path ) )
655 root.getNode( path ).remove();
658 catch ( RepositoryException e )
660 throw new MetadataRepositoryException( e.getMessage(), e );
664 public List<ArtifactMetadata> getArtifacts( String repositoryId )
665 throws MetadataRepositoryException
667 List<ArtifactMetadata> artifacts;
669 String q = getArtifactQuery( repositoryId );
673 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
674 QueryResult result = query.execute();
676 artifacts = new ArrayList<ArtifactMetadata>();
677 for ( Node n : JcrUtils.getNodes( result ) )
679 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
681 artifacts.add( getArtifactFromNode( repositoryId, n ) );
685 catch ( RepositoryException e )
687 throw new MetadataRepositoryException( e.getMessage(), e );
692 private static String getArtifactQuery( String repositoryId )
694 return "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/" +
695 getRepositoryContentPath( repositoryId ) + "')";
698 public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
699 throws MetadataResolutionException
701 ProjectMetadata metadata = null;
705 Node root = getJcrSession().getRootNode();
707 // basically just checking it exists
708 String path = getProjectPath( repositoryId, namespace, projectId );
709 if ( root.hasNode( path ) )
711 metadata = new ProjectMetadata();
712 metadata.setId( projectId );
713 metadata.setNamespace( namespace );
716 catch ( RepositoryException e )
718 throw new MetadataResolutionException( e.getMessage(), e );
724 public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
725 String projectVersion )
726 throws MetadataResolutionException
728 ProjectVersionMetadata versionMetadata;
732 Node root = getJcrSession().getRootNode();
734 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
735 if ( !root.hasNode( path ) )
740 Node node = root.getNode( path );
742 versionMetadata = new ProjectVersionMetadata();
743 versionMetadata.setId( projectVersion );
744 versionMetadata.setName( getPropertyString( node, "name" ) );
745 versionMetadata.setDescription( getPropertyString( node, "description" ) );
746 versionMetadata.setUrl( getPropertyString( node, "url" ) );
747 versionMetadata.setIncomplete(
748 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
750 // FIXME: decide how to treat these in the content repo
751 String scmConnection = getPropertyString( node, "scm.connection" );
752 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
753 String scmUrl = getPropertyString( node, "scm.url" );
754 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
757 scm.setConnection( scmConnection );
758 scm.setDeveloperConnection( scmDeveloperConnection );
759 scm.setUrl( scmUrl );
760 versionMetadata.setScm( scm );
763 String ciSystem = getPropertyString( node, "ci.system" );
764 String ciUrl = getPropertyString( node, "ci.url" );
765 if ( ciSystem != null || ciUrl != null )
767 CiManagement ci = new CiManagement();
768 ci.setSystem( ciSystem );
770 versionMetadata.setCiManagement( ci );
773 String issueSystem = getPropertyString( node, "issue.system" );
774 String issueUrl = getPropertyString( node, "issue.url" );
775 if ( issueSystem != null || issueUrl != null )
777 IssueManagement issueManagement = new IssueManagement();
778 issueManagement.setSystem( issueSystem );
779 issueManagement.setUrl( issueUrl );
780 versionMetadata.setIssueManagement( issueManagement );
783 String orgName = getPropertyString( node, "org.name" );
784 String orgUrl = getPropertyString( node, "org.url" );
785 if ( orgName != null || orgUrl != null )
787 Organization org = new Organization();
788 org.setName( orgName );
789 org.setUrl( orgUrl );
790 versionMetadata.setOrganization( org );
793 boolean done = false;
797 String licenseName = getPropertyString( node, "license." + i + ".name" );
798 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
799 if ( licenseName != null || licenseUrl != null )
801 License license = new License();
802 license.setName( licenseName );
803 license.setUrl( licenseUrl );
804 versionMetadata.addLicense( license );
817 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
818 if ( mailingListName != null )
820 MailingList mailingList = new MailingList();
821 mailingList.setName( mailingListName );
822 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
823 String n = "mailingList." + i + ".otherArchives";
824 if ( node.hasProperty( n ) )
826 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
830 mailingList.setOtherArchives( Collections.<String>emptyList() );
832 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
833 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
834 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
835 versionMetadata.addMailingList( mailingList );
844 if ( node.hasNode( "dependencies" ) )
846 Node dependenciesNode = node.getNode( "dependencies" );
847 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
849 if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
851 Dependency dependency = new Dependency();
852 // FIXME: correct these properties
853 dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
854 dependency.setGroupId( getPropertyString( n, "groupId" ) );
855 dependency.setClassifier( getPropertyString( n, "classifier" ) );
856 dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
857 dependency.setScope( getPropertyString( n, "scope" ) );
858 dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
859 dependency.setType( getPropertyString( n, "type" ) );
860 dependency.setVersion( getPropertyString( n, "version" ) );
861 versionMetadata.addDependency( dependency );
866 for ( Node n : JcrUtils.getChildNodes( node ) )
868 if ( n.isNodeType( FACET_NODE_TYPE ) )
870 String name = n.getName();
871 MetadataFacetFactory factory = metadataFacetFactories.get( name );
872 if ( factory == null )
874 log.error( "Attempted to load unknown project version metadata facet: {}", name );
878 MetadataFacet facet = factory.createMetadataFacet();
879 Map<String, String> map = new HashMap<String, String>();
880 for ( Property property : JcrUtils.getProperties( n ) )
882 String p = property.getName();
883 if ( !p.startsWith( "jcr:" ) )
885 map.put( p, property.getString() );
888 facet.fromProperties( map );
889 versionMetadata.addFacet( facet );
894 catch ( RepositoryException e )
896 throw new MetadataResolutionException( e.getMessage(), e );
899 return versionMetadata;
902 public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
903 String projectVersion )
904 throws MetadataResolutionException
906 Set<String> versions = new LinkedHashSet<String>();
910 Node root = getJcrSession().getRootNode();
912 Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
914 for ( Node n : JcrUtils.getChildNodes( node ) )
916 versions.add( n.getProperty( "version" ).getString() );
919 catch ( PathNotFoundException e )
921 // ignore repo not found for now
923 catch ( RepositoryException e )
925 throw new MetadataResolutionException( e.getMessage(), e );
931 public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
932 String projectId, String projectVersion )
933 throws MetadataResolutionException
935 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
937 // TODO: bind variables instead
938 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId +
939 "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
940 if ( projectVersion != null )
942 q += " AND [version]='" + projectVersion + "'";
946 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
947 QueryResult result = query.execute();
949 for ( Node n : JcrUtils.getNodes( result ) )
951 n = n.getParent(); // dependencies grouping element
953 n = n.getParent(); // project version
954 String usedByProjectVersion = n.getName();
956 n = n.getParent(); // project
957 String usedByProject = n.getName();
959 n = n.getParent(); // namespace
960 String usedByNamespace = n.getProperty( "namespace" ).getString();
962 ProjectVersionReference ref = new ProjectVersionReference();
963 ref.setNamespace( usedByNamespace );
964 ref.setProjectId( usedByProject );
965 ref.setProjectVersion( usedByProjectVersion );
966 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
967 references.add( ref );
970 catch ( RepositoryException e )
972 throw new MetadataResolutionException( e.getMessage(), e );
978 public Collection<String> getRootNamespaces( String repositoryId )
979 throws MetadataResolutionException
981 return getNamespaces( repositoryId, null );
984 public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
985 throws MetadataResolutionException
987 String path = baseNamespace != null
988 ? getNamespacePath( repositoryId, baseNamespace )
989 : getRepositoryContentPath( repositoryId );
991 return getNodeNames( path, NAMESPACE_NODE_TYPE );
994 public Collection<String> getProjects( String repositoryId, String namespace )
995 throws MetadataResolutionException
997 return getNodeNames( getNamespacePath( repositoryId, namespace ), PROJECT_NODE_TYPE );
1000 public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
1001 throws MetadataResolutionException
1003 return getNodeNames( getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
1006 public void removeArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
1008 throws MetadataRepositoryException
1012 Node root = getJcrSession().getRootNode();
1013 String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
1014 if ( root.hasNode( path ) )
1016 root.getNode( path ).remove();
1021 path = getProjectPath( repositoryId, namespace, projectId );
1023 Node nodeAtPath = root.getNode( path );
1025 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1027 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( node.getName(),
1034 catch ( RepositoryException e )
1036 throw new MetadataRepositoryException( e.getMessage(), e );
1040 public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion,
1041 MetadataFacet metadataFacet )
1042 throws MetadataRepositoryException
1046 Node root = getJcrSession().getRootNode();
1047 String path = getProjectVersionPath( repositoryId, namespace, project, projectVersion );
1049 if ( root.hasNode( path ) )
1051 Node node = root.getNode( path );
1053 for ( Node n : JcrUtils.getChildNodes( node ) )
1055 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1057 ArtifactMetadata artifactMetadata = getArtifactFromNode( repositoryId, n );
1058 log.debug( "artifactMetadata: {}", artifactMetadata );
1059 MetadataFacet metadataFacetToRemove = artifactMetadata.getFacet( metadataFacet.getFacetId() );
1060 if ( metadataFacetToRemove != null && metadataFacet.equals( metadataFacetToRemove ) )
1068 catch ( RepositoryException e )
1070 throw new MetadataRepositoryException( e.getMessage(), e );
1074 public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
1075 String projectVersion )
1076 throws MetadataResolutionException
1078 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1082 Node root = getJcrSession().getRootNode();
1083 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1085 if ( root.hasNode( path ) )
1087 Node node = root.getNode( path );
1089 for ( Node n : JcrUtils.getChildNodes( node ) )
1091 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1093 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1098 catch ( RepositoryException e )
1100 throw new MetadataResolutionException( e.getMessage(), e );
1110 getJcrSession().save();
1112 catch ( RepositoryException e )
1114 throw new RuntimeException( e.getMessage(), e );
1118 public void revert()
1122 getJcrSession().refresh( false );
1124 catch ( RepositoryException e )
1126 throw new RuntimeException( e.getMessage(), e );
1130 public boolean canObtainAccess( Class<?> aClass )
1132 return aClass == Session.class;
1135 public Object obtainAccess( Class<?> aClass )
1136 throws MetadataRepositoryException
1138 if ( aClass == Session.class )
1142 return getJcrSession();
1144 catch ( RepositoryException e )
1146 log.error( e.getMessage(), e );
1147 throw new MetadataRepositoryException( e.getMessage(), e );
1150 throw new IllegalArgumentException(
1151 "Access using " + aClass + " is not supported on the JCR metadata storage" );
1155 throws MetadataRepositoryException
1159 if ( getJcrSession().isLive() )
1161 getJcrSession().logout();
1164 catch ( RepositoryException e )
1166 log.error( e.getMessage(), e );
1167 throw new MetadataRepositoryException( e.getMessage(), e );
1171 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1172 throws RepositoryException
1174 String id = artifactNode.getName();
1176 ArtifactMetadata artifact = new ArtifactMetadata();
1177 artifact.setId( id );
1178 artifact.setRepositoryId( repositoryId );
1180 Node projectVersionNode = artifactNode.getParent();
1181 Node projectNode = projectVersionNode.getParent();
1182 Node namespaceNode = projectNode.getParent();
1184 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1185 artifact.setProject( projectNode.getName() );
1186 artifact.setProjectVersion( projectVersionNode.getName() );
1187 artifact.setVersion( artifactNode.hasProperty( "version" )
1188 ? artifactNode.getProperty( "version" ).getString()
1189 : projectVersionNode.getName() );
1191 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1193 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1196 if ( artifactNode.hasProperty( "whenGathered" ) )
1198 artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1201 if ( artifactNode.hasProperty( "size" ) )
1203 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1206 if ( artifactNode.hasProperty( "md5" ) )
1208 artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1211 if ( artifactNode.hasProperty( "sha1" ) )
1213 artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1216 for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1218 if ( n.isNodeType( FACET_NODE_TYPE ) )
1220 String name = n.getName();
1221 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1222 if ( factory == null )
1224 log.error( "Attempted to load unknown project version metadata facet: " + name );
1228 MetadataFacet facet = factory.createMetadataFacet();
1229 Map<String, String> map = new HashMap<String, String>();
1230 for ( Property p : JcrUtils.getProperties( n ) )
1232 String property = p.getName();
1233 if ( !property.startsWith( "jcr:" ) )
1235 map.put( property, p.getString() );
1238 facet.fromProperties( map );
1239 artifact.addFacet( facet );
1246 private static String getPropertyString( Node node, String name )
1247 throws RepositoryException
1249 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1252 private Collection<String> getNodeNames( String path, String nodeType )
1253 throws MetadataResolutionException
1255 List<String> names = new ArrayList<String>();
1259 Node root = getJcrSession().getRootNode();
1261 Node nodeAtPath = root.getNode( path );
1263 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1265 if ( node.isNodeType( nodeType ) )
1267 names.add( node.getName() );
1271 catch ( PathNotFoundException e )
1273 // ignore repo not found for now
1275 catch ( RepositoryException e )
1277 throw new MetadataResolutionException( e.getMessage(), e );
1283 private static String getRepositoryPath( String repositoryId )
1285 return "repositories/" + repositoryId;
1288 private static String getRepositoryContentPath( String repositoryId )
1290 return getRepositoryPath( repositoryId ) + "/content/";
1293 private static String getFacetPath( String repositoryId, String facetId )
1295 return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1298 private static String getNamespacePath( String repositoryId, String namespace )
1300 return getRepositoryContentPath( repositoryId ) + namespace.replace( '.', '/' );
1303 private static String getProjectPath( String repositoryId, String namespace, String projectId )
1305 return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1308 private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1309 String projectVersion )
1311 return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1314 private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1315 String projectVersion, String id )
1317 return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1320 private Node getOrAddNodeByPath( Node baseNode, String name )
1321 throws RepositoryException
1323 return getOrAddNodeByPath( baseNode, name, null );
1326 private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1327 throws RepositoryException
1329 Node node = baseNode;
1330 for ( String n : name.split( "/" ) )
1332 node = JcrUtils.getOrAddNode( node, n );
1333 if ( nodeType != null )
1335 node.addMixin( nodeType );
1341 private static String getFacetPath( String repositoryId, String facetId, String name )
1343 return getFacetPath( repositoryId, facetId ) + "/" + name;
1346 private Node getOrAddRepositoryNode( String repositoryId )
1347 throws RepositoryException
1349 Node root = getJcrSession().getRootNode();
1350 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1351 node = JcrUtils.getOrAddNode( node, repositoryId );
1355 private Node getOrAddRepositoryContentNode( String repositoryId )
1356 throws RepositoryException
1358 Node node = getOrAddRepositoryNode( repositoryId );
1359 return JcrUtils.getOrAddNode( node, "content" );
1362 private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1363 throws RepositoryException
1365 Node repo = getOrAddRepositoryContentNode( repositoryId );
1366 return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1369 private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1370 throws RepositoryException
1372 Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1373 Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1374 node.addMixin( PROJECT_NODE_TYPE );
1378 private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1379 String projectVersion )
1380 throws RepositoryException
1382 Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1383 Node node = JcrUtils.getOrAddNode( projectNode, projectVersion );
1384 node.addMixin( PROJECT_VERSION_NODE_TYPE );
1388 private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1390 throws RepositoryException
1392 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1393 Node node = JcrUtils.getOrAddNode( versionNode, id );
1394 node.addMixin( ARTIFACT_NODE_TYPE );
1398 private static Calendar createCalendar( Date time )
1400 Calendar cal = Calendar.getInstance();
1401 cal.setTime( time );
1405 private String join( Collection<String> ids )
1407 if ( ids != null && !ids.isEmpty() )
1409 StringBuilder s = new StringBuilder();
1410 for ( String id : ids )
1415 return s.substring( 0, s.length() - 1 );
1420 public Session getJcrSession()
1421 throws RepositoryException
1423 if ( this.jcrSession == null || !this.jcrSession.isLive() )
1426 jcrSession = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
1429 return this.jcrSession;