001    package org.apache.archiva.metadata.repository.jcr;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import org.apache.archiva.metadata.model.ArtifactMetadata;
023    import org.apache.archiva.metadata.model.CiManagement;
024    import org.apache.archiva.metadata.model.Dependency;
025    import org.apache.archiva.metadata.model.IssueManagement;
026    import org.apache.archiva.metadata.model.License;
027    import org.apache.archiva.metadata.model.MailingList;
028    import org.apache.archiva.metadata.model.MetadataFacet;
029    import org.apache.archiva.metadata.model.MetadataFacetFactory;
030    import org.apache.archiva.metadata.model.Organization;
031    import org.apache.archiva.metadata.model.ProjectMetadata;
032    import org.apache.archiva.metadata.model.ProjectVersionMetadata;
033    import org.apache.archiva.metadata.model.ProjectVersionReference;
034    import org.apache.archiva.metadata.model.Scm;
035    import org.apache.archiva.metadata.repository.MetadataRepository;
036    import org.apache.archiva.metadata.repository.MetadataRepositoryException;
037    import org.apache.archiva.metadata.repository.MetadataResolutionException;
038    import org.apache.commons.lang.StringUtils;
039    import org.apache.jackrabbit.commons.JcrUtils;
040    import org.slf4j.Logger;
041    import org.slf4j.LoggerFactory;
042    
043    import javax.jcr.NamespaceRegistry;
044    import javax.jcr.Node;
045    import javax.jcr.NodeIterator;
046    import javax.jcr.PathNotFoundException;
047    import javax.jcr.Property;
048    import javax.jcr.Repository;
049    import javax.jcr.RepositoryException;
050    import javax.jcr.Session;
051    import javax.jcr.SimpleCredentials;
052    import javax.jcr.ValueFactory;
053    import javax.jcr.Workspace;
054    import javax.jcr.nodetype.NodeTypeManager;
055    import javax.jcr.nodetype.NodeTypeTemplate;
056    import javax.jcr.query.Query;
057    import javax.jcr.query.QueryResult;
058    import java.util.ArrayList;
059    import java.util.Arrays;
060    import java.util.Calendar;
061    import java.util.Collection;
062    import java.util.Collections;
063    import java.util.Date;
064    import java.util.HashMap;
065    import java.util.Iterator;
066    import java.util.LinkedHashSet;
067    import java.util.List;
068    import java.util.Map;
069    import java.util.Set;
070    
071    /**
072     * @todo below: revise storage format for project version metadata
073     * @todo revise reference storage
074     */
075    public class JcrMetadataRepository
076        implements MetadataRepository
077    {
078    
079        private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
080    
081        static final String NAMESPACE_NODE_TYPE = "archiva:namespace";
082    
083        static final String PROJECT_NODE_TYPE = "archiva:project";
084    
085        static final String PROJECT_VERSION_NODE_TYPE = "archiva:projectVersion";
086    
087        static final String ARTIFACT_NODE_TYPE = "archiva:artifact";
088    
089        static final String FACET_NODE_TYPE = "archiva:facet";
090    
091        private static final String DEPENDENCY_NODE_TYPE = "archiva:dependency";
092    
093        private final Map<String, MetadataFacetFactory> metadataFacetFactories;
094    
095        private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
096    
097        private Repository repository;
098    
099        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    }