]> source.dussan.org Git - archiva.git/blob
2d3688bb08bc42ba9a608d7eaf3c3c35cdb5ad4d
[archiva.git] /
1 package org.apache.archiva.metadata.repository.jcr;
2
3 /*
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
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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;
41
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Calendar;
45 import java.util.Collection;
46 import java.util.Collections;
47 import java.util.Date;
48 import java.util.HashMap;
49 import java.util.LinkedHashSet;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Set;
53 import javax.jcr.NamespaceRegistry;
54 import javax.jcr.Node;
55 import javax.jcr.NodeIterator;
56 import javax.jcr.PathNotFoundException;
57 import javax.jcr.Property;
58 import javax.jcr.Repository;
59 import javax.jcr.RepositoryException;
60 import javax.jcr.Session;
61 import javax.jcr.SimpleCredentials;
62 import javax.jcr.ValueFactory;
63 import javax.jcr.Workspace;
64 import javax.jcr.nodetype.NodeTypeManager;
65 import javax.jcr.nodetype.NodeTypeTemplate;
66 import javax.jcr.query.Query;
67 import javax.jcr.query.QueryResult;
68
69 /**
70  * @todo below: revise storage format for project version metadata
71  * @todo revise reference storage
72  */
73 public class JcrMetadataRepository
74     implements MetadataRepository
75 {
76     private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
77
78     static final String ARTIFACT_NODE_TYPE = "archiva:artifact";
79
80     static final String FACET_NODE_TYPE = "archiva:facet";
81
82     private static final String QUERY_ARTIFACTS = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "]";
83
84     private final Map<String, MetadataFacetFactory> metadataFacetFactories;
85
86     private static final Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
87
88     private Repository repository;
89
90     private Session session;
91
92     public JcrMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories, Repository repository )
93         throws RepositoryException
94     {
95         this.metadataFacetFactories = metadataFacetFactories;
96         this.repository = repository;
97
98         session = repository.login( new SimpleCredentials( "username", "password".toCharArray() ) );
99     }
100
101     static void initialize( Session session )
102         throws RepositoryException
103     {
104         // TODO: consider using namespaces for facets instead of the current approach:
105         // (if used, check if actually called by normal injection)
106 //        for ( String facetId : metadataFacetFactories.keySet() )
107 //        {
108 //            session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
109 //        }
110
111         Workspace workspace = session.getWorkspace();
112         NamespaceRegistry registry = workspace.getNamespaceRegistry();
113
114         if ( !Arrays.asList( registry.getPrefixes() ).contains( "archiva" ) )
115         {
116             registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
117         }
118
119         NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
120         registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.ARTIFACT_NODE_TYPE );
121         registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.FACET_NODE_TYPE );
122     }
123
124     private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
125         throws RepositoryException
126     {
127         NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
128         nodeType.setMixin( true );
129         nodeType.setName( name );
130
131         // for now just don't re-create - but in future if we change the definition, make sure to remove first as an
132         // upgrade path
133         if ( !nodeTypeManager.hasNodeType( name ) )
134         {
135             nodeTypeManager.registerNodeType( nodeType, false );
136         }
137     }
138
139     public void updateProject( String repositoryId, ProjectMetadata project )
140         throws MetadataRepositoryException
141     {
142         updateProject( repositoryId, project.getNamespace(), project.getId() );
143     }
144
145     private void updateProject( String repositoryId, String namespace, String projectId )
146         throws MetadataRepositoryException
147     {
148         updateNamespace( repositoryId, namespace );
149
150         try
151         {
152             getOrAddProjectNode( repositoryId, namespace, projectId );
153         }
154         catch ( RepositoryException e )
155         {
156             throw new MetadataRepositoryException( e.getMessage(), e );
157         }
158     }
159
160     public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
161                                 ArtifactMetadata artifactMeta )
162         throws MetadataRepositoryException
163     {
164         updateNamespace( repositoryId, namespace );
165
166         try
167         {
168             Node node = getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion,
169                                               artifactMeta.getId() );
170
171             Calendar cal = Calendar.getInstance();
172             cal.setTime( artifactMeta.getFileLastModified() );
173             node.setProperty( JCR_LAST_MODIFIED, cal );
174
175             cal = Calendar.getInstance();
176             cal.setTime( artifactMeta.getWhenGathered() );
177             node.setProperty( "whenGathered", cal );
178
179             node.setProperty( "size", artifactMeta.getSize() );
180             node.setProperty( "md5", artifactMeta.getMd5() );
181             node.setProperty( "sha1", artifactMeta.getSha1() );
182
183             node.setProperty( "version", artifactMeta.getVersion() );
184
185             for ( MetadataFacet facet : artifactMeta.getFacetList() )
186             {
187                 if ( node.hasNode( facet.getFacetId() ) )
188                 {
189                     node.getNode( facet.getFacetId() ).remove();
190                 }
191
192                 // recreate, to ensure properties are removed
193                 Node n = node.addNode( facet.getFacetId() );
194                 n.addMixin( FACET_NODE_TYPE );
195
196                 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
197                 {
198                     n.setProperty( entry.getKey(), entry.getValue() );
199                 }
200             }
201             // FIXME: need some context around this so it can be done only when needed
202             session.save();
203         }
204         catch ( RepositoryException e )
205         {
206             throw new MetadataRepositoryException( e.getMessage(), e );
207         }
208     }
209
210     public void updateProjectVersion( String repositoryId, String namespace, String projectId,
211                                       ProjectVersionMetadata versionMetadata )
212         throws MetadataRepositoryException
213     {
214         updateProject( repositoryId, namespace, projectId );
215
216         try
217         {
218             Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId,
219                                                            versionMetadata.getId() );
220
221             versionNode.setProperty( "name", versionMetadata.getName() );
222             versionNode.setProperty( "description", versionMetadata.getDescription() );
223             versionNode.setProperty( "url", versionMetadata.getUrl() );
224             versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
225
226             // FIXME: decide how to treat these in the content repo
227             if ( versionMetadata.getScm() != null )
228             {
229                 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
230                 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
231                 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
232             }
233             if ( versionMetadata.getCiManagement() != null )
234             {
235                 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
236                 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
237             }
238             if ( versionMetadata.getIssueManagement() != null )
239             {
240                 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
241                 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
242             }
243             if ( versionMetadata.getOrganization() != null )
244             {
245                 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
246                 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
247             }
248             int i = 0;
249             for ( License license : versionMetadata.getLicenses() )
250             {
251                 versionNode.setProperty( "license." + i + ".name", license.getName() );
252                 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
253                 i++;
254             }
255             i = 0;
256             for ( MailingList mailingList : versionMetadata.getMailingLists() )
257             {
258                 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
259                 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
260                 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
261                 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
262                 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
263                 versionNode.setProperty( "mailingList." + i + ".otherArchives", join(
264                     mailingList.getOtherArchives() ) );
265                 i++;
266             }
267             i = 0;
268             for ( Dependency dependency : versionMetadata.getDependencies() )
269             {
270                 versionNode.setProperty( "dependency." + i + ".classifier", dependency.getClassifier() );
271                 versionNode.setProperty( "dependency." + i + ".scope", dependency.getScope() );
272                 versionNode.setProperty( "dependency." + i + ".systemPath", dependency.getSystemPath() );
273                 versionNode.setProperty( "dependency." + i + ".artifactId", dependency.getArtifactId() );
274                 versionNode.setProperty( "dependency." + i + ".groupId", dependency.getGroupId() );
275                 versionNode.setProperty( "dependency." + i + ".version", dependency.getVersion() );
276                 versionNode.setProperty( "dependency." + i + ".type", dependency.getType() );
277                 i++;
278             }
279
280             for ( MetadataFacet facet : versionMetadata.getFacetList() )
281             {
282                 // recreate, to ensure properties are removed
283                 if ( versionNode.hasNode( facet.getFacetId() ) )
284                 {
285                     versionNode.getNode( facet.getFacetId() ).remove();
286                 }
287                 Node n = versionNode.addNode( facet.getFacetId() );
288                 n.addMixin( FACET_NODE_TYPE );
289
290                 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
291                 {
292                     n.setProperty( entry.getKey(), entry.getValue() );
293                 }
294             }
295         }
296         catch ( RepositoryException e )
297         {
298             throw new MetadataRepositoryException( e.getMessage(), e );
299         }
300     }
301
302     public void updateProjectReference( String repositoryId, String namespace, String projectId, String projectVersion,
303                                         ProjectVersionReference reference )
304         throws MetadataRepositoryException
305     {
306         // not using weak references, since they still need to exist upfront to be referred to
307         try
308         {
309             Node node = getOrAddRepositoryContentNode( repositoryId );
310             node = JcrUtils.getOrAddNode( node, namespace );
311             node = JcrUtils.getOrAddNode( node, projectId );
312             node = JcrUtils.getOrAddNode( node, projectVersion );
313             node = JcrUtils.getOrAddNode( node, "references" );
314             node = JcrUtils.getOrAddNode( node, reference.getNamespace() );
315             node = JcrUtils.getOrAddNode( node, reference.getProjectId() );
316             node = JcrUtils.getOrAddNode( node, reference.getProjectVersion() );
317             node.setProperty( "type", reference.getReferenceType().toString() );
318         }
319         catch ( RepositoryException e )
320         {
321             throw new MetadataRepositoryException( e.getMessage(), e );
322         }
323     }
324
325     public void updateNamespace( String repositoryId, String namespace )
326         throws MetadataRepositoryException
327     {
328         try
329         {
330             Node node = getOrAddNamespaceNode( repositoryId, namespace );
331             node.setProperty( "namespace", namespace );
332         }
333         catch ( RepositoryException e )
334         {
335             throw new MetadataRepositoryException( e.getMessage(), e );
336         }
337     }
338
339     public List<String> getMetadataFacets( String repositoryId, String facetId )
340         throws MetadataRepositoryException
341     {
342         List<String> facets = new ArrayList<String>();
343
344         try
345         {
346             // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
347             // are paths themselves
348             Node node = session.getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
349
350             // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
351             //   paths helpful?
352             recurse( facets, "", node );
353         }
354         catch ( PathNotFoundException e )
355         {
356             // ignored - the facet doesn't exist, so return the empty list
357         }
358         catch ( RepositoryException e )
359         {
360             throw new MetadataRepositoryException( e.getMessage(), e );
361         }
362         return facets;
363     }
364
365     private void recurse( List<String> facets, String prefix, Node node )
366         throws RepositoryException
367     {
368         for ( Node n : JcrUtils.getChildNodes( node ) )
369         {
370             String name = prefix + "/" + n.getName();
371             if ( n.hasNodes() )
372             {
373                 recurse( facets, name, n );
374             }
375             else
376             {
377                 // strip leading / first
378                 facets.add( name.substring( 1 ) );
379             }
380         }
381     }
382
383     public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
384         throws MetadataRepositoryException
385     {
386         MetadataFacet metadataFacet = null;
387         try
388         {
389             Node root = session.getRootNode();
390             Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
391
392             MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
393             if ( metadataFacetFactory != null )
394             {
395                 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
396                 Map<String, String> map = new HashMap<String, String>();
397                 for ( Property property : JcrUtils.getProperties( node ) )
398                 {
399                     String p = property.getName();
400                     if ( !p.startsWith( "jcr:" ) )
401                     {
402                         map.put( p, property.getString() );
403                     }
404                 }
405                 metadataFacet.fromProperties( map );
406             }
407         }
408         catch ( PathNotFoundException e )
409         {
410             // ignored - the facet doesn't exist, so return null
411         }
412         catch ( RepositoryException e )
413         {
414             throw new MetadataRepositoryException( e.getMessage(), e );
415         }
416         return metadataFacet;
417     }
418
419     public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
420         throws MetadataRepositoryException
421     {
422         try
423         {
424             Node repo = getOrAddRepositoryNode( repositoryId );
425             Node facets = JcrUtils.getOrAddNode( repo, "facets" );
426
427             String id = metadataFacet.getFacetId();
428             Node facetNode = JcrUtils.getOrAddNode( facets, id );
429
430             Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
431
432             for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
433             {
434                 node.setProperty( entry.getKey(), entry.getValue() );
435             }
436         }
437         catch ( RepositoryException e )
438         {
439             throw new MetadataRepositoryException( e.getMessage(), e );
440         }
441     }
442
443     public void removeMetadataFacets( String repositoryId, String facetId )
444         throws MetadataRepositoryException
445     {
446         try
447         {
448             Node root = session.getRootNode();
449             String path = getFacetPath( repositoryId, facetId );
450             if ( root.hasNode( path ) )
451             {
452                 root.getNode( path ).remove();
453             }
454         }
455         catch ( RepositoryException e )
456         {
457             throw new MetadataRepositoryException( e.getMessage(), e );
458         }
459     }
460
461     public void removeMetadataFacet( String repositoryId, String facetId, String name )
462         throws MetadataRepositoryException
463     {
464         try
465         {
466             Node root = session.getRootNode();
467             String path = getFacetPath( repositoryId, facetId, name );
468             if ( root.hasNode( path ) )
469             {
470                 Node node = root.getNode( path );
471                 do
472                 {
473                     // also remove empty container nodes
474                     Node parent = node.getParent();
475                     node.remove();
476                     node = parent;
477                 }
478                 while ( !node.hasNodes() );
479             }
480         }
481         catch ( RepositoryException e )
482         {
483             throw new MetadataRepositoryException( e.getMessage(), e );
484         }
485     }
486
487     public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
488         throws MetadataRepositoryException
489     {
490         List<ArtifactMetadata> artifacts;
491
492         String q = QUERY_ARTIFACTS;
493
494         String clause = " WHERE";
495         if ( startTime != null )
496         {
497             q += clause + " [whenGathered] >= $start";
498             clause = " AND";
499         }
500         if ( endTime != null )
501         {
502             q += clause + " [whenGathered] <= $end";
503         }
504
505         try
506         {
507             Query query = session.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
508             ValueFactory valueFactory = session.getValueFactory();
509             if ( startTime != null )
510             {
511                 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
512             }
513             if ( endTime != null )
514             {
515                 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
516             }
517             QueryResult result = query.execute();
518
519             artifacts = new ArrayList<ArtifactMetadata>();
520             for ( Node n : JcrUtils.getNodes( result ) )
521             {
522                 artifacts.add( getArtifactFromNode( repoId, n ) );
523             }
524         }
525         catch ( RepositoryException e )
526         {
527             throw new MetadataRepositoryException( e.getMessage(), e );
528         }
529         return artifacts;
530     }
531
532     public Collection<String> getRepositories()
533         throws MetadataRepositoryException
534     {
535         List<String> repositories;
536
537         try
538         {
539             Node root = session.getRootNode();
540             if ( root.hasNode( "repositories" ) )
541             {
542                 Node node = root.getNode( "repositories" );
543
544                 repositories = new ArrayList<String>();
545                 NodeIterator i = node.getNodes();
546                 while ( i.hasNext() )
547                 {
548                     Node n = i.nextNode();
549                     repositories.add( n.getName() );
550                 }
551             }
552             else
553             {
554                 repositories = Collections.emptyList();
555             }
556         }
557         catch ( RepositoryException e )
558         {
559             throw new MetadataRepositoryException( e.getMessage(), e );
560         }
561         return repositories;
562     }
563
564     public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
565         throws MetadataRepositoryException
566     {
567         List<ArtifactMetadata> artifacts;
568
569         String q = QUERY_ARTIFACTS + " WHERE [sha1] = $checksum OR [md5] = $checksum";
570
571         try
572         {
573             Query query = session.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
574             ValueFactory valueFactory = session.getValueFactory();
575             query.bindValue( "checksum", valueFactory.createValue( checksum ) );
576             QueryResult result = query.execute();
577
578             artifacts = new ArrayList<ArtifactMetadata>();
579             for ( Node n : JcrUtils.getNodes( result ) )
580             {
581                 artifacts.add( getArtifactFromNode( repositoryId, n ) );
582             }
583         }
584         catch ( RepositoryException e )
585         {
586             throw new MetadataRepositoryException( e.getMessage(), e );
587         }
588         return artifacts;
589     }
590
591     public void removeArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
592                                 String id )
593         throws MetadataRepositoryException
594     {
595         try
596         {
597             Node root = session.getRootNode();
598             String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
599             if ( root.hasNode( path ) )
600             {
601                 root.getNode( path ).remove();
602             }
603         }
604         catch ( RepositoryException e )
605         {
606             throw new MetadataRepositoryException( e.getMessage(), e );
607         }
608     }
609
610     public void removeRepository( String repositoryId )
611         throws MetadataRepositoryException
612     {
613         try
614         {
615             Node root = session.getRootNode();
616             String path = getRepositoryPath( repositoryId );
617             if ( root.hasNode( path ) )
618             {
619                 root.getNode( path ).remove();
620             }
621         }
622         catch ( RepositoryException e )
623         {
624             throw new MetadataRepositoryException( e.getMessage(), e );
625         }
626     }
627
628     public List<ArtifactMetadata> getArtifacts( String repositoryId )
629         throws MetadataRepositoryException
630     {
631         List<ArtifactMetadata> artifacts;
632
633         String q = QUERY_ARTIFACTS;
634
635         try
636         {
637             Query query = session.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
638             QueryResult result = query.execute();
639
640             artifacts = new ArrayList<ArtifactMetadata>();
641             for ( Node n : JcrUtils.getNodes( result ) )
642             {
643                 artifacts.add( getArtifactFromNode( repositoryId, n ) );
644             }
645         }
646         catch ( RepositoryException e )
647         {
648             throw new MetadataRepositoryException( e.getMessage(), e );
649         }
650         return artifacts;
651     }
652
653     public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
654         throws MetadataResolutionException
655     {
656         ProjectMetadata metadata = null;
657
658         try
659         {
660             Node root = session.getRootNode();
661
662             // basically just checking it exists
663             String path = getProjectPath( repositoryId, namespace, projectId );
664             if ( root.hasNode( path ) )
665             {
666                 metadata = new ProjectMetadata();
667                 metadata.setId( projectId );
668                 metadata.setNamespace( namespace );
669             }
670         }
671         catch ( RepositoryException e )
672         {
673             throw new MetadataResolutionException( e.getMessage(), e );
674         }
675
676         return metadata;
677     }
678
679     public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
680                                                      String projectVersion )
681         throws MetadataResolutionException
682     {
683         ProjectVersionMetadata versionMetadata;
684
685         try
686         {
687             Node root = session.getRootNode();
688
689             String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
690             if ( !root.hasNode( path ) )
691             {
692                 return null;
693             }
694
695             Node node = root.getNode( path );
696
697             versionMetadata = new ProjectVersionMetadata();
698             versionMetadata.setId( projectVersion );
699             versionMetadata.setName( getPropertyString( node, "name" ) );
700             versionMetadata.setDescription( getPropertyString( node, "description" ) );
701             versionMetadata.setUrl( getPropertyString( node, "url" ) );
702             versionMetadata.setIncomplete( node.hasProperty( "incomplete" ) && node.getProperty(
703                 "incomplete" ).getBoolean() );
704
705             // FIXME: decide how to treat these in the content repo
706             String scmConnection = getPropertyString( node, "scm.connection" );
707             String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
708             String scmUrl = getPropertyString( node, "scm.url" );
709             if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
710             {
711                 Scm scm = new Scm();
712                 scm.setConnection( scmConnection );
713                 scm.setDeveloperConnection( scmDeveloperConnection );
714                 scm.setUrl( scmUrl );
715                 versionMetadata.setScm( scm );
716             }
717
718             String ciSystem = getPropertyString( node, "ci.system" );
719             String ciUrl = getPropertyString( node, "ci.url" );
720             if ( ciSystem != null || ciUrl != null )
721             {
722                 CiManagement ci = new CiManagement();
723                 ci.setSystem( ciSystem );
724                 ci.setUrl( ciUrl );
725                 versionMetadata.setCiManagement( ci );
726             }
727
728             String issueSystem = getPropertyString( node, "issue.system" );
729             String issueUrl = getPropertyString( node, "issue.url" );
730             if ( issueSystem != null || issueUrl != null )
731             {
732                 IssueManagement issueManagement = new IssueManagement();
733                 issueManagement.setSystem( issueSystem );
734                 issueManagement.setUrl( issueUrl );
735                 versionMetadata.setIssueManagement( issueManagement );
736             }
737
738             String orgName = getPropertyString( node, "org.name" );
739             String orgUrl = getPropertyString( node, "org.url" );
740             if ( orgName != null || orgUrl != null )
741             {
742                 Organization org = new Organization();
743                 org.setName( orgName );
744                 org.setUrl( orgUrl );
745                 versionMetadata.setOrganization( org );
746             }
747
748             boolean done = false;
749             int i = 0;
750             while ( !done )
751             {
752                 String licenseName = getPropertyString( node, "license." + i + ".name" );
753                 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
754                 if ( licenseName != null || licenseUrl != null )
755                 {
756                     License license = new License();
757                     license.setName( licenseName );
758                     license.setUrl( licenseUrl );
759                     versionMetadata.addLicense( license );
760                 }
761                 else
762                 {
763                     done = true;
764                 }
765                 i++;
766             }
767
768             done = false;
769             i = 0;
770             while ( !done )
771             {
772                 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
773                 if ( mailingListName != null )
774                 {
775                     MailingList mailingList = new MailingList();
776                     mailingList.setName( mailingListName );
777                     mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
778                     String n = "mailingList." + i + ".otherArchives";
779                     if ( node.hasProperty( n ) )
780                     {
781                         mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
782                     }
783                     else
784                     {
785                         mailingList.setOtherArchives( Collections.<String>emptyList() );
786                     }
787                     mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
788                     mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
789                     mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
790                     versionMetadata.addMailingList( mailingList );
791                 }
792                 else
793                 {
794                     done = true;
795                 }
796                 i++;
797             }
798
799             done = false;
800             i = 0;
801             while ( !done )
802             {
803                 String dependencyArtifactId = getPropertyString( node, "dependency." + i + ".artifactId" );
804                 if ( dependencyArtifactId != null )
805                 {
806                     Dependency dependency = new Dependency();
807                     dependency.setArtifactId( dependencyArtifactId );
808                     dependency.setGroupId( getPropertyString( node, "dependency." + i + ".groupId" ) );
809                     dependency.setClassifier( getPropertyString( node, "dependency." + i + ".classifier" ) );
810                     dependency.setOptional( Boolean.valueOf( getPropertyString( node,
811                                                                                 "dependency." + i + ".optional" ) ) );
812                     dependency.setScope( getPropertyString( node, "dependency." + i + ".scope" ) );
813                     dependency.setSystemPath( getPropertyString( node, "dependency." + i + ".systemPath" ) );
814                     dependency.setType( getPropertyString( node, "dependency." + i + ".type" ) );
815                     dependency.setVersion( getPropertyString( node, "dependency." + i + ".version" ) );
816                     versionMetadata.addDependency( dependency );
817                 }
818                 else
819                 {
820                     done = true;
821                 }
822                 i++;
823             }
824
825             for ( Node n : JcrUtils.getChildNodes( node ) )
826             {
827                 if ( n.isNodeType( FACET_NODE_TYPE ) )
828                 {
829                     String name = n.getName();
830                     MetadataFacetFactory factory = metadataFacetFactories.get( name );
831                     if ( factory == null )
832                     {
833                         log.error( "Attempted to load unknown project version metadata facet: " + name );
834                     }
835                     else
836                     {
837                         MetadataFacet facet = factory.createMetadataFacet();
838                         Map<String, String> map = new HashMap<String, String>();
839                         for ( Property property : JcrUtils.getProperties( n ) )
840                         {
841                             String p = property.getName();
842                             if ( !p.startsWith( "jcr:" ) )
843                             {
844                                 map.put( p, property.getString() );
845                             }
846                         }
847                         facet.fromProperties( map );
848                         versionMetadata.addFacet( facet );
849                     }
850                 }
851             }
852         }
853         catch ( RepositoryException e )
854         {
855             throw new MetadataResolutionException( e.getMessage(), e );
856         }
857
858         return versionMetadata;
859     }
860
861     public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
862                                                    String projectVersion )
863         throws MetadataResolutionException
864     {
865         Set<String> versions = new LinkedHashSet<String>();
866
867         try
868         {
869             Node root = session.getRootNode();
870
871             Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
872
873             for ( Node n : JcrUtils.getChildNodes( node ) )
874             {
875                 versions.add( n.getProperty( "version" ).getString() );
876             }
877         }
878         catch ( PathNotFoundException e )
879         {
880             // ignore repo not found for now
881         }
882         catch ( RepositoryException e )
883         {
884             throw new MetadataResolutionException( e.getMessage(), e );
885         }
886
887         return versions;
888     }
889
890     public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
891                                                                      String projectId, String projectVersion )
892         throws MetadataResolutionException
893     {
894         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
895
896         try
897         {
898             Node root = session.getRootNode();
899
900             String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/references";
901             if ( root.hasNode( path ) )
902             {
903                 Node node = root.getNode( path );
904
905                 NodeIterator i = node.getNodes();
906                 while ( i.hasNext() )
907                 {
908                     Node ns = i.nextNode();
909
910                     NodeIterator j = ns.getNodes();
911
912                     while ( j.hasNext() )
913                     {
914                         Node project = j.nextNode();
915
916                         NodeIterator k = project.getNodes();
917
918                         while ( k.hasNext() )
919                         {
920                             Node version = k.nextNode();
921
922                             ProjectVersionReference ref = new ProjectVersionReference();
923                             ref.setNamespace( ns.getName() );
924                             ref.setProjectId( project.getName() );
925                             ref.setProjectVersion( version.getName() );
926                             String type = version.getProperty( "type" ).getString();
927                             ref.setReferenceType( ProjectVersionReference.ReferenceType.valueOf( type ) );
928                             references.add( ref );
929                         }
930                     }
931                 }
932             }
933         }
934         catch ( RepositoryException e )
935         {
936             throw new MetadataResolutionException( e.getMessage(), e );
937         }
938
939         return references;
940     }
941
942     public Collection<String> getRootNamespaces( String repositoryId )
943         throws MetadataResolutionException
944     {
945         return getNamespaces( repositoryId, null );
946     }
947
948     public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
949         throws MetadataResolutionException
950     {
951         String path = baseNamespace != null
952             ? getNamespacePath( repositoryId, baseNamespace )
953             : getRepositoryContentPath( repositoryId );
954
955         return getNodeNames( path );
956     }
957
958     public Collection<String> getProjects( String repositoryId, String namespace )
959         throws MetadataResolutionException
960     {
961         return getNodeNames( getNamespacePath( repositoryId, namespace ) );
962     }
963
964     public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
965         throws MetadataResolutionException
966     {
967         return getNodeNames( getProjectPath( repositoryId, namespace, projectId ) );
968     }
969
970     public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
971                                                       String projectVersion )
972         throws MetadataResolutionException
973     {
974         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
975
976         try
977         {
978             Node root = session.getRootNode();
979             String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
980
981             if ( root.hasNode( path ) )
982             {
983                 Node node = root.getNode( path );
984
985                 for ( Node n : JcrUtils.getChildNodes( node ) )
986                 {
987                     artifacts.add( getArtifactFromNode( repositoryId, n ) );
988                 }
989             }
990         }
991         catch ( RepositoryException e )
992         {
993             throw new MetadataResolutionException( e.getMessage(), e );
994         }
995
996         return artifacts;
997     }
998
999     public void save()
1000         throws MetadataRepositoryException
1001     {
1002         try
1003         {
1004             session.save();
1005         }
1006         catch ( RepositoryException e )
1007         {
1008             throw new MetadataRepositoryException( e.getMessage(), e );
1009         }
1010     }
1011
1012     public void revert()
1013         throws MetadataRepositoryException
1014     {
1015         try
1016         {
1017             session.refresh( false );
1018         }
1019         catch ( RepositoryException e )
1020         {
1021             throw new MetadataRepositoryException( e.getMessage(), e );
1022         }
1023     }
1024
1025     public void close()
1026     {
1027         session.logout();
1028     }
1029
1030     private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1031         throws RepositoryException
1032     {
1033         String id = artifactNode.getName();
1034
1035         ArtifactMetadata artifact = new ArtifactMetadata();
1036         artifact.setId( id );
1037         artifact.setRepositoryId( repositoryId );
1038
1039         Node projectVersionNode = artifactNode.getParent();
1040         Node projectNode = projectVersionNode.getParent();
1041         Node namespaceNode = projectNode.getParent();
1042
1043         artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1044         artifact.setProject( projectNode.getName() );
1045         artifact.setProjectVersion( projectVersionNode.getName() );
1046         artifact.setVersion( artifactNode.hasProperty( "version" )
1047                                  ? artifactNode.getProperty( "version" ).getString()
1048                                  : projectVersionNode.getName() );
1049
1050         if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1051         {
1052             artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1053         }
1054
1055         if ( artifactNode.hasProperty( "whenGathered" ) )
1056         {
1057             artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1058         }
1059
1060         if ( artifactNode.hasProperty( "size" ) )
1061         {
1062             artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1063         }
1064
1065         if ( artifactNode.hasProperty( "md5" ) )
1066         {
1067             artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1068         }
1069
1070         if ( artifactNode.hasProperty( "sha1" ) )
1071         {
1072             artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1073         }
1074
1075         for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1076         {
1077             if ( n.isNodeType( FACET_NODE_TYPE ) )
1078             {
1079                 String name = n.getName();
1080                 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1081                 if ( factory == null )
1082                 {
1083                     log.error( "Attempted to load unknown project version metadata facet: " + name );
1084                 }
1085                 else
1086                 {
1087                     MetadataFacet facet = factory.createMetadataFacet();
1088                     Map<String, String> map = new HashMap<String, String>();
1089                     for ( Property p : JcrUtils.getProperties( n ) )
1090                     {
1091                         String property = p.getName();
1092                         if ( !property.startsWith( "jcr:" ) )
1093                         {
1094                             map.put( property, p.getString() );
1095                         }
1096                     }
1097                     facet.fromProperties( map );
1098                     artifact.addFacet( facet );
1099                 }
1100             }
1101         }
1102         return artifact;
1103     }
1104
1105     private static String getPropertyString( Node node, String name )
1106         throws RepositoryException
1107     {
1108         return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1109     }
1110
1111     private Collection<String> getNodeNames( String path )
1112         throws MetadataResolutionException
1113     {
1114         List<String> names = new ArrayList<String>();
1115
1116         try
1117         {
1118             Node root = session.getRootNode();
1119
1120             Node repository = root.getNode( path );
1121
1122             NodeIterator nodes = repository.getNodes();
1123             while ( nodes.hasNext() )
1124             {
1125                 Node node = nodes.nextNode();
1126                 names.add( node.getName() );
1127             }
1128         }
1129         catch ( PathNotFoundException e )
1130         {
1131             // ignore repo not found for now
1132         }
1133         catch ( RepositoryException e )
1134         {
1135             throw new MetadataResolutionException( e.getMessage(), e );
1136         }
1137
1138         return names;
1139     }
1140
1141     private static String getRepositoryPath( String repositoryId )
1142     {
1143         return "repositories/" + repositoryId;
1144     }
1145
1146     private static String getRepositoryContentPath( String repositoryId )
1147     {
1148         return getRepositoryPath( repositoryId ) + "/content/";
1149     }
1150
1151     private static String getFacetPath( String repositoryId, String facetId )
1152     {
1153         return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1154     }
1155
1156     private static String getNamespacePath( String repositoryId, String namespace )
1157     {
1158         return getRepositoryContentPath( repositoryId ) + namespace.replace( '.', '/' );
1159     }
1160
1161     private static String getProjectPath( String repositoryId, String namespace, String projectId )
1162     {
1163         return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1164     }
1165
1166     private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1167                                                  String projectVersion )
1168     {
1169         return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1170     }
1171
1172     private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1173                                            String projectVersion, String id )
1174     {
1175         return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1176     }
1177
1178     private Node getOrAddNodeByPath( Node baseNode, String name )
1179         throws RepositoryException
1180     {
1181         Node node = baseNode;
1182         for ( String n : name.split( "/" ) )
1183         {
1184             node = JcrUtils.getOrAddNode( node, n );
1185         }
1186         return node;
1187     }
1188
1189     private static String getFacetPath( String repositoryId, String facetId, String name )
1190     {
1191         return getFacetPath( repositoryId, facetId ) + "/" + name;
1192     }
1193
1194     private Node getOrAddRepositoryNode( String repositoryId )
1195         throws RepositoryException
1196     {
1197         Node root = session.getRootNode();
1198         Node node = JcrUtils.getOrAddNode( root, "repositories" );
1199         node = JcrUtils.getOrAddNode( node, repositoryId );
1200         return node;
1201     }
1202
1203     private Node getOrAddRepositoryContentNode( String repositoryId )
1204         throws RepositoryException
1205     {
1206         Node node = getOrAddRepositoryNode( repositoryId );
1207         return JcrUtils.getOrAddNode( node, "content" );
1208     }
1209
1210     private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1211         throws RepositoryException
1212     {
1213         Node repo = getOrAddRepositoryContentNode( repositoryId );
1214         return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ) );
1215     }
1216
1217     private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1218         throws RepositoryException
1219     {
1220         Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1221         return JcrUtils.getOrAddNode( namespaceNode, projectId );
1222     }
1223
1224     private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1225                                              String projectVersion )
1226         throws RepositoryException
1227     {
1228         Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1229         return JcrUtils.getOrAddNode( projectNode, projectVersion );
1230     }
1231
1232     private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1233                                        String id )
1234         throws RepositoryException
1235     {
1236         Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1237         Node node = JcrUtils.getOrAddNode( versionNode, id );
1238         node.addMixin( ARTIFACT_NODE_TYPE );
1239         return node;
1240     }
1241
1242     private static Calendar createCalendar( Date time )
1243     {
1244         Calendar cal = Calendar.getInstance();
1245         cal.setTime( time );
1246         return cal;
1247     }
1248
1249     private String join( Collection<String> ids )
1250     {
1251         if ( ids != null && !ids.isEmpty() )
1252         {
1253             StringBuilder s = new StringBuilder();
1254             for ( String id : ids )
1255             {
1256                 s.append( id );
1257                 s.append( "," );
1258             }
1259             return s.substring( 0, s.length() - 1 );
1260         }
1261         return null;
1262     }
1263
1264     public Session getJcrSession()
1265     {
1266         return session;
1267     }
1268 }