1 package org.apache.archiva.metadata.repository.jcr;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
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;
43 import com.google.common.collect.ImmutableMap;
45 import javax.jcr.InvalidItemStateException;
46 import javax.jcr.NamespaceRegistry;
47 import javax.jcr.Node;
48 import javax.jcr.NodeIterator;
49 import javax.jcr.PathNotFoundException;
50 import javax.jcr.Property;
51 import javax.jcr.Repository;
52 import javax.jcr.RepositoryException;
53 import javax.jcr.Session;
54 import javax.jcr.SimpleCredentials;
55 import javax.jcr.ValueFactory;
56 import javax.jcr.Workspace;
57 import javax.jcr.nodetype.NodeTypeManager;
58 import javax.jcr.nodetype.NodeTypeTemplate;
59 import javax.jcr.query.Query;
60 import javax.jcr.query.QueryResult;
61 import javax.jcr.query.Row;
62 import javax.jcr.query.RowIterator;
64 import java.util.ArrayList;
65 import java.util.Arrays;
66 import java.util.Calendar;
67 import java.util.Collection;
68 import java.util.Collections;
69 import java.util.Date;
70 import java.util.HashMap;
71 import java.util.Iterator;
72 import java.util.LinkedHashSet;
73 import java.util.List;
75 import java.util.Map.Entry;
79 * TODO below: revise storage format for project version metadata
80 * TODO revise reference storage
82 public class JcrMetadataRepository
83 implements MetadataRepository
86 private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
88 static final String NAMESPACE_NODE_TYPE = "archiva:namespace";
90 static final String PROJECT_NODE_TYPE = "archiva:project";
92 static final String PROJECT_VERSION_NODE_TYPE = "archiva:projectVersion";
94 static final String ARTIFACT_NODE_TYPE = "archiva:artifact";
96 static final String FACET_NODE_TYPE = "archiva:facet";
98 private static final String DEPENDENCY_NODE_TYPE = "archiva:dependency";
100 private final Map<String, MetadataFacetFactory> metadataFacetFactories;
102 private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
104 private Repository repository;
106 private Session jcrSession;
108 public JcrMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories, Repository repository )
109 throws RepositoryException
111 this.metadataFacetFactories = metadataFacetFactories;
112 this.repository = repository;
116 static void initialize( Session session )
117 throws RepositoryException
120 // TODO: consider using namespaces for facets instead of the current approach:
121 // (if used, check if actually called by normal injection)
122 // for ( String facetId : metadataFacetFactories.keySet() )
124 // session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
126 Workspace workspace = session.getWorkspace();
127 NamespaceRegistry registry = workspace.getNamespaceRegistry();
129 if ( !Arrays.asList( registry.getPrefixes() ).contains( "archiva" ) )
131 registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
134 NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
135 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.NAMESPACE_NODE_TYPE );
136 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_NODE_TYPE );
137 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.PROJECT_VERSION_NODE_TYPE );
138 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.ARTIFACT_NODE_TYPE );
139 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.FACET_NODE_TYPE );
140 registerMixinNodeType( nodeTypeManager, JcrMetadataRepository.DEPENDENCY_NODE_TYPE );
144 private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
145 throws RepositoryException
147 NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
148 nodeType.setMixin( true );
149 nodeType.setName( name );
151 // for now just don't re-create - but in future if we change the definition, make sure to remove first as an
153 if ( !nodeTypeManager.hasNodeType( name ) )
155 nodeTypeManager.registerNodeType( nodeType, false );
160 public void updateProject( String repositoryId, ProjectMetadata project )
161 throws MetadataRepositoryException
163 updateProject( repositoryId, project.getNamespace(), project.getId() );
166 private void updateProject( String repositoryId, String namespace, String projectId )
167 throws MetadataRepositoryException
169 updateNamespace( repositoryId, namespace );
173 getOrAddProjectNode( repositoryId, namespace, projectId );
175 catch ( RepositoryException e )
177 throw new MetadataRepositoryException( e.getMessage(), e );
182 public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
183 ArtifactMetadata artifactMeta )
184 throws MetadataRepositoryException
186 updateNamespace( repositoryId, namespace );
191 getOrAddArtifactNode( repositoryId, namespace, projectId, projectVersion, artifactMeta.getId() );
193 Calendar cal = Calendar.getInstance();
194 cal.setTime( artifactMeta.getFileLastModified() );
195 node.setProperty( JCR_LAST_MODIFIED, cal );
197 cal = Calendar.getInstance();
198 cal.setTime( artifactMeta.getWhenGathered() );
199 node.setProperty( "whenGathered", cal );
201 node.setProperty( "size", artifactMeta.getSize() );
202 node.setProperty( "md5", artifactMeta.getMd5() );
203 node.setProperty( "sha1", artifactMeta.getSha1() );
205 node.setProperty( "version", artifactMeta.getVersion() );
207 // iterate over available facets to update/add/remove from the artifactMetadata
208 for ( String facetId : metadataFacetFactories.keySet() )
210 MetadataFacet metadataFacet = artifactMeta.getFacet( facetId );
211 if ( metadataFacet == null )
215 if ( node.hasNode( facetId ) )
217 node.getNode( facetId ).remove();
219 if ( metadataFacet != null )
221 // recreate, to ensure properties are removed
222 Node n = node.addNode( facetId );
223 n.addMixin( FACET_NODE_TYPE );
225 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
227 n.setProperty( entry.getKey(), entry.getValue() );
232 catch ( RepositoryException e )
234 throw new MetadataRepositoryException( e.getMessage(), e );
239 public void updateProjectVersion( String repositoryId, String namespace, String projectId,
240 ProjectVersionMetadata versionMetadata )
241 throws MetadataRepositoryException
243 updateProject( repositoryId, namespace, projectId );
248 getOrAddProjectVersionNode( repositoryId, namespace, projectId, versionMetadata.getId() );
250 versionNode.setProperty( "name", versionMetadata.getName() );
251 versionNode.setProperty( "description", versionMetadata.getDescription() );
252 versionNode.setProperty( "url", versionMetadata.getUrl() );
253 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
255 // FIXME: decide how to treat these in the content repo
256 if ( versionMetadata.getScm() != null )
258 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
259 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
260 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
262 if ( versionMetadata.getCiManagement() != null )
264 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
265 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
267 if ( versionMetadata.getIssueManagement() != null )
269 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
270 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
272 if ( versionMetadata.getOrganization() != null )
274 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
275 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
278 for ( License license : versionMetadata.getLicenses() )
280 versionNode.setProperty( "license." + i + ".name", license.getName() );
281 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
285 for ( MailingList mailingList : versionMetadata.getMailingLists() )
287 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
288 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
289 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
290 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
291 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
292 versionNode.setProperty( "mailingList." + i + ".otherArchives",
293 join( mailingList.getOtherArchives() ) );
297 if ( !versionMetadata.getDependencies().isEmpty() )
299 Node dependenciesNode = JcrUtils.getOrAddNode( versionNode, "dependencies" );
301 for ( Dependency dependency : versionMetadata.getDependencies() )
303 // Note that we deliberately don't alter the namespace path - not enough dependencies for
304 // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each
305 // component of the ID as that creates extra depth and causes a great cost in space and memory
307 // FIXME: change group ID to namespace
308 // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now.
309 // This won't support types where the extension doesn't match the type.
310 // (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM)
312 dependency.getGroupId() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion();
313 if ( dependency.getClassifier() != null )
315 id += "-" + dependency.getClassifier();
317 id += "." + dependency.getType();
319 Node n = JcrUtils.getOrAddNode( dependenciesNode, id );
320 n.addMixin( DEPENDENCY_NODE_TYPE );
322 // FIXME: remove temp code just to make it keep working
323 n.setProperty( "groupId", dependency.getGroupId() );
324 n.setProperty( "artifactId", dependency.getArtifactId() );
325 n.setProperty( "version", dependency.getVersion() );
326 n.setProperty( "type", dependency.getType() );
327 n.setProperty( "classifier", dependency.getClassifier() );
328 n.setProperty( "scope", dependency.getScope() );
329 n.setProperty( "systemPath", dependency.getSystemPath() );
330 n.setProperty( "optional", dependency.isOptional() );
332 // node has no native content at this time, just facets
333 // no need to list a type as it's implied by the path. Parents are Maven specific.
335 // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet
336 // (should also have been added to the Dependency)
338 // TODO: add a property that is a weak reference to the originating artifact, creating it if
339 // necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an
340 // artifact, which gives a different meaning to "incomplete" which is a known local project
341 // that doesn't have metadata yet but has artifacts). (Though we may want to give it the
342 // artifact mixin and another property to identify all non-local artifacts for the closure
347 for ( MetadataFacet facet : versionMetadata.getFacetList() )
349 // recreate, to ensure properties are removed
350 if ( versionNode.hasNode( facet.getFacetId() ) )
352 versionNode.getNode( facet.getFacetId() ).remove();
354 Node n = versionNode.addNode( facet.getFacetId() );
355 n.addMixin( FACET_NODE_TYPE );
357 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
359 n.setProperty( entry.getKey(), entry.getValue() );
363 catch ( RepositoryException e )
365 throw new MetadataRepositoryException( e.getMessage(), e );
370 public void updateNamespace( String repositoryId, String namespace )
371 throws MetadataRepositoryException
375 Node node = getOrAddNamespaceNode( repositoryId, namespace );
376 node.setProperty( "namespace", namespace );
378 catch ( RepositoryException e )
380 throw new MetadataRepositoryException( e.getMessage(), e );
385 public void removeProject( String repositoryId, String namespace, String projectId )
386 throws MetadataRepositoryException
390 Node root = getJcrSession().getRootNode();
391 String namespacePath = getNamespacePath( repositoryId, namespace );
393 if ( root.hasNode( namespacePath ) )
395 Iterator<Node> nodeIterator = JcrUtils.getChildNodes( root.getNode( namespacePath ) ).iterator();
396 while ( nodeIterator.hasNext() )
398 Node node = nodeIterator.next();
399 if ( node.isNodeType( PROJECT_NODE_TYPE ) && projectId.equals( node.getName() ) )
407 catch ( RepositoryException e )
409 throw new MetadataRepositoryException( e.getMessage(), e );
416 public boolean hasMetadataFacet( String repositoryId, String facetId )
417 throws MetadataRepositoryException
421 Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
422 return node.getNodes().hasNext();
424 catch ( PathNotFoundException e )
426 // ignored - the facet doesn't exist, so return false
429 catch ( RepositoryException e )
431 throw new MetadataRepositoryException( e.getMessage(), e );
436 public List<String> getMetadataFacets( String repositoryId, String facetId )
437 throws MetadataRepositoryException
439 List<String> facets = new ArrayList<>();
443 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
444 // are paths themselves
445 Node node = getJcrSession().getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
447 // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
449 recurse( facets, "", node );
451 catch ( PathNotFoundException e )
453 // ignored - the facet doesn't exist, so return the empty list
455 catch ( RepositoryException e )
457 throw new MetadataRepositoryException( e.getMessage(), e );
462 private void recurse( List<String> facets, String prefix, Node node )
463 throws RepositoryException
465 for ( Node n : JcrUtils.getChildNodes( node ) )
467 String name = prefix + "/" + n.getName();
470 recurse( facets, name, n );
474 // strip leading / first
475 facets.add( name.substring( 1 ) );
481 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
482 throws MetadataRepositoryException
484 MetadataFacet metadataFacet = null;
487 Node root = getJcrSession().getRootNode();
488 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
490 if ( metadataFacetFactories == null )
492 return metadataFacet;
495 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
496 if ( metadataFacetFactory != null )
498 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
499 Map<String, String> map = new HashMap<>();
500 for ( Property property : JcrUtils.getProperties( node ) )
502 String p = property.getName();
503 if ( !p.startsWith( "jcr:" ) )
505 map.put( p, property.getString() );
508 metadataFacet.fromProperties( map );
511 catch ( PathNotFoundException e )
513 // ignored - the facet doesn't exist, so return null
515 catch ( RepositoryException e )
517 throw new MetadataRepositoryException( e.getMessage(), e );
519 return metadataFacet;
523 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
524 throws MetadataRepositoryException
528 Node repo = getOrAddRepositoryNode( repositoryId );
529 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
531 String id = metadataFacet.getFacetId();
532 Node facetNode = JcrUtils.getOrAddNode( facets, id );
534 Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
536 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
538 node.setProperty( entry.getKey(), entry.getValue() );
541 catch ( RepositoryException e )
543 throw new MetadataRepositoryException( e.getMessage(), e );
548 public void removeNamespace( String repositoryId, String projectId )
549 throws MetadataRepositoryException
553 Node root = getJcrSession().getRootNode();
554 String path = getNamespacePath( repositoryId, projectId );
555 if ( root.hasNode( path ) )
557 Node node = root.getNode( path );
558 if ( node.isNodeType( NAMESPACE_NODE_TYPE ) )
564 catch ( RepositoryException e )
566 throw new MetadataRepositoryException( e.getMessage(), e );
571 public void removeMetadataFacets( String repositoryId, String facetId )
572 throws MetadataRepositoryException
576 Node root = getJcrSession().getRootNode();
577 String path = getFacetPath( repositoryId, facetId );
578 if ( root.hasNode( path ) )
580 root.getNode( path ).remove();
583 catch ( RepositoryException e )
585 throw new MetadataRepositoryException( e.getMessage(), e );
590 public void removeMetadataFacet( String repositoryId, String facetId, String name )
591 throws MetadataRepositoryException
595 Node root = getJcrSession().getRootNode();
596 String path = getFacetPath( repositoryId, facetId, name );
597 if ( root.hasNode( path ) )
599 Node node = root.getNode( path );
602 // also remove empty container nodes
603 Node parent = node.getParent();
607 while ( !node.hasNodes() );
610 catch ( RepositoryException e )
612 throw new MetadataRepositoryException( e.getMessage(), e );
617 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
618 throws MetadataRepositoryException
620 List<ArtifactMetadata> artifacts;
622 String q = getArtifactQuery( repoId );
624 if ( startTime != null )
626 q += " AND [whenGathered] >= $start";
628 if ( endTime != null )
630 q += " AND [whenGathered] <= $end";
635 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
636 ValueFactory valueFactory = getJcrSession().getValueFactory();
637 if ( startTime != null )
639 query.bindValue( "start", valueFactory.createValue( createCalendar( startTime ) ) );
641 if ( endTime != null )
643 query.bindValue( "end", valueFactory.createValue( createCalendar( endTime ) ) );
645 QueryResult result = query.execute();
647 artifacts = new ArrayList<>();
648 for ( Node n : JcrUtils.getNodes( result ) )
650 artifacts.add( getArtifactFromNode( repoId, n ) );
653 catch ( RepositoryException e )
655 throw new MetadataRepositoryException( e.getMessage(), e );
661 public Collection<String> getRepositories()
662 throws MetadataRepositoryException
664 List<String> repositories;
668 Node root = getJcrSession().getRootNode();
669 if ( root.hasNode( "repositories" ) )
671 Node node = root.getNode( "repositories" );
673 repositories = new ArrayList<>();
674 NodeIterator i = node.getNodes();
675 while ( i.hasNext() )
677 Node n = i.nextNode();
678 repositories.add( n.getName() );
683 repositories = Collections.emptyList();
686 catch ( RepositoryException e )
688 throw new MetadataRepositoryException( e.getMessage(), e );
694 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
695 throws MetadataRepositoryException
697 List<ArtifactMetadata> artifacts;
699 String q = getArtifactQuery( repositoryId ) + " AND ([sha1] = $checksum OR [md5] = $checksum)";
703 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
704 ValueFactory valueFactory = getJcrSession().getValueFactory();
705 query.bindValue( "checksum", valueFactory.createValue( checksum ) );
706 QueryResult result = query.execute();
708 artifacts = new ArrayList<>();
709 for ( Node n : JcrUtils.getNodes( result ) )
711 artifacts.add( getArtifactFromNode( repositoryId, n ) );
714 catch ( RepositoryException e )
716 throw new MetadataRepositoryException( e.getMessage(), e );
721 private List<ArtifactMetadata> runJcrQuery( String repositoryId, String q, Map<String, String> bindings )
722 throws MetadataRepositoryException
724 List<ArtifactMetadata> artifacts;
725 if ( repositoryId != null )
727 q += " AND ISDESCENDANTNODE(artifact,'/" + getRepositoryContentPath( repositoryId ) + "')";
730 log.info( "Running JCR Query: {}", q );
734 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
735 ValueFactory valueFactory = getJcrSession().getValueFactory();
736 for ( Entry<String, String> entry : bindings.entrySet() )
738 query.bindValue( entry.getKey(), valueFactory.createValue( entry.getValue() ) );
740 long start = Calendar.getInstance().getTimeInMillis();
741 QueryResult result = query.execute();
742 long end = Calendar.getInstance().getTimeInMillis();
743 log.info( "JCR Query ran in {} milliseconds: {}", end - start , q );
745 artifacts = new ArrayList<>();
746 RowIterator rows = result.getRows();
747 while ( rows.hasNext() )
749 Row row = rows.nextRow();
750 Node node = row.getNode( "artifact" );
751 artifacts.add( getArtifactFromNode( repositoryId, node ) );
754 catch ( RepositoryException e )
756 throw new MetadataRepositoryException( e.getMessage(), e );
762 public List<ArtifactMetadata> getArtifactsByProjectVersionMetadata( String key, String value, String repositoryId )
763 throws MetadataRepositoryException
766 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE
767 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) INNER JOIN [" + FACET_NODE_TYPE
768 + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE ([facet].[" + key + "] = $value)";
770 return runJcrQuery( repositoryId, q, ImmutableMap.of( "value", value ) );
775 public List<ArtifactMetadata> getArtifactsByMetadata( String key, String value, String repositoryId )
776 throws MetadataRepositoryException
779 "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact INNER JOIN [" + FACET_NODE_TYPE
780 + "] AS facet ON ISCHILDNODE(facet, artifact) WHERE ([facet].[" + key + "] = $value)";
782 return runJcrQuery( repositoryId, q, ImmutableMap.of( "value", value ) );
787 public List<ArtifactMetadata> getArtifactsByProperty( String key, String value, String repositoryId )
788 throws MetadataRepositoryException
791 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE
792 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE ([projectVersion].[" + key
795 return runJcrQuery( repositoryId, q, ImmutableMap.of( "value", value ) );
800 public void removeRepository( String repositoryId )
801 throws MetadataRepositoryException
805 Node root = getJcrSession().getRootNode();
806 String path = getRepositoryPath( repositoryId );
807 if ( root.hasNode( path ) )
809 root.getNode( path ).remove();
812 catch ( RepositoryException e )
814 throw new MetadataRepositoryException( e.getMessage(), e );
819 public List<ArtifactMetadata> getArtifacts( String repositoryId )
820 throws MetadataRepositoryException
822 List<ArtifactMetadata> artifacts;
824 String q = getArtifactQuery( repositoryId );
828 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
829 QueryResult result = query.execute();
831 artifacts = new ArrayList<>();
832 for ( Node n : JcrUtils.getNodes( result ) )
834 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
836 artifacts.add( getArtifactFromNode( repositoryId, n ) );
840 catch ( RepositoryException e )
842 throw new MetadataRepositoryException( e.getMessage(), e );
847 private static String getArtifactQuery( String repositoryId )
849 return "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/" +
850 getRepositoryContentPath( repositoryId ) + "')";
854 public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
855 throws MetadataResolutionException
857 ProjectMetadata metadata = null;
861 Node root = getJcrSession().getRootNode();
863 // basically just checking it exists
864 String path = getProjectPath( repositoryId, namespace, projectId );
865 if ( root.hasNode( path ) )
867 metadata = new ProjectMetadata();
868 metadata.setId( projectId );
869 metadata.setNamespace( namespace );
872 catch ( RepositoryException e )
874 throw new MetadataResolutionException( e.getMessage(), e );
881 public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
882 String projectVersion )
883 throws MetadataResolutionException
885 ProjectVersionMetadata versionMetadata;
889 Node root = getJcrSession().getRootNode();
891 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
892 if ( !root.hasNode( path ) )
897 Node node = root.getNode( path );
899 versionMetadata = new ProjectVersionMetadata();
900 versionMetadata.setId( projectVersion );
901 versionMetadata.setName( getPropertyString( node, "name" ) );
902 versionMetadata.setDescription( getPropertyString( node, "description" ) );
903 versionMetadata.setUrl( getPropertyString( node, "url" ) );
904 versionMetadata.setIncomplete(
905 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
907 // FIXME: decide how to treat these in the content repo
908 String scmConnection = getPropertyString( node, "scm.connection" );
909 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
910 String scmUrl = getPropertyString( node, "scm.url" );
911 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
914 scm.setConnection( scmConnection );
915 scm.setDeveloperConnection( scmDeveloperConnection );
916 scm.setUrl( scmUrl );
917 versionMetadata.setScm( scm );
920 String ciSystem = getPropertyString( node, "ci.system" );
921 String ciUrl = getPropertyString( node, "ci.url" );
922 if ( ciSystem != null || ciUrl != null )
924 CiManagement ci = new CiManagement();
925 ci.setSystem( ciSystem );
927 versionMetadata.setCiManagement( ci );
930 String issueSystem = getPropertyString( node, "issue.system" );
931 String issueUrl = getPropertyString( node, "issue.url" );
932 if ( issueSystem != null || issueUrl != null )
934 IssueManagement issueManagement = new IssueManagement();
935 issueManagement.setSystem( issueSystem );
936 issueManagement.setUrl( issueUrl );
937 versionMetadata.setIssueManagement( issueManagement );
940 String orgName = getPropertyString( node, "org.name" );
941 String orgUrl = getPropertyString( node, "org.url" );
942 if ( orgName != null || orgUrl != null )
944 Organization org = new Organization();
945 org.setName( orgName );
946 org.setUrl( orgUrl );
947 versionMetadata.setOrganization( org );
950 boolean done = false;
954 String licenseName = getPropertyString( node, "license." + i + ".name" );
955 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
956 if ( licenseName != null || licenseUrl != null )
958 License license = new License();
959 license.setName( licenseName );
960 license.setUrl( licenseUrl );
961 versionMetadata.addLicense( license );
974 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
975 if ( mailingListName != null )
977 MailingList mailingList = new MailingList();
978 mailingList.setName( mailingListName );
979 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
980 String n = "mailingList." + i + ".otherArchives";
981 if ( node.hasProperty( n ) )
983 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
987 mailingList.setOtherArchives( Collections.<String>emptyList() );
989 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
990 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
991 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
992 versionMetadata.addMailingList( mailingList );
1001 if ( node.hasNode( "dependencies" ) )
1003 Node dependenciesNode = node.getNode( "dependencies" );
1004 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
1006 if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
1008 Dependency dependency = new Dependency();
1009 // FIXME: correct these properties
1010 dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
1011 dependency.setGroupId( getPropertyString( n, "groupId" ) );
1012 dependency.setClassifier( getPropertyString( n, "classifier" ) );
1013 dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
1014 dependency.setScope( getPropertyString( n, "scope" ) );
1015 dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
1016 dependency.setType( getPropertyString( n, "type" ) );
1017 dependency.setVersion( getPropertyString( n, "version" ) );
1018 versionMetadata.addDependency( dependency );
1023 for ( Node n : JcrUtils.getChildNodes( node ) )
1025 if ( n.isNodeType( FACET_NODE_TYPE ) )
1027 String name = n.getName();
1028 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1029 if ( factory == null )
1031 log.error( "Attempted to load unknown project version metadata facet: {}", name );
1035 MetadataFacet facet = factory.createMetadataFacet();
1036 Map<String, String> map = new HashMap<>();
1037 for ( Property property : JcrUtils.getProperties( n ) )
1039 String p = property.getName();
1040 if ( !p.startsWith( "jcr:" ) )
1042 map.put( p, property.getString() );
1045 facet.fromProperties( map );
1046 versionMetadata.addFacet( facet );
1051 catch ( RepositoryException e )
1053 throw new MetadataResolutionException( e.getMessage(), e );
1056 return versionMetadata;
1060 public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
1061 String projectVersion )
1062 throws MetadataResolutionException
1064 Set<String> versions = new LinkedHashSet<String>();
1068 Node root = getJcrSession().getRootNode();
1070 Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
1072 for ( Node n : JcrUtils.getChildNodes( node ) )
1074 versions.add( n.getProperty( "version" ).getString() );
1077 catch ( PathNotFoundException e )
1079 // ignore repo not found for now
1081 catch ( RepositoryException e )
1083 throw new MetadataResolutionException( e.getMessage(), e );
1090 public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
1091 String projectId, String projectVersion )
1092 throws MetadataResolutionException
1095 List<ProjectVersionReference> references = new ArrayList<>();
1097 // TODO: bind variables instead
1098 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId +
1099 "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
1100 if ( projectVersion != null )
1102 q += " AND [version]='" + projectVersion + "'";
1106 Query query = getJcrSession().getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
1107 QueryResult result = query.execute();
1109 for ( Node n : JcrUtils.getNodes( result ) )
1111 n = n.getParent(); // dependencies grouping element
1113 n = n.getParent(); // project version
1114 String usedByProjectVersion = n.getName();
1116 n = n.getParent(); // project
1117 String usedByProject = n.getName();
1119 n = n.getParent(); // namespace
1120 String usedByNamespace = n.getProperty( "namespace" ).getString();
1122 ProjectVersionReference ref = new ProjectVersionReference();
1123 ref.setNamespace( usedByNamespace );
1124 ref.setProjectId( usedByProject );
1125 ref.setProjectVersion( usedByProjectVersion );
1126 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
1127 references.add( ref );
1130 catch ( RepositoryException e )
1132 throw new MetadataResolutionException( e.getMessage(), e );
1139 public Collection<String> getRootNamespaces( String repositoryId )
1140 throws MetadataResolutionException
1142 return getNamespaces( repositoryId, null );
1146 public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
1147 throws MetadataResolutionException
1149 String path = baseNamespace != null
1150 ? getNamespacePath( repositoryId, baseNamespace )
1151 : getRepositoryContentPath( repositoryId );
1153 return getNodeNames( path, NAMESPACE_NODE_TYPE );
1157 public Collection<String> getProjects( String repositoryId, String namespace )
1158 throws MetadataResolutionException
1160 return getNodeNames( getNamespacePath( repositoryId, namespace ), PROJECT_NODE_TYPE );
1164 public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
1165 throws MetadataResolutionException
1167 return getNodeNames( getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
1171 public void removeArtifact( ArtifactMetadata artifactMetadata, String baseVersion )
1172 throws MetadataRepositoryException
1175 String repositoryId = artifactMetadata.getRepositoryId();
1179 Node root = getJcrSession().getRootNode();
1181 getProjectVersionPath( repositoryId, artifactMetadata.getNamespace(), artifactMetadata.getProject(),
1184 if ( root.hasNode( path ) )
1186 Node node = root.getNode( path );
1188 for ( Node n : JcrUtils.getChildNodes( node ) )
1190 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1192 if ( n.hasProperty( "version" ) )
1194 String version = n.getProperty( "version" ).getString();
1195 if ( StringUtils.equals( version, artifactMetadata.getVersion() ) )
1205 catch ( RepositoryException e )
1207 throw new MetadataRepositoryException( e.getMessage(), e );
1215 public void removeProjectVersion( String repoId, String namespace, String projectId, String projectVersion )
1216 throws MetadataRepositoryException
1221 String path = getProjectPath( repoId, namespace, projectId );
1222 Node root = getJcrSession().getRootNode();
1224 Node nodeAtPath = root.getNode( path );
1226 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1228 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( projectVersion,
1235 catch ( RepositoryException e )
1237 throw new MetadataRepositoryException( e.getMessage(), e );
1242 public void removeArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
1244 throws MetadataRepositoryException
1248 Node root = getJcrSession().getRootNode();
1249 String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
1250 if ( root.hasNode( path ) )
1252 root.getNode( path ).remove();
1257 path = getProjectPath( repositoryId, namespace, projectId );
1259 Node nodeAtPath = root.getNode( path );
1261 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1263 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) //
1264 && StringUtils.equals( node.getName(), projectVersion ) )
1270 catch ( RepositoryException e )
1272 throw new MetadataRepositoryException( e.getMessage(), e );
1277 public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion,
1278 MetadataFacet metadataFacet )
1279 throws MetadataRepositoryException
1283 Node root = getJcrSession().getRootNode();
1284 String path = getProjectVersionPath( repositoryId, namespace, project, projectVersion );
1286 if ( root.hasNode( path ) )
1288 Node node = root.getNode( path );
1290 for ( Node n : JcrUtils.getChildNodes( node ) )
1292 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1294 ArtifactMetadata artifactMetadata = getArtifactFromNode( repositoryId, n );
1295 log.debug( "artifactMetadata: {}", artifactMetadata );
1296 MetadataFacet metadataFacetToRemove = artifactMetadata.getFacet( metadataFacet.getFacetId() );
1297 if ( metadataFacetToRemove != null && metadataFacet.equals( metadataFacetToRemove ) )
1305 catch ( RepositoryException e )
1307 throw new MetadataRepositoryException( e.getMessage(), e );
1312 public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
1313 String projectVersion )
1314 throws MetadataResolutionException
1316 List<ArtifactMetadata> artifacts = new ArrayList<>();
1320 Node root = getJcrSession().getRootNode();
1321 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1323 if ( root.hasNode( path ) )
1325 Node node = root.getNode( path );
1327 for ( Node n : JcrUtils.getChildNodes( node ) )
1329 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1331 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1336 catch ( RepositoryException e )
1338 throw new MetadataResolutionException( e.getMessage(), e );
1349 getJcrSession().save();
1350 } catch ( InvalidItemStateException e ) {
1351 // olamy this might happen when deleting a repo while is under scanning
1352 log.warn( "skip InvalidItemStateException:" + e.getMessage(), e );
1354 catch ( RepositoryException e )
1356 throw new RuntimeException( e.getMessage(), e );
1361 public void revert()
1365 getJcrSession().refresh( false );
1367 catch ( RepositoryException e )
1369 throw new RuntimeException( e.getMessage(), e );
1374 public boolean canObtainAccess( Class<?> aClass )
1376 return aClass == Session.class;
1380 public <T>T obtainAccess( Class<T> aClass )
1381 throws MetadataRepositoryException
1383 if ( aClass == Session.class )
1387 return (T) getJcrSession();
1389 catch ( RepositoryException e )
1391 log.error( e.getMessage(), e );
1392 throw new MetadataRepositoryException( e.getMessage(), e );
1395 throw new IllegalArgumentException(
1396 "Access using " + aClass + " is not supported on the JCR metadata storage" );
1401 throws MetadataRepositoryException
1403 if ( jcrSession != null && jcrSession.isLive() )
1405 jcrSession.logout();
1411 * Exact is ignored as we can't do exact search in any property, we need a key
1414 public List<ArtifactMetadata> searchArtifacts( String text, String repositoryId, boolean exact )
1415 throws MetadataRepositoryException
1417 return searchArtifacts( null, text, repositoryId, exact );
1421 public List<ArtifactMetadata> searchArtifacts( String key, String text, String repositoryId, boolean exact )
1422 throws MetadataRepositoryException
1424 // we can't do exact search in any property (*), we need a key
1425 boolean e = exact && key != null;
1426 String theKey = key == null ? "*" : "[" + key + "]";
1427 String projectVersionCondition =
1428 e ? "(projectVersion." + theKey + " = $value)" : "contains([projectVersion]." + theKey + ", $value)";
1429 String facetCondition = e ? "(facet." + theKey + " = $value)" : "contains([facet]." + theKey + ", $value)";
1431 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion LEFT OUTER JOIN ["
1432 + ARTIFACT_NODE_TYPE + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) LEFT OUTER JOIN ["
1433 + FACET_NODE_TYPE + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE ("
1434 + projectVersionCondition + " OR " + facetCondition + ")";
1436 return runJcrQuery( repositoryId, q, ImmutableMap.of( "value", text ) );
1439 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1440 throws RepositoryException
1442 String id = artifactNode.getName();
1444 ArtifactMetadata artifact = new ArtifactMetadata();
1445 artifact.setId( id );
1446 artifact.setRepositoryId( repositoryId == null ? artifactNode.getAncestor(2).getName() : repositoryId );
1448 Node projectVersionNode = artifactNode.getParent();
1449 Node projectNode = projectVersionNode.getParent();
1450 Node namespaceNode = projectNode.getParent();
1452 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1453 artifact.setProject( projectNode.getName() );
1454 artifact.setProjectVersion( projectVersionNode.getName() );
1455 artifact.setVersion( artifactNode.hasProperty( "version" )
1456 ? artifactNode.getProperty( "version" ).getString()
1457 : projectVersionNode.getName() );
1459 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1461 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1464 if ( artifactNode.hasProperty( "whenGathered" ) )
1466 artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
1469 if ( artifactNode.hasProperty( "size" ) )
1471 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1474 if ( artifactNode.hasProperty( "md5" ) )
1476 artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
1479 if ( artifactNode.hasProperty( "sha1" ) )
1481 artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
1484 for ( Node n : JcrUtils.getChildNodes( artifactNode ) )
1486 if ( n.isNodeType( FACET_NODE_TYPE ) )
1488 String name = n.getName();
1489 MetadataFacetFactory factory = metadataFacetFactories.get( name );
1490 if ( factory == null )
1492 log.error( "Attempted to load unknown project version metadata facet: " + name );
1496 MetadataFacet facet = factory.createMetadataFacet();
1497 Map<String, String> map = new HashMap<>();
1498 for ( Property p : JcrUtils.getProperties( n ) )
1500 String property = p.getName();
1501 if ( !property.startsWith( "jcr:" ) )
1503 map.put( property, p.getString() );
1506 facet.fromProperties( map );
1507 artifact.addFacet( facet );
1514 private static String getPropertyString( Node node, String name )
1515 throws RepositoryException
1517 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1520 private Collection<String> getNodeNames( String path, String nodeType )
1521 throws MetadataResolutionException
1523 List<String> names = new ArrayList<>();
1527 Node root = getJcrSession().getRootNode();
1529 Node nodeAtPath = root.getNode( path );
1531 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1533 if ( node.isNodeType( nodeType ) )
1535 names.add( node.getName() );
1539 catch ( PathNotFoundException e )
1541 // ignore repo not found for now
1543 catch ( RepositoryException e )
1545 throw new MetadataResolutionException( e.getMessage(), e );
1551 private static String getRepositoryPath( String repositoryId )
1553 return "repositories/" + repositoryId;
1556 private static String getRepositoryContentPath( String repositoryId )
1558 return getRepositoryPath( repositoryId ) + "/content/";
1561 private static String getFacetPath( String repositoryId, String facetId )
1563 return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1566 private static String getNamespacePath( String repositoryId, String namespace )
1568 return getRepositoryContentPath( repositoryId ) + namespace.replace( '.', '/' );
1571 private static String getProjectPath( String repositoryId, String namespace, String projectId )
1573 return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1576 private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1577 String projectVersion )
1579 return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1582 private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1583 String projectVersion, String id )
1585 return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1588 private Node getOrAddNodeByPath( Node baseNode, String name )
1589 throws RepositoryException
1591 return getOrAddNodeByPath( baseNode, name, null );
1594 private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1595 throws RepositoryException
1597 Node node = baseNode;
1598 for ( String n : name.split( "/" ) )
1600 node = JcrUtils.getOrAddNode( node, n );
1601 if ( nodeType != null )
1603 node.addMixin( nodeType );
1609 private static String getFacetPath( String repositoryId, String facetId, String name )
1611 return getFacetPath( repositoryId, facetId ) + "/" + name;
1614 private Node getOrAddRepositoryNode( String repositoryId )
1615 throws RepositoryException
1617 Node root = getJcrSession().getRootNode();
1618 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1619 node = JcrUtils.getOrAddNode( node, repositoryId );
1623 private Node getOrAddRepositoryContentNode( String repositoryId )
1624 throws RepositoryException
1626 Node node = getOrAddRepositoryNode( repositoryId );
1627 return JcrUtils.getOrAddNode( node, "content" );
1630 private Node getOrAddNamespaceNode( String repositoryId, String namespace )
1631 throws RepositoryException
1633 Node repo = getOrAddRepositoryContentNode( repositoryId );
1634 return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1637 private Node getOrAddProjectNode( String repositoryId, String namespace, String projectId )
1638 throws RepositoryException
1640 Node namespaceNode = getOrAddNamespaceNode( repositoryId, namespace );
1641 Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1642 node.addMixin( PROJECT_NODE_TYPE );
1646 private Node getOrAddProjectVersionNode( String repositoryId, String namespace, String projectId,
1647 String projectVersion )
1648 throws RepositoryException
1650 Node projectNode = getOrAddProjectNode( repositoryId, namespace, projectId );
1651 Node node = JcrUtils.getOrAddNode( projectNode, projectVersion );
1652 node.addMixin( PROJECT_VERSION_NODE_TYPE );
1656 private Node getOrAddArtifactNode( String repositoryId, String namespace, String projectId, String projectVersion,
1658 throws RepositoryException
1660 Node versionNode = getOrAddProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
1661 Node node = JcrUtils.getOrAddNode( versionNode, id );
1662 node.addMixin( ARTIFACT_NODE_TYPE );
1666 private static Calendar createCalendar( Date time )
1668 Calendar cal = Calendar.getInstance();
1669 cal.setTime( time );
1673 private String join( Collection<String> ids )
1675 if ( ids != null && !ids.isEmpty() )
1677 StringBuilder s = new StringBuilder();
1678 for ( String id : ids )
1683 return s.substring( 0, s.length() - 1 );
1688 public Session getJcrSession()
1689 throws RepositoryException
1691 if ( this.jcrSession == null || !this.jcrSession.isLive() )
1693 jcrSession = repository.login( new SimpleCredentials( "admin", "admin".toCharArray() ) );
1695 return this.jcrSession;