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