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