]> source.dussan.org Git - archiva.git/blob
f7e4e8f88c2e5cdbd1da576a38841b6cfb5da70c
[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.commons.lang.StringUtils;
39 import org.apache.jackrabbit.commons.JcrUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import javax.jcr.NamespaceRegistry;
44 import javax.jcr.Node;
45 import javax.jcr.NodeIterator;
46 import javax.jcr.PathNotFoundException;
47 import javax.jcr.Property;
48 import javax.jcr.Repository;
49 import javax.jcr.RepositoryException;
50 import javax.jcr.Session;
51 import javax.jcr.SimpleCredentials;
52 import javax.jcr.ValueFactory;
53 import javax.jcr.Workspace;
54 import javax.jcr.nodetype.NodeTypeManager;
55 import javax.jcr.nodetype.NodeTypeTemplate;
56 import javax.jcr.query.Query;
57 import javax.jcr.query.QueryResult;
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.Calendar;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.Date;
64 import java.util.HashMap;
65 import java.util.Iterator;
66 import java.util.LinkedHashSet;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Set;
70
71 /**
72  * TODO below: revise storage format for project version metadata
73  * TODO revise reference storage
74  */
75 public class JcrMetadataRepository
76     implements MetadataRepository
77 {
78
79     private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
80
81     static final String NAMESPACE_NODE_TYPE = "archiva:namespace";
82
83     static final String PROJECT_NODE_TYPE = "archiva:project";
84
85     static final String PROJECT_VERSION_NODE_TYPE = "archiva:projectVersion";
86
87     static final String ARTIFACT_NODE_TYPE = "archiva:artifact";
88
89     static final String FACET_NODE_TYPE = "archiva:facet";
90
91     private static final String DEPENDENCY_NODE_TYPE = "archiva:dependency";
92
93     private final Map<String, MetadataFacetFactory> metadataFacetFactories;
94
95     private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
96
97     private Repository repository;
98
99     private Session jcrSession;
100
101     public JcrMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories, Repository repository )
102         throws RepositoryException
103     {
104         this.metadataFacetFactories = metadataFacetFactories;
105         this.repository = repository;
106     }
107
108
109     static void initialize( Session session )
110         throws RepositoryException
111     {
112
113         // TODO: consider using namespaces for facets instead of the current approach:
114         // (if used, check if actually called by normal injection)
115 //        for ( String facetId : metadataFacetFactories.keySet() )
116 //        {
117 //            session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
118 //        }
119
120         Workspace workspace = session.getWorkspace();
121         NamespaceRegistry registry = workspace.getNamespaceRegistry();
122
123         if ( !Arrays.asList( registry.getPrefixes() ).contains( "archiva" ) )
124         {
125             registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
126         }
127
128         NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
129         registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.NAMESPACE_NODE_TYPE );
130         registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_NODE_TYPE );
131         registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_VERSION_NODE_TYPE );
132         registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.ARTIFACT_NODE_TYPE );
133         registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.FACET_NODE_TYPE );
134         registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.DEPENDENCY_NODE_TYPE );
135
136     }
137
138     private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
139         throws RepositoryException
140     {
141         NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
142         nodeType.setMixin( true );
143         nodeType.setName( name );
144
145         // for now just don't re-create - but in future if we change the definition, make sure to remove first as an
146         // upgrade path
147         if ( !nodeTypeManager.hasNodeType( name ) )
148         {
149             nodeTypeManager.registerNodeType( nodeType, false );
150         }
151     }
152
153     public void updateProject( String repositoryId, ProjectMetadata project )
154         throws MetadataRepositoryException
155     {
156         updateProject( repositoryId, project.getNamespace(), project.getId() );
157     }
158
159     private void updateProject( String repositoryId, String namespace, String projectId )
160         throws MetadataRepositoryException
161     {
162         updateNamespace( repositoryId, namespace );
163
164         try
165         {
166             getOrAddProjectNode( repositoryId, namespace, projectId );
167         }
168         catch ( RepositoryException e )
169         {
170             throw new MetadataRepositoryException( e.getMessage(), e );
171         }
172     }
173
174     public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
175                                 ArtifactMetadata artifactMeta )
176         throws MetadataRepositoryException
177     {
178         updateNamespace( repositoryId, namespace );
179
180         try
181         {
182             Node node =
183                 getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion, artifactMeta.getId() );
184
185             Calendar cal = Calendar.getInstance();
186             cal.setTime( artifactMeta.getFileLastModified() );
187             node.setProperty( JCR_LAST_MODIFIED, cal );
188
189             cal = Calendar.getInstance();
190             cal.setTime( artifactMeta.getWhenGathered() );
191             node.setProperty( "whenGathered", cal );
192
193             node.setProperty( "size", artifactMeta.getSize() );
194             node.setProperty( "md5", artifactMeta.getMd5() );
195             node.setProperty( "sha1", artifactMeta.getSha1() );
196
197             node.setProperty( "version", artifactMeta.getVersion() );
198
199             // iterate over available facets to update/add/remove from the artifactMetadata
200             for ( String facetId : metadataFacetFactories.keySet() )
201             {
202                 MetadataFacet metadataFacet = artifactMeta.getFacet( facetId );
203                 if ( metadataFacet == null )
204                 {
205                     continue;
206                 }
207                 if ( node.hasNode( facetId ) )
208                 {
209                     node.getNode( facetId ).remove();
210                 }
211                 if ( metadataFacet != null )
212                 {
213                     // recreate, to ensure properties are removed
214                     Node n = node.addNode( facetId );
215                     n.addMixin( FACET_NODE_TYPE );
216
217                     for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
218                     {
219                         n.setProperty( entry.getKey(), entry.getValue() );
220                     }
221                 }
222             }
223         }
224         catch ( RepositoryException e )
225         {
226             throw new MetadataRepositoryException( e.getMessage(), e );
227         }
228     }
229
230     public void updateProjectVersion( String repositoryId, String namespace, String projectId,
231                                       ProjectVersionMetadata versionMetadata )
232         throws MetadataRepositoryException
233     {
234         updateProject( repositoryId, namespace, projectId );
235
236         try
237         {
238             Node versionNode =
239                 getOrAddProjectVersionNode( repositoryId, namespace, projectId, versionMetadata.getId() );
240
241             versionNode.setProperty( "name", versionMetadata.getName() );
242             versionNode.setProperty( "description", versionMetadata.getDescription() );
243             versionNode.setProperty( "url", versionMetadata.getUrl() );
244             versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
245
246             // FIXME: decide how to treat these in the content repo
247             if ( versionMetadata.getScm() != null )
248             {
249                 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
250                 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
251                 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
252             }
253             if ( versionMetadata.getCiManagement() != null )
254             {
255                 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
256                 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
257             }
258             if ( versionMetadata.getIssueManagement() != null )
259             {
260                 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
261                 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
262             }
263             if ( versionMetadata.getOrganization() != null )
264             {
265                 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
266                 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
267             }
268             int i = 0;
269             for ( License license : versionMetadata.getLicenses() )
270             {
271                 versionNode.setProperty( "license." + i + ".name", license.getName() );
272                 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
273                 i++;
274             }
275             i = 0;
276             for ( MailingList mailingList : versionMetadata.getMailingLists() )
277             {
278                 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
279                 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
280                 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
281                 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
282                 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
283                 versionNode.setProperty( "mailingList." + i + ".otherArchives",
284                                          join( mailingList.getOtherArchives() ) );
285                 i++;
286             }
287
288             if ( !versionMetadata.getDependencies().isEmpty() )
289             {
290                 Node dependenciesNode = JcrUtils.getOrAddNode( versionNode, "dependencies" );
291
292                 for ( Dependency dependency : versionMetadata.getDependencies() )
293                 {
294                     // Note that we deliberately don't alter the namespace path - not enough dependencies for
295                     // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each
296                     // component of the ID as that creates extra depth and causes a great cost in space and memory
297
298                     // FIXME: change group ID to namespace
299                     // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now.
300                     //        This won't support types where the extension doesn't match the type.
301                     //        (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM)
302                     String id =
303                         dependency.getGroupId() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion();
304                     if ( dependency.getClassifier() != null )
305                     {
306                         id += "-" + dependency.getClassifier();
307                     }
308                     id += "." + dependency.getType();
309
310                     Node n = JcrUtils.getOrAddNode( dependenciesNode, id );
311                     n.addMixin( DEPENDENCY_NODE_TYPE );
312
313                     // FIXME: remove temp code just to make it keep working
314                     n.setProperty( "groupId", dependency.getGroupId() );
315                     n.setProperty( "artifactId", dependency.getArtifactId() );
316                     n.setProperty( "version", dependency.getVersion() );
317                     n.setProperty( "type", dependency.getType() );
318                     n.setProperty( "classifier", dependency.getClassifier() );
319                     n.setProperty( "scope", dependency.getScope() );
320                     n.setProperty( "systemPath", dependency.getSystemPath() );
321                     n.setProperty( "optional", dependency.isOptional() );
322
323                     // node has no native content at this time, just facets
324                     // no need to list a type as it's implied by the path. Parents are Maven specific.
325
326                     // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet
327                     //        (should also have been added to the Dependency)
328
329                     // TODO: add a property that is a weak reference to the originating artifact, creating it if
330                     //       necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an
331                     //       artifact, which gives a different meaning to "incomplete" which is a known local project
332                     //       that doesn't have metadata yet but has artifacts). (Though we may want to give it the
333                     //       artifact mixin and another property to identify all non-local artifacts for the closure
334                     //       reports)
335                 }
336             }
337
338             for ( MetadataFacet facet : versionMetadata.getFacetList() )
339             {
340                 // recreate, to ensure properties are removed
341                 if ( versionNode.hasNode( facet.getFacetId() ) )
342                 {
343                     versionNode.getNode( facet.getFacetId() ).remove();
344                 }
345                 Node n = versionNode.addNode( facet.getFacetId() );
346                 n.addMixin( FACET_NODE_TYPE );
347
348                 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
349                 {
350                     n.setProperty( entry.getKey(), entry.getValue() );
351                 }
352             }
353         }
354         catch ( RepositoryException e )
355         {
356             throw new MetadataRepositoryException( e.getMessage(), e );
357         }
358     }
359
360     public void updateNamespace( String repositoryId, String namespace )
361         throws MetadataRepositoryException
362     {
363         try
364         {
365             Node node = getOrAddNamespaceNode( repositoryId, namespace );
366             node.setProperty( "namespace", namespace );
367         }
368         catch ( RepositoryException e )
369         {
370             throw new MetadataRepositoryException( e.getMessage(), e );
371         }
372     }
373
374     public void removeProject( String repositoryId, String namespace, String projectId )
375         throws MetadataRepositoryException
376     {
377         try
378         {
379             Node root = getJcrSession().getRootNode();
380             String namespacePath = getNamespacePath( repositoryId, namespace );
381
382             if ( root.hasNode( namespacePath ) )
383             {
384                 Iterator<Node> nodeIterator = JcrUtils.getChildNodes( root.getNode( namespacePath ) ).iterator();
385                 while ( nodeIterator.hasNext() )
386                 {
387                     Node node = nodeIterator.next();
388                     if ( node.isNodeType( PROJECT_NODE_TYPE ) && projectId.equals( node.getName() ) )
389                     {
390                         node.remove();
391                     }
392                 }
393
394             }
395         }
396         catch ( RepositoryException e )
397         {
398             throw new MetadataRepositoryException( e.getMessage(), e );
399         }
400
401     }
402
403
404     public boolean hasMetadataFacet( String repositoryId, String facetId )
405         throws MetadataRepositoryException
406     {
407         try
408         {
409             Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
410             return node.getNodes().hasNext();
411         }
412         catch ( PathNotFoundException e )
413         {
414             // ignored - the facet doesn't exist, so return false
415             return false;
416         }
417         catch ( RepositoryException e )
418         {
419             throw new MetadataRepositoryException( e.getMessage(), e );
420         }
421     }
422
423     public List<String> getMetadataFacets( String repositoryId, String facetId )
424         throws MetadataRepositoryException
425     {
426         List<String> facets = new ArrayList<String>();
427
428         try
429         {
430             // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
431             // are paths themselves
432             Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
433
434             // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
435             //   paths helpful?
436             recurse( facets, "", node );
437         }
438         catch ( PathNotFoundException e )
439         {
440             // ignored - the facet doesn't exist, so return the empty list
441         }
442         catch ( RepositoryException e )
443         {
444             throw new MetadataRepositoryException( e.getMessage(), e );
445         }
446         return facets;
447     }
448
449     private void recurse( List<String> facets, String prefix, Node node )
450         throws RepositoryException
451     {
452         for ( Node n : JcrUtils.getChildNodes( node ) )
453         {
454             String name = prefix + "/" + n.getName();
455             if ( n.hasNodes() )
456             {
457                 recurse( facets, name, n );
458             }
459             else
460             {
461                 // strip leading / first
462                 facets.add( name.substring( 1 ) );
463             }
464         }
465     }
466
467     public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
468         throws MetadataRepositoryException
469     {
470         MetadataFacet metadataFacet = null;
471         try
472         {
473             Node root = getJcrSession().getRootNode();
474             Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
475
476             if ( metadataFacetFactories == null )
477             {
478                 return metadataFacet;
479             }
480
481             MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
482             if ( metadataFacetFactory != null )
483             {
484                 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
485                 Map<String, String> map = new HashMap<String, String>();
486                 for ( Property property : JcrUtils.getProperties( node ) )
487                 {
488                     String p = property.getName();
489                     if ( !p.startsWith( "jcr:" ) )
490                     {
491                         map.put( p, property.getString() );
492                     }
493                 }
494                 metadataFacet.fromProperties( map );
495             }
496         }
497         catch ( PathNotFoundException e )
498         {
499             // ignored - the facet doesn't exist, so return null
500         }
501         catch ( RepositoryException e )
502         {
503             throw new MetadataRepositoryException( e.getMessage(), e );
504         }
505         return metadataFacet;
506     }
507
508     public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
509         throws MetadataRepositoryException
510     {
511         try
512         {
513             Node repo = getOrAddRepositoryNode( repositoryId );
514             Node facets = JcrUtils.getOrAddNode( repo, "facets" );
515
516             String id = metadataFacet.getFacetId();
517             Node facetNode = JcrUtils.getOrAddNode( facets, id );
518
519             Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
520
521             for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
522             {
523                 node.setProperty( entry.getKey(), entry.getValue() );
524             }
525         }
526         catch ( RepositoryException e )
527         {
528             throw new MetadataRepositoryException( e.getMessage(), e );
529         }
530     }
531
532     public void removeNamespace( String repositoryId, String projectId )
533         throws MetadataRepositoryException
534     {
535         try
536         {
537             Node root = getJcrSession().getRootNode();
538             String path = getNamespacePath( repositoryId, projectId );
539             if ( root.hasNode( path ) )
540             {
541                 Node node = root.getNode( path );
542                 if ( node.isNodeType( NAMESPACE_NODE_TYPE ) )
543                 {
544                     node.remove();
545                 }
546             }
547         }
548         catch ( RepositoryException e )
549         {
550             throw new MetadataRepositoryException( e.getMessage(), e );
551         }
552     }
553
554     public void removeMetadataFacets( String repositoryId, String facetId )
555         throws MetadataRepositoryException
556     {
557         try
558         {
559             Node root = getJcrSession().getRootNode();
560             String path = getFacetPath( repositoryId, facetId );
561             if ( root.hasNode( path ) )
562             {
563                 root.getNode( path ).remove();
564             }
565         }
566         catch ( RepositoryException e )
567         {
568             throw new MetadataRepositoryException( e.getMessage(), e );
569         }
570     }
571
572     public void removeMetadataFacet( String repositoryId, String facetId, String name )
573         throws MetadataRepositoryException
574     {
575         try
576         {
577             Node root = getJcrSession().getRootNode();
578             String path = getFacetPath( repositoryId, facetId, name );
579             if ( root.hasNode( path ) )
580             {
581                 Node node = root.getNode( path );
582                 do
583                 {
584                     // also remove empty container nodes
585                     Node parent = node.getParent();
586                     node.remove();
587                     node = parent;
588                 }
589                 while ( !node.hasNodes() );
590             }
591         }
592         catch ( RepositoryException e )
593         {
594             throw new MetadataRepositoryException( e.getMessage(), e );
595         }
596     }
597
598     public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
599         throws MetadataRepositoryException
600     {
601         List<ArtifactMetadata> artifacts;
602
603         String q = getArtifactQuery( repoId );
604
605         if ( startTime != null )
606         {
607             q += " AND [whenGathered] >= $start";
608         }
609         if ( endTime != null )
610         {
611             q += " AND [whenGathered] <= $end";
612         }
613
614         try
615         {
616             Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
617             ValueFactory valueFactory = getJcrSession().getValueFactory();
618             if ( startTime != null )
619             {
620                 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
621             }
622             if ( endTime != null )
623             {
624                 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
625             }
626             QueryResult result = query.execute();
627
628             artifacts = new ArrayList<ArtifactMetadata>();
629             for ( Node n : JcrUtils.getNodes( result ) )
630             {
631                 artifacts.add( getArtifactFromNode( repoId, n ) );
632             }
633         }
634         catch ( RepositoryException e )
635         {
636             throw new MetadataRepositoryException( e.getMessage(), e );
637         }
638         return artifacts;
639     }
640
641     public Collection<String> getRepositories()
642         throws MetadataRepositoryException
643     {
644         List<String> repositories;
645
646         try
647         {
648             Node root = getJcrSession().getRootNode();
649             if ( root.hasNode( "repositories" ) )
650             {
651                 Node node = root.getNode( "repositories" );
652
653                 repositories = new ArrayList<String>();
654                 NodeIterator i = node.getNodes();
655                 while ( i.hasNext() )
656                 {
657                     Node n = i.nextNode();
658                     repositories.add( n.getName() );
659                 }
660             }
661             else
662             {
663                 repositories = Collections.emptyList();
664             }
665         }
666         catch ( RepositoryException e )
667         {
668             throw new MetadataRepositoryException( e.getMessage(), e );
669         }
670         return repositories;
671     }
672
673     public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
674         throws MetadataRepositoryException
675     {
676         List<ArtifactMetadata> artifacts;
677
678         String q = getArtifactQuery( repositoryId ) + " AND ([sha1] = $checksum OR [md5] = $checksum)";
679
680         try
681         {
682             Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
683             ValueFactory valueFactory = getJcrSession().getValueFactory();
684             query.bindValue( "checksum", valueFactory.createValue( checksum ) );
685             QueryResult result = query.execute();
686
687             artifacts = new ArrayList<ArtifactMetadata>();
688             for ( Node n : JcrUtils.getNodes( result ) )
689             {
690                 artifacts.add( getArtifactFromNode( repositoryId, n ) );
691             }
692         }
693         catch ( RepositoryException e )
694         {
695             throw new MetadataRepositoryException( e.getMessage(), e );
696         }
697         return artifacts;
698     }
699
700
701     public void removeRepository( String repositoryId )
702         throws MetadataRepositoryException
703     {
704         try
705         {
706             Node root = getJcrSession().getRootNode();
707             String path = getRepositoryPath( repositoryId );
708             if ( root.hasNode( path ) )
709             {
710                 root.getNode( path ).remove();
711             }
712         }
713         catch ( RepositoryException e )
714         {
715             throw new MetadataRepositoryException( e.getMessage(), e );
716         }
717     }
718
719     public List<ArtifactMetadata> getArtifacts( String repositoryId )
720         throws MetadataRepositoryException
721     {
722         List<ArtifactMetadata> artifacts;
723
724         String q = getArtifactQuery( repositoryId );
725
726         try
727         {
728             Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
729             QueryResult result = query.execute();
730
731             artifacts = new ArrayList<ArtifactMetadata>();
732             for ( Node n : JcrUtils.getNodes( result ) )
733             {
734                 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
735                 {
736                     artifacts.add( getArtifactFromNode( repositoryId, n ) );
737                 }
738             }
739         }
740         catch ( RepositoryException e )
741         {
742             throw new MetadataRepositoryException( e.getMessage(), e );
743         }
744         return artifacts;
745     }
746
747     private static String getArtifactQuery( String repositoryId )
748     {
749         return "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/" +
750             getRepositoryContentPath( repositoryId ) + "')";
751     }
752
753     public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
754         throws MetadataResolutionException
755     {
756         ProjectMetadata metadata = null;
757
758         try
759         {
760             Node root = getJcrSession().getRootNode();
761
762             // basically just checking it exists
763             String path = getProjectPath( repositoryId, namespace, projectId );
764             if ( root.hasNode( path ) )
765             {
766                 metadata = new ProjectMetadata();
767                 metadata.setId( projectId );
768                 metadata.setNamespace( namespace );
769             }
770         }
771         catch ( RepositoryException e )
772         {
773             throw new MetadataResolutionException( e.getMessage(), e );
774         }
775
776         return metadata;
777     }
778
779     public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
780                                                      String projectVersion )
781         throws MetadataResolutionException
782     {
783         ProjectVersionMetadata versionMetadata;
784
785         try
786         {
787             Node root = getJcrSession().getRootNode();
788
789             String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
790             if ( !root.hasNode( path ) )
791             {
792                 return null;
793             }
794
795             Node node = root.getNode( path );
796
797             versionMetadata = new ProjectVersionMetadata();
798             versionMetadata.setId( projectVersion );
799             versionMetadata.setName( getPropertyString( node, "name" ) );
800             versionMetadata.setDescription( getPropertyString( node, "description" ) );
801             versionMetadata.setUrl( getPropertyString( node, "url" ) );
802             versionMetadata.setIncomplete(
803                 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
804
805             // FIXME: decide how to treat these in the content repo
806             String scmConnection = getPropertyString( node, "scm.connection" );
807             String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
808             String scmUrl = getPropertyString( node, "scm.url" );
809             if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
810             {
811                 Scm scm = new Scm();
812                 scm.setConnection( scmConnection );
813                 scm.setDeveloperConnection( scmDeveloperConnection );
814                 scm.setUrl( scmUrl );
815                 versionMetadata.setScm( scm );
816             }
817
818             String ciSystem = getPropertyString( node, "ci.system" );
819             String ciUrl = getPropertyString( node, "ci.url" );
820             if ( ciSystem != null || ciUrl != null )
821             {
822                 CiManagement ci = new CiManagement();
823                 ci.setSystem( ciSystem );
824                 ci.setUrl( ciUrl );
825                 versionMetadata.setCiManagement( ci );
826             }
827
828             String issueSystem = getPropertyString( node, "issue.system" );
829             String issueUrl = getPropertyString( node, "issue.url" );
830             if ( issueSystem != null || issueUrl != null )
831             {
832                 IssueManagement issueManagement = new IssueManagement();
833                 issueManagement.setSystem( issueSystem );
834                 issueManagement.setUrl( issueUrl );
835                 versionMetadata.setIssueManagement( issueManagement );
836             }
837
838             String orgName = getPropertyString( node, "org.name" );
839             String orgUrl = getPropertyString( node, "org.url" );
840             if ( orgName != null || orgUrl != null )
841             {
842                 Organization org = new Organization();
843                 org.setName( orgName );
844                 org.setUrl( orgUrl );
845                 versionMetadata.setOrganization( org );
846             }
847
848             boolean done = false;
849             int i = 0;
850             while ( !done )
851             {
852                 String licenseName = getPropertyString( node, "license." + i + ".name" );
853                 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
854                 if ( licenseName != null || licenseUrl != null )
855                 {
856                     License license = new License();
857                     license.setName( licenseName );
858                     license.setUrl( licenseUrl );
859                     versionMetadata.addLicense( license );
860                 }
861                 else
862                 {
863                     done = true;
864                 }
865                 i++;
866             }
867
868             done = false;
869             i = 0;
870             while ( !done )
871             {
872                 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
873                 if ( mailingListName != null )
874                 {
875                     MailingList mailingList = new MailingList();
876                     mailingList.setName( mailingListName );
877                     mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
878                     String n = "mailingList." + i + ".otherArchives";
879                     if ( node.hasProperty( n ) )
880                     {
881                         mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
882                     }
883                     else
884                     {
885                         mailingList.setOtherArchives( Collections.<String>emptyList() );
886                     }
887                     mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
888                     mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
889                     mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
890                     versionMetadata.addMailingList( mailingList );
891                 }
892                 else
893                 {
894                     done = true;
895                 }
896                 i++;
897             }
898
899             if ( node.hasNode( "dependencies" ) )
900             {
901                 Node dependenciesNode = node.getNode( "dependencies" );
902                 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
903                 {
904                     if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
905                     {
906                         Dependency dependency = new Dependency();
907                         // FIXME: correct these properties
908                         dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
909                         dependency.setGroupId( getPropertyString( n, "groupId" ) );
910                         dependency.setClassifier( getPropertyString( n, "classifier" ) );
911                         dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
912                         dependency.setScope( getPropertyString( n, "scope" ) );
913                         dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
914                         dependency.setType( getPropertyString( n, "type" ) );
915                         dependency.setVersion( getPropertyString( n, "version" ) );
916                         versionMetadata.addDependency( dependency );
917                     }
918                 }
919             }
920
921             for ( Node n : JcrUtils.getChildNodes( node ) )
922             {
923                 if ( n.isNodeType( FACET_NODE_TYPE ) )
924                 {
925                     String name = n.getName();
926                     MetadataFacetFactory factory = metadataFacetFactories.get( name );
927                     if ( factory == null )
928                     {
929                         log.error( "Attempted to load unknown project version metadata facet: {}", name );
930                     }
931                     else
932                     {
933                         MetadataFacet facet = factory.createMetadataFacet();
934                         Map<String, String> map = new HashMap<String, String>();
935                         for ( Property property : JcrUtils.getProperties( n ) )
936                         {
937                             String p = property.getName();
938                             if ( !p.startsWith( "jcr:" ) )
939                             {
940                                 map.put( p, property.getString() );
941                             }
942                         }
943                         facet.fromProperties( map );
944                         versionMetadata.addFacet( facet );
945                     }
946                 }
947             }
948         }
949         catch ( RepositoryException e )
950         {
951             throw new MetadataResolutionException( e.getMessage(), e );
952         }
953
954         return versionMetadata;
955     }
956
957     public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
958                                                    String projectVersion )
959         throws MetadataResolutionException
960     {
961         Set<String> versions = new LinkedHashSet<String>();
962
963         try
964         {
965             Node root = getJcrSession().getRootNode();
966
967             Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
968
969             for ( Node n : JcrUtils.getChildNodes( node ) )
970             {
971                 versions.add( n.getProperty( "version" ).getString() );
972             }
973         }
974         catch ( PathNotFoundException e )
975         {
976             // ignore repo not found for now
977         }
978         catch ( RepositoryException e )
979         {
980             throw new MetadataResolutionException( e.getMessage(), e );
981         }
982
983         return versions;
984     }
985
986     public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
987                                                                      String projectId, String projectVersion )
988         throws MetadataResolutionException
989     {
990         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
991
992         // TODO: bind variables instead
993         String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId +
994             "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
995         if ( projectVersion != null )
996         {
997             q += " AND [version]='" + projectVersion + "'";
998         }
999         try
1000         {
1001             Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
1002             QueryResult result = query.execute();
1003
1004             for ( Node n : JcrUtils.getNodes( result ) )
1005             {
1006                 n = n.getParent(); // dependencies grouping element
1007
1008                 n = n.getParent(); // project version
1009                 String usedByProjectVersion = n.getName();
1010
1011                 n = n.getParent(); // project
1012                 String usedByProject = n.getName();
1013
1014                 n = n.getParent(); // namespace
1015                 String usedByNamespace = n.getProperty( "namespace" ).getString();
1016
1017                 ProjectVersionReference ref = new ProjectVersionReference();
1018                 ref.setNamespace( usedByNamespace );
1019                 ref.setProjectId( usedByProject );
1020                 ref.setProjectVersion( usedByProjectVersion );
1021                 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
1022                 references.add( ref );
1023             }
1024         }
1025         catch ( RepositoryException e )
1026         {
1027             throw new MetadataResolutionException( e.getMessage(), e );
1028         }
1029
1030         return references;
1031     }
1032
1033     public Collection<String> getRootNamespaces( String repositoryId )
1034         throws MetadataResolutionException
1035     {
1036         return getNamespaces( repositoryId, null );
1037     }
1038
1039     public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
1040         throws MetadataResolutionException
1041     {
1042         String path = baseNamespace != null
1043             ? getNamespacePath( repositoryId, baseNamespace )
1044             : getRepositoryContentPath( repositoryId );
1045
1046         return getNodeNames( path, NAMESPACE_NODE_TYPE );
1047     }
1048
1049     public Collection<String> getProjects( String repositoryId, String namespace )
1050         throws MetadataResolutionException
1051     {
1052         return getNodeNames( getNamespacePath( repositoryId, namespace ), PROJECT_NODE_TYPE );
1053     }
1054
1055     public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
1056         throws MetadataResolutionException
1057     {
1058         return getNodeNames( getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
1059     }
1060
1061     public void removeArtifact( ArtifactMetadata artifactMetadata, String baseVersion )
1062         throws MetadataRepositoryException
1063     {
1064
1065         String repositoryId = artifactMetadata.getRepositoryId();
1066
1067         try
1068         {
1069             Node root = getJcrSession().getRootNode();
1070             String path =
1071                 getProjectVersionPath( repositoryId, artifactMetadata.getNamespace(), artifactMetadata.getProject(),
1072                                        baseVersion );
1073
1074             if ( root.hasNode( path ) )
1075             {
1076                 Node node = root.getNode( path );
1077
1078                 for ( Node n : JcrUtils.getChildNodes( node ) )
1079                 {
1080                     if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1081                     {
1082                         if ( n.hasProperty( "version" ) )
1083                         {
1084                             String version = n.getProperty( "version" ).getString();
1085                             if ( StringUtils.equals( version, artifactMetadata.getVersion() ) )
1086                             {
1087                                 n.remove();
1088                             }
1089                         }
1090
1091                     }
1092                 }
1093             }
1094         }
1095         catch ( RepositoryException e )
1096         {
1097             throw new MetadataRepositoryException( e.getMessage(), e );
1098         }
1099
1100
1101     }
1102
1103
1104     public void removeProjectVersion( String repoId, String namespace, String projectId, String projectVersion )
1105         throws MetadataRepositoryException
1106     {
1107         try
1108         {
1109
1110             String path = getProjectPath( repoId, namespace, projectId );
1111             Node root = getJcrSession().getRootNode();
1112
1113             Node nodeAtPath = root.getNode( path );
1114
1115             for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1116             {
1117                 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( projectVersion,
1118                                                                                          node.getName() ) )
1119                 {
1120                     node.remove();
1121                 }
1122             }
1123         }
1124         catch ( RepositoryException e )
1125         {
1126             throw new MetadataRepositoryException( e.getMessage(), e );
1127         }
1128     }
1129
1130     public void removeArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
1131                                 String id )
1132         throws MetadataRepositoryException
1133     {
1134         try
1135         {
1136             Node root = getJcrSession().getRootNode();
1137             String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
1138             if ( root.hasNode( path ) )
1139             {
1140                 root.getNode( path ).remove();
1141             }
1142
1143             // remove version
1144
1145             path = getProjectPath( repositoryId, namespace, projectId );
1146
1147             Node nodeAtPath = root.getNode( path );
1148
1149             for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1150             {
1151                 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( node.getName(),
1152                                                                                          projectVersion ) )
1153                 {
1154                     node.remove();
1155                 }
1156             }
1157         }
1158         catch ( RepositoryException e )
1159         {
1160             throw new MetadataRepositoryException( e.getMessage(), e );
1161         }
1162     }
1163
1164     public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion,
1165                                 MetadataFacet metadataFacet )
1166         throws MetadataRepositoryException
1167     {
1168         try
1169         {
1170             Node root = getJcrSession().getRootNode();
1171             String path = getProjectVersionPath( repositoryId, namespace, project, projectVersion );
1172
1173             if ( root.hasNode( path ) )
1174             {
1175                 Node node = root.getNode( path );
1176
1177                 for ( Node n : JcrUtils.getChildNodes( node ) )
1178                 {
1179                     if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1180                     {
1181                         ArtifactMetadata artifactMetadata = getArtifactFromNode( repositoryId, n );
1182                         log.debug( "artifactMetadata: {}", artifactMetadata );
1183                         MetadataFacet metadataFacetToRemove = artifactMetadata.getFacet( metadataFacet.getFacetId() );
1184                         if ( metadataFacetToRemove != null && metadataFacet.equals( metadataFacetToRemove ) )
1185                         {
1186                             n.remove();
1187                         }
1188                     }
1189                 }
1190             }
1191         }
1192         catch ( RepositoryException e )
1193         {
1194             throw new MetadataRepositoryException( e.getMessage(), e );
1195         }
1196     }
1197
1198     public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
1199                                                       String projectVersion )
1200         throws MetadataResolutionException
1201     {
1202         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1203
1204         try
1205         {
1206             Node root = getJcrSession().getRootNode();
1207             String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1208
1209             if ( root.hasNode( path ) )
1210             {
1211                 Node node = root.getNode( path );
1212
1213                 for ( Node n : JcrUtils.getChildNodes( node ) )
1214                 {
1215                     if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1216                     {
1217                         artifacts.add( getArtifactFromNode( repositoryId, n ) );
1218                     }
1219                 }
1220             }
1221         }
1222         catch ( RepositoryException e )
1223         {
1224             throw new MetadataResolutionException( e.getMessage(), e );
1225         }
1226
1227         return artifacts;
1228     }
1229
1230     public void save()
1231     {
1232         try
1233         {
1234             getJcrSession().save();
1235         }
1236         catch ( RepositoryException e )
1237         {
1238             throw new RuntimeException( e.getMessage(), e );
1239         }
1240     }
1241
1242     public void revert()
1243     {
1244         try
1245         {
1246             getJcrSession().refresh( false );
1247         }
1248         catch ( RepositoryException e )
1249         {
1250             throw new RuntimeException( e.getMessage(), e );
1251         }
1252     }
1253
1254     public boolean canObtainAccess( Class<?> aClass )
1255     {
1256         return aClass == Session.class;
1257     }
1258
1259     public <T>T obtainAccess( Class<T> aClass )
1260         throws MetadataRepositoryException
1261     {
1262         if ( aClass == Session.class )
1263         {
1264             try
1265             {
1266                 return (T) getJcrSession();
1267             }
1268             catch ( RepositoryException e )
1269             {
1270                 log.error( e.getMessage(), e );
1271                 throw new MetadataRepositoryException( e.getMessage(), e );
1272             }
1273         }
1274         throw new IllegalArgumentException(
1275             "Access using " + aClass + " is not supported on the JCR metadata storage" );
1276     }
1277
1278     public void close()
1279         throws MetadataRepositoryException
1280     {
1281         try
1282         {
1283             if ( getJcrSession().isLive() )
1284             {
1285                 getJcrSession().logout();
1286             }
1287         }
1288         catch ( RepositoryException e )
1289         {
1290             log.error( e.getMessage(), e );
1291             throw new MetadataRepositoryException( e.getMessage(), e );
1292         }
1293     }
1294
1295     private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1296         throws RepositoryException
1297     {
1298         String id = artifactNode.getName();
1299
1300         ArtifactMetadata artifact = new ArtifactMetadata();
1301         artifact.setId( id );
1302         artifact.setRepositoryId( repositoryId );
1303
1304         Node projectVersionNode = artifactNode.getParent();
1305         Node projectNode = projectVersionNode.getParent();
1306         Node namespaceNode = projectNode.getParent();
1307
1308         artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1309         artifact.setProject( projectNode.getName() );
1310         artifact.setProjectVersion( projectVersionNode.getName() );
1311         artifact.setVersion( artifactNode.hasProperty( "version" )
1312                                  ? artifactNode.getProperty( "version" ).getString()
1313                                  : projectVersionNode.getName() );
1314
1315         if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1316         {
1317             artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1318         }
1319
1320         if ( artifactNode.hasProperty( "whenGathered" ) )
1321         {
1322             artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1323         }
1324
1325         if ( artifactNode.hasProperty( "size" ) )
1326         {
1327             artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1328         }
1329
1330         if ( artifactNode.hasProperty( "md5" ) )
1331         {
1332             artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1333         }
1334
1335         if ( artifactNode.hasProperty( "sha1" ) )
1336         {
1337             artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1338         }
1339
1340         for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1341         {
1342             if ( n.isNodeType( FACET_NODE_TYPE ) )
1343             {
1344                 String name = n.getName();
1345                 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1346                 if ( factory == null )
1347                 {
1348                     log.error( "Attempted to load unknown project version metadata facet: " + name );
1349                 }
1350                 else
1351                 {
1352                     MetadataFacet facet = factory.createMetadataFacet();
1353                     Map<String, String> map = new HashMap<String, String>();
1354                     for ( Property p : JcrUtils.getProperties( n ) )
1355                     {
1356                         String property = p.getName();
1357                         if ( !property.startsWith( "jcr:" ) )
1358                         {
1359                             map.put( property, p.getString() );
1360                         }
1361                     }
1362                     facet.fromProperties( map );
1363                     artifact.addFacet( facet );
1364                 }
1365             }
1366         }
1367         return artifact;
1368     }
1369
1370     private static String getPropertyString( Node node, String name )
1371         throws RepositoryException
1372     {
1373         return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1374     }
1375
1376     private Collection<String> getNodeNames( String path, String nodeType )
1377         throws MetadataResolutionException
1378     {
1379         List<String> names = new ArrayList<String>();
1380
1381         try
1382         {
1383             Node root = getJcrSession().getRootNode();
1384
1385             Node nodeAtPath = root.getNode( path );
1386
1387             for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1388             {
1389                 if ( node.isNodeType( nodeType ) )
1390                 {
1391                     names.add( node.getName() );
1392                 }
1393             }
1394         }
1395         catch ( PathNotFoundException e )
1396         {
1397             // ignore repo not found for now
1398         }
1399         catch ( RepositoryException e )
1400         {
1401             throw new MetadataResolutionException( e.getMessage(), e );
1402         }
1403
1404         return names;
1405     }
1406
1407     private static String getRepositoryPath( String repositoryId )
1408     {
1409         return "repositories/" + repositoryId;
1410     }
1411
1412     private static String getRepositoryContentPath( String repositoryId )
1413     {
1414         return getRepositoryPath( repositoryId ) + "/content/";
1415     }
1416
1417     private static String getFacetPath( String repositoryId, String facetId )
1418     {
1419         return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1420     }
1421
1422     private static String getNamespacePath( String repositoryId, String namespace )
1423     {
1424         return getRepositoryContentPath( repositoryId ) + namespace.replace( '.', '/' );
1425     }
1426
1427     private static String getProjectPath( String repositoryId, String namespace, String projectId )
1428     {
1429         return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1430     }
1431
1432     private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1433                                                  String projectVersion )
1434     {
1435         return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1436     }
1437
1438     private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1439                                            String projectVersion, String id )
1440     {
1441         return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1442     }
1443
1444     private Node getOrAddNodeByPath( Node baseNode, String name )
1445         throws RepositoryException
1446     {
1447         return getOrAddNodeByPath( baseNode, name, null );
1448     }
1449
1450     private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1451         throws RepositoryException
1452     {
1453         Node node = baseNode;
1454         for ( String n : name.split( "/" ) )
1455         {
1456             node = JcrUtils.getOrAddNode( node, n );
1457             if ( nodeType != null )
1458             {
1459                 node.addMixin( nodeType );
1460             }
1461         }
1462         return node;
1463     }
1464
1465     private static String getFacetPath( String repositoryId, String facetId, String name )
1466     {
1467         return getFacetPath( repositoryId, facetId ) + "/" + name;
1468     }
1469
1470     private Node getOrAddRepositoryNode( String repositoryId )
1471         throws RepositoryException
1472     {
1473         Node root = getJcrSession().getRootNode();
1474         Node node = JcrUtils.getOrAddNode( root, "repositories" );
1475         node = JcrUtils.getOrAddNode( node, repositoryId );
1476         return node;
1477     }
1478
1479     private Node getOrAddRepositoryContentNode( String repositoryId )
1480         throws RepositoryException
1481     {
1482         Node node = getOrAddRepositoryNode( repositoryId );
1483         return JcrUtils.getOrAddNode( node, "content" );
1484     }
1485
1486     private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1487         throws RepositoryException
1488     {
1489         Node repo = getOrAddRepositoryContentNode( repositoryId );
1490         return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1491     }
1492
1493     private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1494         throws RepositoryException
1495     {
1496         Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1497         Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1498         node.addMixin( PROJECT_NODE_TYPE );
1499         return node;
1500     }
1501
1502     private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1503                                              String projectVersion )
1504         throws RepositoryException
1505     {
1506         Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1507         Node node = JcrUtils.getOrAddNode( projectNode, projectVersion );
1508         node.addMixin( PROJECT_VERSION_NODE_TYPE );
1509         return node;
1510     }
1511
1512     private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1513                                        String id )
1514         throws RepositoryException
1515     {
1516         Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1517         Node node = JcrUtils.getOrAddNode( versionNode, id );
1518         node.addMixin( ARTIFACT_NODE_TYPE );
1519         return node;
1520     }
1521
1522     private static Calendar createCalendar( Date time )
1523     {
1524         Calendar cal = Calendar.getInstance();
1525         cal.setTime( time );
1526         return cal;
1527     }
1528
1529     private String join( Collection<String> ids )
1530     {
1531         if ( ids != null && !ids.isEmpty() )
1532         {
1533             StringBuilder s = new StringBuilder();
1534             for ( String id : ids )
1535             {
1536                 s.append( id );
1537                 s.append( "," );
1538             }
1539             return s.substring( 0, s.length() - 1 );
1540         }
1541         return null;
1542     }
1543
1544     public Session getJcrSession()
1545         throws RepositoryException
1546     {
1547         if ( this.jcrSession == null || !this.jcrSession.isLive() )
1548         {
1549             jcrSession = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
1550         }
1551         return this.jcrSession;
1552     }
1553 }