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