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 com.google.common.collect.ImmutableMap;
23 import org.apache.archiva.checksum.ChecksumAlgorithm;
24 import org.apache.archiva.metadata.QueryParameter;
25 import org.apache.archiva.metadata.model.*;
26 import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
27 import org.apache.archiva.metadata.repository.AbstractMetadataRepository;
28 import org.apache.archiva.metadata.repository.MetadataRepository;
29 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
30 import org.apache.archiva.metadata.repository.MetadataResolutionException;
31 import org.apache.archiva.metadata.repository.MetadataService;
32 import org.apache.archiva.metadata.repository.MetadataSessionException;
33 import org.apache.archiva.metadata.repository.RepositorySession;
34 import org.apache.archiva.metadata.repository.stats.model.RepositoryStatistics;
35 import org.apache.archiva.metadata.repository.stats.model.RepositoryStatisticsProvider;
36 import org.apache.commons.lang.StringUtils;
37 import org.apache.jackrabbit.JcrConstants;
38 import org.apache.jackrabbit.commons.JcrUtils;
39 import org.apache.jackrabbit.commons.cnd.CndImporter;
40 import org.apache.jackrabbit.commons.cnd.ParseException;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 import javax.jcr.NamespaceRegistry;
45 import javax.jcr.Node;
46 import javax.jcr.NodeIterator;
47 import javax.jcr.PathNotFoundException;
48 import javax.jcr.Property;
49 import javax.jcr.Repository;
50 import javax.jcr.RepositoryException;
51 import javax.jcr.Session;
52 import javax.jcr.SimpleCredentials;
53 import javax.jcr.Value;
54 import javax.jcr.ValueFactory;
55 import javax.jcr.Workspace;
56 import javax.jcr.query.Query;
57 import javax.jcr.query.QueryManager;
58 import javax.jcr.query.QueryResult;
59 import javax.jcr.query.Row;
60 import javax.jcr.query.RowIterator;
61 import java.io.IOException;
62 import java.io.InputStreamReader;
63 import java.io.Reader;
64 import java.time.ZonedDateTime;
66 import java.util.Map.Entry;
67 import java.util.function.Consumer;
68 import java.util.function.Function;
69 import java.util.stream.Stream;
70 import java.util.stream.StreamSupport;
72 import static javax.jcr.Property.JCR_LAST_MODIFIED;
73 import static org.apache.archiva.metadata.repository.jcr.JcrConstants.*;
76 * TODO below: revise storage format for project version metadata
77 * TODO revise reference storage
79 public class JcrMetadataRepository
80 extends AbstractMetadataRepository implements MetadataRepository, RepositoryStatisticsProvider
84 private static final String QUERY_ARTIFACT_1 = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/";
86 static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_1 = "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE
87 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) INNER JOIN [" + FACET_NODE_TYPE
88 + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE ([facet].[";
89 static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_2= "] = $value)";
91 static final String QUERY_ARTIFACTS_BY_METADATA_1 = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact INNER JOIN [" + FACET_NODE_TYPE
92 + "] AS facet ON ISCHILDNODE(facet, artifact) WHERE ([facet].[";
93 static final String QUERY_ARTIFACTS_BY_METADATA_2 = "] = $value)";
95 static final String QUERY_ARTIFACTS_BY_PROPERTY_1 = "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE
96 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE ([projectVersion].[";
97 static final String QUERY_ARTIFACTS_BY_PROPERTY_2 = "] = $value)";
100 private static final String QUERY_ARTIFACT_2 = "')";
102 private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
104 private Repository repository;
106 public JcrMetadataRepository( MetadataService metadataService, Repository repository )
107 throws RepositoryException
109 super( metadataService );
110 this.repository = repository;
114 public static void initializeNodeTypes( Session session )
115 throws RepositoryException
118 // TODO: consider using namespaces for facets instead of the current approach:
119 // (if used, check if actually called by normal injection)
120 // for ( String facetId : metadataFacetFactories.keySet() )
122 // session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
124 Workspace workspace = session.getWorkspace();
125 NamespaceRegistry registry = workspace.getNamespaceRegistry();
127 if ( !Arrays.asList( registry.getPrefixes() ).contains( "archiva" ) )
129 registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
133 Reader cndReader = new InputStreamReader(
134 Thread.currentThread( ).getContextClassLoader( ).getResourceAsStream( "org/apache/archiva/metadata/repository/jcr/jcr-schema.cnd" ) ))
136 CndImporter.registerNodeTypes( cndReader, session );
138 catch ( ParseException e )
140 e.printStackTrace( );
142 catch ( IOException e )
144 e.printStackTrace( );
149 private Session getSession(RepositorySession repositorySession) throws MetadataRepositoryException {
150 if (repositorySession instanceof JcrRepositorySession ) {
151 return ( (JcrRepositorySession) repositorySession ).getJcrSession();
153 throw new MetadataRepositoryException( "The given session object is not a JcrSession instance: " + repositorySession.getClass( ).getName( ) );
158 public void updateProject( RepositorySession session, String repositoryId, ProjectMetadata project )
159 throws MetadataRepositoryException
161 final Session jcrSession = getSession( session );
162 updateProject( jcrSession, repositoryId, project.getNamespace(), project.getId() );
165 private void updateProject( Session jcrSession, String repositoryId, String namespace, String projectId )
166 throws MetadataRepositoryException
168 updateNamespace( jcrSession , repositoryId, namespace );
172 getOrAddProjectNode( jcrSession, repositoryId, namespace, projectId );
174 catch ( RepositoryException e )
176 throw new MetadataRepositoryException( e.getMessage(), e );
181 public void updateArtifact( RepositorySession session, String repositoryId, String namespace, String projectId, String projectVersion,
182 ArtifactMetadata artifactMeta )
183 throws MetadataRepositoryException
185 final Session jcrSession = getSession( session );
186 updateNamespace( session, repositoryId, namespace );
191 getOrAddArtifactNode( jcrSession, repositoryId, namespace, projectId, projectVersion, artifactMeta.getId() );
193 node.setProperty( "id", artifactMeta.getId( ) );
194 Calendar cal = GregorianCalendar.from(artifactMeta.getFileLastModified());
195 node.setProperty( JCR_LAST_MODIFIED, cal );
197 cal = GregorianCalendar.from(artifactMeta.getWhenGathered());
198 node.setProperty( "whenGathered", cal );
200 node.setProperty( "size", artifactMeta.getSize() );
203 Node cslistNode = getOrAddNodeByPath( node, "checksums" );
204 NodeIterator nit = cslistNode.getNodes("*");
205 while (nit.hasNext()) {
206 Node csNode = nit.nextNode();
207 if (csNode.isNodeType( CHECKSUM_NODE_TYPE )) {
211 for ( Map.Entry<ChecksumAlgorithm, String> entry : artifactMeta.getChecksums().entrySet()) {
212 String type = entry.getKey( ).name( );
213 Node csNode = cslistNode.addNode( type , CHECKSUM_NODE_TYPE);
214 csNode.setProperty( "type", type );
215 csNode.setProperty( "value", entry.getValue( ) );
218 node.setProperty( "version", artifactMeta.getVersion() );
220 // iterate over available facets to update/add/remove from the artifactMetadata
221 for ( String facetId : metadataService.getSupportedFacets() )
223 MetadataFacet metadataFacet = artifactMeta.getFacet( facetId );
224 if ( metadataFacet == null )
228 if ( node.hasNode( facetId ) )
230 node.getNode( facetId ).remove();
232 if ( metadataFacet != null )
234 // recreate, to ensure properties are removed
235 Node n = node.addNode( facetId);
236 n.addMixin( FACET_NODE_TYPE );
237 n.setProperty( "facetId", facetId );
239 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
241 n.setProperty( entry.getKey(), entry.getValue() );
246 catch ( RepositoryException e )
248 throw new MetadataRepositoryException( e.getMessage(), e );
253 public void updateProjectVersion( RepositorySession session, String repositoryId, String namespace, String projectId,
254 ProjectVersionMetadata versionMetadata )
255 throws MetadataRepositoryException
257 final Session jcrSession = getSession( session );
258 updateProject( jcrSession, repositoryId, namespace, projectId );
263 getOrAddProjectVersionNode( jcrSession, repositoryId, namespace, projectId, versionMetadata.getId() );
264 versionNode.setProperty( "id", versionMetadata.getId( ) );
265 versionNode.setProperty( "name", StringUtils.isEmpty( versionMetadata.getName() ) ? "" : versionMetadata.getName() );
266 versionNode.setProperty( "description", StringUtils.isEmpty( versionMetadata.getDescription() ) ? "" : versionMetadata.getDescription() );
267 versionNode.setProperty( "url", versionMetadata.getUrl() );
268 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
270 // FIXME: decide how to treat these in the content repo
271 if ( versionMetadata.getScm() != null )
273 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
274 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
275 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
277 if ( versionMetadata.getCiManagement() != null )
279 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
280 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
282 if ( versionMetadata.getIssueManagement() != null )
284 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
285 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
287 if ( versionMetadata.getOrganization() != null )
289 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
290 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
293 for ( License license : versionMetadata.getLicenses() )
295 versionNode.setProperty( "license." + i + ".name", license.getName() );
296 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
300 for ( MailingList mailingList : versionMetadata.getMailingLists() )
302 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
303 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
304 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
305 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
306 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
307 versionNode.setProperty( "mailingList." + i + ".otherArchives",
308 join( mailingList.getOtherArchives() ) );
312 if ( !versionMetadata.getDependencies().isEmpty() )
314 Node dependenciesNode = JcrUtils.getOrAddNode( versionNode, "dependencies" );
316 for ( Dependency dependency : versionMetadata.getDependencies() )
318 // Note that we deliberately don't alter the namespace path - not enough dependencies for
319 // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each
320 // component of the ID as that creates extra depth and causes a great cost in space and memory
322 // FIXME: change group ID to namespace
323 // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now.
324 // This won't support types where the extension doesn't match the type.
325 // (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM)
327 dependency.getGroupId() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion();
328 if ( dependency.getClassifier() != null )
330 id += "-" + dependency.getClassifier();
332 id += "." + dependency.getType();
334 Node n = JcrUtils.getOrAddNode( dependenciesNode, id, DEPENDENCY_NODE_TYPE );
335 n.setProperty( "id", id );
337 // FIXME: remove temp code just to make it keep working
338 n.setProperty( "groupId", dependency.getGroupId() );
339 n.setProperty( "artifactId", dependency.getArtifactId() );
340 n.setProperty( "version", dependency.getVersion() );
341 n.setProperty( "type", dependency.getType() );
342 n.setProperty( "classifier", dependency.getClassifier() );
343 n.setProperty( "scope", dependency.getScope() );
344 n.setProperty( "systemPath", dependency.getSystemPath() );
345 n.setProperty( "optional", dependency.isOptional() );
347 // node has no native content at this time, just facets
348 // no need to list a type as it's implied by the path. Parents are Maven specific.
350 // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet
351 // (should also have been added to the Dependency)
353 // TODO: add a property that is a weak reference to the originating artifact, creating it if
354 // necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an
355 // artifact, which gives a different meaning to "incomplete" which is a known local project
356 // that doesn't have metadata yet but has artifacts). (Though we may want to give it the
357 // artifact mixin and another property to identify all non-local artifacts for the closure
362 for ( MetadataFacet facet : versionMetadata.getFacetList() )
364 // recreate, to ensure properties are removed
365 if ( versionNode.hasNode( facet.getFacetId() ) )
367 versionNode.getNode( facet.getFacetId() ).remove();
369 Node n = versionNode.addNode( facet.getFacetId() );
370 n.addMixin( FACET_NODE_TYPE );
372 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
374 n.setProperty( entry.getKey(), entry.getValue() );
378 catch ( RepositoryException e )
380 throw new MetadataRepositoryException( e.getMessage(), e );
384 private void updateNamespace(Session jcrSession, String repositoryId, String namespace) throws MetadataRepositoryException
388 Node node = getOrAddNamespaceNode( jcrSession, repositoryId, namespace );
389 node.setProperty( "id", namespace );
390 node.setProperty( "namespace", namespace );
392 catch ( RepositoryException e )
394 throw new MetadataRepositoryException( e.getMessage(), e );
399 public void updateNamespace( RepositorySession session, String repositoryId, String namespace )
400 throws MetadataRepositoryException
402 updateNamespace( getSession(session), repositoryId, namespace );
406 public void removeProject( RepositorySession session, String repositoryId, String namespace, String projectId )
407 throws MetadataRepositoryException
409 final Session jcrSession = getSession( session );
412 Node root = jcrSession.getRootNode();
413 String namespacePath = getNamespacePath( repositoryId, namespace );
415 if ( root.hasNode( namespacePath ) )
417 Iterator<Node> nodeIterator = JcrUtils.getChildNodes( root.getNode( namespacePath ) ).iterator();
418 while ( nodeIterator.hasNext() )
420 Node node = nodeIterator.next();
421 if ( node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_NODE_TYPE ) && projectId.equals( node.getName() ) )
429 catch ( RepositoryException e )
431 throw new MetadataRepositoryException( e.getMessage(), e );
438 public boolean hasMetadataFacet( RepositorySession session, String repositoryId, String facetId )
439 throws MetadataRepositoryException
441 final Session jcrSession = getSession( session );
444 Node node = jcrSession.getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
445 return node.getNodes().hasNext();
447 catch ( PathNotFoundException e )
449 // ignored - the facet doesn't exist, so return false
452 catch ( RepositoryException e )
454 throw new MetadataRepositoryException( e.getMessage(), e );
459 public List<String> getMetadataFacets( RepositorySession session, String repositoryId, String facetId )
460 throws MetadataRepositoryException
462 final Session jcrSession = getSession( session );
463 List<String> facets = new ArrayList<>();
467 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
468 // are paths themselves
469 Node node = jcrSession.getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
471 // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
473 recurse( facets, "", node );
475 catch ( PathNotFoundException e )
477 // ignored - the facet doesn't exist, so return the empty list
479 catch ( RepositoryException e )
481 throw new MetadataRepositoryException( e.getMessage(), e );
486 private <T> Spliterator<T> createResultSpliterator(QueryResult result, Function<Row, T> converter) throws MetadataRepositoryException
488 final RowIterator rowIterator;
491 rowIterator = result.getRows();
493 catch ( RepositoryException e )
495 throw new MetadataRepositoryException( e.getMessage( ), e );
497 return new Spliterator<T>( )
500 public boolean tryAdvance( Consumer<? super T> action )
502 while (rowIterator.hasNext()) {
503 T item = converter.apply( rowIterator.nextRow() );
506 action.accept( item );
514 public Spliterator<T> trySplit( )
520 public long estimateSize( )
526 public int characteristics( )
528 return ORDERED+NONNULL;
533 private StringBuilder appendQueryParams(StringBuilder query, String selector, String defaultProperty, QueryParameter queryParameter) {
534 if (queryParameter.getSortFields().size()==0) {
535 query.append(" ORDER BY [").append(selector).append("].[").append(defaultProperty).append("]");
536 if (queryParameter.isAscending()) {
537 query.append(" ASC");
539 query.append(" DESC");
542 query.append(" ORDER BY");
543 for (String property : queryParameter.getSortFields()) {
544 query.append(" [").append(selector).append("].[").append(property).append("]");
545 if (queryParameter.isAscending()) {
546 query.append(" ASC");
548 query.append(" DESC");
556 public <T extends MetadataFacet> Stream<T> getMetadataFacetStream(RepositorySession session, String repositoryId, Class<T> facetClazz, QueryParameter queryParameter) throws MetadataRepositoryException
558 final Session jcrSession = getSession( session );
559 final MetadataFacetFactory<T> factory = metadataService.getFactory( facetClazz );
560 final String facetId = factory.getFacetId( );
561 final String facetPath = '/'+getFacetPath( repositoryId, facetId );
562 StringBuilder query = new StringBuilder("SELECT * FROM [");
563 query.append(FACET_NODE_TYPE).append("] AS facet WHERE ISDESCENDANTNODE(facet, [")
564 .append(facetPath).append("])");
565 appendQueryParams(query, "facet", "archiva:name", queryParameter);
566 String q = query.toString();
567 Map<String, String> params = new HashMap<>( );
568 QueryResult result = runNativeJcrQuery( jcrSession, q, params, queryParameter.getOffset(), queryParameter.getLimit());
569 return StreamSupport.stream( createResultSpliterator( result, (Row row)-> {
572 Node node = row.getNode( "facet" );
573 String facetName = node.getProperty( "archiva:name" ).getString();
574 return createFacetFromNode( factory, node, repositoryId, facetName );
576 catch ( RepositoryException e )
584 private void recurse( List<String> facets, String prefix, Node node )
585 throws RepositoryException
587 for ( Node n : JcrUtils.getChildNodes( node ) )
589 String name = prefix + "/" + n.getName();
592 recurse( facets, name, n );
596 // strip leading / first
597 facets.add( name.substring( 1 ) );
604 public <T extends MetadataFacet> T getMetadataFacet( RepositorySession session, String repositoryId, Class<T> clazz, String name ) throws MetadataRepositoryException
606 if (!metadataService.supportsFacet( clazz )) {
607 log.warn( "The required metadata class is not supported: " + clazz );
610 final Session jcrSession = getSession( session );
611 final MetadataFacetFactory<T> factory = getFacetFactory( clazz );
612 final String facetId = factory.getFacetId( );
615 Node root = jcrSession.getRootNode();
616 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
618 if ( getSupportedFacets().size()==0)
623 return createFacetFromNode( factory, node, repositoryId, name );
625 catch ( PathNotFoundException e )
627 // ignored - the facet doesn't exist, so return null
629 catch ( RepositoryException e )
631 throw new MetadataRepositoryException( e.getMessage(), e );
636 private <T extends MetadataFacet> T createFacetFromNode(final MetadataFacetFactory<T> factory, final Node node) throws RepositoryException {
637 return createFacetFromNode(factory, node, null, null);
640 private <T extends MetadataFacet> T createFacetFromNode(final MetadataFacetFactory<T> factory, final Node node,
641 final String repositoryId, final String name ) throws RepositoryException
643 if ( factory != null )
646 if (repositoryId!=null) {
647 metadataFacet = factory.createMetadataFacet( repositoryId, name );
649 metadataFacet = factory.createMetadataFacet();
651 Map<String, String> map = new HashMap<>();
652 for ( Property property : JcrUtils.getProperties( node ) )
654 String p = property.getName();
655 if ( !p.startsWith( "jcr:" ) )
657 map.put( p, property.getString() );
660 metadataFacet.fromProperties( map );
661 return metadataFacet;
667 public void addMetadataFacet( RepositorySession session, String repositoryId, MetadataFacet metadataFacet )
668 throws MetadataRepositoryException
670 final Session jcrSession = getSession( session );
673 Node repo = getOrAddRepositoryNode( jcrSession, repositoryId );
674 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
676 String id = metadataFacet.getFacetId();
677 Node facetNode = JcrUtils.getOrAddNode( facets, id );
679 Node facetInstance = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
680 if (!facetInstance.isNodeType( FACET_NODE_TYPE ))
682 facetInstance.addMixin( FACET_NODE_TYPE );
683 facetInstance.setProperty( "archiva:facetId", id );
684 facetInstance.setProperty( "archiva:name", metadataFacet.getName( ) );
687 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
689 facetInstance.setProperty( entry.getKey(), entry.getValue() );
693 catch ( RepositoryException | MetadataSessionException e )
695 throw new MetadataRepositoryException( e.getMessage(), e );
700 public void removeNamespace( RepositorySession session, String repositoryId, String projectId )
701 throws MetadataRepositoryException
703 final Session jcrSession = getSession( session );
706 Node root = jcrSession.getRootNode();
707 String path = getNamespacePath( repositoryId, projectId );
708 if ( root.hasNode( path ) )
710 Node node = root.getNode( path );
711 if ( node.isNodeType( NAMESPACE_NODE_TYPE ) )
717 catch ( RepositoryException e )
719 throw new MetadataRepositoryException( e.getMessage(), e );
724 public void removeMetadataFacets( RepositorySession session, String repositoryId, String facetId )
725 throws MetadataRepositoryException
727 final Session jcrSession = getSession( session );
730 Node root = jcrSession.getRootNode();
731 String path = getFacetPath( repositoryId, facetId );
732 if ( root.hasNode( path ) )
734 root.getNode( path ).remove();
737 catch ( RepositoryException e )
739 throw new MetadataRepositoryException( e.getMessage(), e );
744 public void removeMetadataFacet( RepositorySession session, String repositoryId, String facetId, String name )
745 throws MetadataRepositoryException
747 final Session jcrSession = getSession( session );
750 Node root = jcrSession.getRootNode();
751 String path = getFacetPath( repositoryId, facetId, name );
752 if ( root.hasNode( path ) )
754 Node node = root.getNode( path );
757 // also remove empty container nodes
758 Node parent = node.getParent();
762 while ( !node.hasNodes() );
765 catch ( RepositoryException e )
767 throw new MetadataRepositoryException( e.getMessage(), e );
771 private StringBuilder buildArtifactByDateRangeQuery(String repoId, ZonedDateTime startTime, ZonedDateTime endTime,
772 QueryParameter queryParameter) {
773 StringBuilder q = getArtifactQuery( repoId );
775 if ( startTime != null )
777 q.append(" AND [artifact].[whenGathered] >= $start");
779 if ( endTime != null )
781 q.append(" AND [artifact].[whenGathered] <= $end");
783 appendQueryParams(q, "artifact", "whenGathered", queryParameter);
787 private QueryResult queryArtifactByDateRange(Session jcrSession, String repositoryId,
788 ZonedDateTime startTime, ZonedDateTime endTime,
789 QueryParameter queryParameter) throws MetadataRepositoryException {
790 String q = buildArtifactByDateRangeQuery(repositoryId, startTime, endTime, queryParameter).toString();
793 Query query = jcrSession.getWorkspace().getQueryManager().createQuery(q, Query.JCR_SQL2);
794 query.setOffset(queryParameter.getOffset());
795 query.setLimit(queryParameter.getLimit());
796 ValueFactory valueFactory = jcrSession.getValueFactory();
797 if (startTime != null) {
798 query.bindValue("start", valueFactory.createValue(createCalendar(startTime.withZoneSameInstant(ModelInfo.STORAGE_TZ))));
800 if (endTime != null) {
801 query.bindValue("end", valueFactory.createValue(createCalendar(endTime.withZoneSameInstant(ModelInfo.STORAGE_TZ))));
803 return query.execute();
804 } catch (RepositoryException e) {
805 throw new MetadataRepositoryException(e.getMessage(), e);
810 public List<ArtifactMetadata> getArtifactsByDateRange(RepositorySession session, String repoId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter )
811 throws MetadataRepositoryException
813 final Session jcrSession = getSession( session );
815 List<ArtifactMetadata> artifacts;
818 QueryResult result = queryArtifactByDateRange(jcrSession, repoId, startTime, endTime, queryParameter);
820 artifacts = new ArrayList<>();
821 for ( Node n : JcrUtils.getNodes( result ) )
823 artifacts.add( getArtifactFromNode( repoId, n ) );
826 catch ( RepositoryException e )
828 throw new MetadataRepositoryException( e.getMessage(), e );
834 public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
836 final Session jcrSession = getSession( session );
837 QueryResult result = queryArtifactByDateRange(jcrSession, repositoryId, startTime, endTime, queryParameter);
838 return StreamSupport.stream(createResultSpliterator(result, (row) -> {
840 return getArtifactFromNode(repositoryId, row.getNode("artifact"));
841 } catch (RepositoryException e) {
849 public List<ArtifactMetadata> getArtifactsByChecksum(RepositorySession session, String repositoryId, String checksum )
850 throws MetadataRepositoryException
852 final Session jcrSession = getSession( session );
853 List<ArtifactMetadata> artifacts;
855 String q = getArtifactQuery( repositoryId ).append(" AND ([artifact].[checksums/*/value] = $checksum)").toString();
859 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
860 ValueFactory valueFactory = jcrSession.getValueFactory();
861 query.bindValue( "checksum", valueFactory.createValue( checksum ) );
862 QueryResult result = query.execute();
864 artifacts = new ArrayList<>();
865 for ( Node n : JcrUtils.getNodes( result ) )
867 artifacts.add( getArtifactFromNode( repositoryId, n ) );
870 catch ( RepositoryException e )
872 throw new MetadataRepositoryException( e.getMessage(), e );
877 public List<ArtifactMetadata> runJcrQuery( Session jcrSession, String repositoryId, String q, Map<String, String> bindingParam)
878 throws MetadataRepositoryException
880 return runJcrQuery( jcrSession, repositoryId, q, bindingParam, true );
883 public List<ArtifactMetadata> runJcrQuery( final Session jcrSession, final String repositoryId, final String qParam,
884 final Map<String, String> bindingParam, final boolean checkPath )
885 throws MetadataRepositoryException
889 List<ArtifactMetadata> artifacts;
890 if ( repositoryId != null && checkPath )
892 q += " AND ISDESCENDANTNODE(artifact,'/" + getRepositoryContentPath( repositoryId ) + "')";
895 log.info( "Running JCR Query: {}", q );
899 QueryResult result = runNativeJcrQuery( jcrSession, q, bindingParam );
900 artifacts = new ArrayList<>();
901 RowIterator rows = result.getRows();
902 while ( rows.hasNext() )
904 Row row = rows.nextRow();
905 Node node = row.getNode( "artifact" );
906 artifacts.add( getArtifactFromNode( repositoryId, node ) );
909 catch ( RepositoryException e )
911 throw new MetadataRepositoryException( e.getMessage(), e );
913 log.info( "Artifacts found {}", artifacts.size() );
914 for ( ArtifactMetadata meta : artifacts )
916 log.info( "Artifact: " + meta.getVersion() + " " + meta.getFacetList() );
921 public QueryResult runNativeJcrQuery( final Session jcrSession, final String q, final Map<String, String> bindingParam) throws MetadataRepositoryException
923 return runNativeJcrQuery( jcrSession, q, bindingParam, 0, Long.MAX_VALUE );
926 public QueryResult runNativeJcrQuery( final Session jcrSession, final String q, final Map<String, String> bindingParam, long offset, long maxEntries)
927 throws MetadataRepositoryException
929 Map<String, String> bindings;
930 if (bindingParam==null) {
931 bindings = new HashMap<>( );
933 bindings = bindingParam;
938 log.debug( "Query: offset={}, limit={}, query={}", offset, maxEntries, q );
939 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
940 query.setLimit( maxEntries );
941 query.setOffset( offset );
942 ValueFactory valueFactory = jcrSession.getValueFactory();
943 for ( Entry<String, String> entry : bindings.entrySet() )
945 log.debug( "Binding: {}={}", entry.getKey( ), entry.getValue( ) );
946 Value value = valueFactory.createValue( entry.getValue( ) );
947 log.debug( "Binding value {}={}", entry.getKey( ), value);
948 query.bindValue( entry.getKey(), value );
950 long start = System.currentTimeMillis( );
951 log.debug( "Execute query {}", query );
952 QueryResult result = query.execute();
953 long end = System.currentTimeMillis( );
954 log.info( "JCR Query ran in {} milliseconds: {}", end - start, q );
957 catch ( RepositoryException e )
959 throw new MetadataRepositoryException( e.getMessage(), e );
964 public List<ArtifactMetadata> getArtifactsByProjectVersionMetadata( RepositorySession session, String key, String value, String repositoryId )
965 throws MetadataRepositoryException
967 final Session jcrSession = getSession( session );
968 final String q = new StringBuilder( QUERY_ARTIFACTS_BY_PROJECT_VERSION_1 ).append( key ).append( QUERY_ARTIFACTS_BY_PROJECT_VERSION_2 ).toString();
969 return runJcrQuery( jcrSession, repositoryId, q, ImmutableMap.of( "value", value ) );
974 public List<ArtifactMetadata> getArtifactsByMetadata( RepositorySession session, String key, String value, String repositoryId )
975 throws MetadataRepositoryException
977 final Session jcrSession = getSession( session );
978 final String q = new StringBuilder( QUERY_ARTIFACTS_BY_METADATA_1 ).append( key ).append( QUERY_ARTIFACTS_BY_METADATA_2 ).toString( );
979 return runJcrQuery( jcrSession, repositoryId, q, ImmutableMap.of( "value", value ) );
984 public List<ArtifactMetadata> getArtifactsByProperty( RepositorySession session, String key, String value, String repositoryId )
985 throws MetadataRepositoryException
987 final Session jcrSession = getSession( session );
988 final String q = new StringBuilder( QUERY_ARTIFACTS_BY_PROPERTY_1 ).append( key ).append( QUERY_ARTIFACTS_BY_PROPERTY_2 ).toString();
989 return runJcrQuery( jcrSession, repositoryId, q, ImmutableMap.of( "value", value ) );
994 public void removeRepository( RepositorySession session, String repositoryId )
995 throws MetadataRepositoryException
997 final Session jcrSession = getSession( session );
1000 Node root = jcrSession.getRootNode();
1001 String path = getRepositoryPath( repositoryId );
1002 if ( root.hasNode( path ) )
1004 root.getNode( path ).remove();
1007 catch ( RepositoryException e )
1009 throw new MetadataRepositoryException( e.getMessage(), e );
1014 public List<ArtifactMetadata> getArtifacts( RepositorySession session, String repositoryId )
1015 throws MetadataRepositoryException
1017 final Session jcrSession = getSession( session );
1018 List<ArtifactMetadata> artifacts;
1020 String q = getArtifactQuery( repositoryId ).toString();
1024 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
1025 QueryResult result = query.execute();
1027 artifacts = new ArrayList<>();
1028 for ( Node n : JcrUtils.getNodes( result ) )
1030 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1032 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1036 catch ( RepositoryException e )
1038 throw new MetadataRepositoryException( e.getMessage(), e );
1043 private static StringBuilder getArtifactQuery( String repositoryId )
1045 return new StringBuilder(QUERY_ARTIFACT_1).append(getRepositoryContentPath( repositoryId )).append(QUERY_ARTIFACT_2);
1049 public ProjectMetadata getProject( RepositorySession session, String repositoryId, String namespace, String projectId )
1050 throws MetadataResolutionException
1052 final Session jcrSession;
1055 jcrSession = getSession( session );
1057 catch ( MetadataRepositoryException e )
1059 throw new MetadataResolutionException( e.getMessage() );
1061 ProjectMetadata metadata = null;
1065 Node root = jcrSession.getRootNode();
1067 // basically just checking it exists
1068 String path = getProjectPath( repositoryId, namespace, projectId );
1069 if ( root.hasNode( path ) )
1071 metadata = new ProjectMetadata();
1072 metadata.setId( projectId );
1073 metadata.setNamespace( namespace );
1076 catch ( RepositoryException e )
1078 throw new MetadataResolutionException( e.getMessage(), e );
1085 public ProjectVersionMetadata getProjectVersion( RepositorySession session, String repositoryId, String namespace, String projectId,
1086 String projectVersion )
1087 throws MetadataResolutionException
1089 final Session jcrSession;
1092 jcrSession = getSession( session );
1094 catch ( MetadataRepositoryException e )
1096 throw new MetadataResolutionException( e.getMessage() );
1098 ProjectVersionMetadata versionMetadata;
1102 Node root = jcrSession.getRootNode();
1104 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1105 if ( !root.hasNode( path ) )
1110 Node node = root.getNode( path );
1112 versionMetadata = new ProjectVersionMetadata();
1113 versionMetadata.setId( projectVersion );
1114 versionMetadata.setName( getPropertyString( node, "name" ) );
1115 versionMetadata.setDescription( getPropertyString( node, "description" ) );
1116 versionMetadata.setUrl( getPropertyString( node, "url" ) );
1117 versionMetadata.setIncomplete(
1118 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
1120 // FIXME: decide how to treat these in the content repo
1121 String scmConnection = getPropertyString( node, "scm.connection" );
1122 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
1123 String scmUrl = getPropertyString( node, "scm.url" );
1124 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
1126 Scm scm = new Scm();
1127 scm.setConnection( scmConnection );
1128 scm.setDeveloperConnection( scmDeveloperConnection );
1129 scm.setUrl( scmUrl );
1130 versionMetadata.setScm( scm );
1133 String ciSystem = getPropertyString( node, "ci.system" );
1134 String ciUrl = getPropertyString( node, "ci.url" );
1135 if ( ciSystem != null || ciUrl != null )
1137 CiManagement ci = new CiManagement();
1138 ci.setSystem( ciSystem );
1140 versionMetadata.setCiManagement( ci );
1143 String issueSystem = getPropertyString( node, "issue.system" );
1144 String issueUrl = getPropertyString( node, "issue.url" );
1145 if ( issueSystem != null || issueUrl != null )
1147 IssueManagement issueManagement = new IssueManagement();
1148 issueManagement.setSystem( issueSystem );
1149 issueManagement.setUrl( issueUrl );
1150 versionMetadata.setIssueManagement( issueManagement );
1153 String orgName = getPropertyString( node, "org.name" );
1154 String orgUrl = getPropertyString( node, "org.url" );
1155 if ( orgName != null || orgUrl != null )
1157 Organization org = new Organization();
1158 org.setName( orgName );
1159 org.setUrl( orgUrl );
1160 versionMetadata.setOrganization( org );
1163 boolean done = false;
1167 String licenseName = getPropertyString( node, "license." + i + ".name" );
1168 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
1169 if ( licenseName != null || licenseUrl != null )
1171 License license = new License();
1172 license.setName( licenseName );
1173 license.setUrl( licenseUrl );
1174 versionMetadata.addLicense( license );
1187 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
1188 if ( mailingListName != null )
1190 MailingList mailingList = new MailingList();
1191 mailingList.setName( mailingListName );
1192 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
1193 String n = "mailingList." + i + ".otherArchives";
1194 if ( node.hasProperty( n ) )
1196 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
1200 mailingList.setOtherArchives( Collections.<String>emptyList() );
1202 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
1203 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
1204 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
1205 versionMetadata.addMailingList( mailingList );
1214 if ( node.hasNode( "dependencies" ) )
1216 Node dependenciesNode = node.getNode( "dependencies" );
1217 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
1219 if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
1221 Dependency dependency = new Dependency();
1222 // FIXME: correct these properties
1223 dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
1224 dependency.setGroupId( getPropertyString( n, "groupId" ) );
1225 dependency.setClassifier( getPropertyString( n, "classifier" ) );
1226 dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
1227 dependency.setScope( getPropertyString( n, "scope" ) );
1228 dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
1229 dependency.setType( getPropertyString( n, "type" ) );
1230 dependency.setVersion( getPropertyString( n, "version" ) );
1231 versionMetadata.addDependency( dependency );
1236 retrieveFacetProperties( versionMetadata, node );
1238 catch ( RepositoryException e )
1240 throw new MetadataResolutionException( e.getMessage(), e );
1243 return versionMetadata;
1246 private void retrieveFacetProperties( FacetedMetadata metadata, Node node ) throws RepositoryException
1248 for ( Node n : JcrUtils.getChildNodes( node ) )
1250 if ( n.isNodeType( FACET_NODE_TYPE ) )
1252 String name = n.getName();
1253 MetadataFacetFactory factory = metadataService.getFactory( name );
1254 if ( factory == null )
1256 log.error( "Attempted to load unknown project version metadata facet: {}", name );
1260 MetadataFacet facet = createFacetFromNode(factory, n);
1261 metadata.addFacet( facet );
1268 public Collection<String> getArtifactVersions( RepositorySession session, String repositoryId, String namespace, String projectId,
1269 String projectVersion )
1270 throws MetadataResolutionException
1272 final Session jcrSession;
1275 jcrSession = getSession( session );
1277 catch ( MetadataRepositoryException e )
1279 throw new MetadataResolutionException( e.getMessage() );
1281 Set<String> versions = new LinkedHashSet<String>();
1285 Node root = jcrSession.getRootNode();
1287 Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
1289 for ( Node n : JcrUtils.getChildNodes( node ) )
1291 versions.add( n.getProperty( "version" ).getString() );
1294 catch ( PathNotFoundException e )
1296 // ignore repo not found for now
1298 catch ( RepositoryException e )
1300 throw new MetadataResolutionException( e.getMessage(), e );
1307 public Collection<ProjectVersionReference> getProjectReferences( RepositorySession session, String repositoryId, String namespace,
1308 String projectId, String projectVersion )
1309 throws MetadataResolutionException
1311 final Session jcrSession;
1314 jcrSession = getSession( session );
1316 catch ( MetadataRepositoryException e )
1318 throw new MetadataResolutionException( e.getMessage() );
1321 List<ProjectVersionReference> references = new ArrayList<>();
1323 // TODO: bind variables instead
1324 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId
1325 + "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
1326 if ( projectVersion != null )
1328 q += " AND [version]='" + projectVersion + "'";
1332 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
1333 QueryResult result = query.execute();
1335 for ( Node n : JcrUtils.getNodes( result ) )
1337 n = n.getParent(); // dependencies grouping element
1339 n = n.getParent(); // project version
1340 String usedByProjectVersion = n.getName();
1342 n = n.getParent(); // project
1343 String usedByProject = n.getName();
1345 n = n.getParent(); // namespace
1346 String usedByNamespace = n.getProperty( "namespace" ).getString();
1348 ProjectVersionReference ref = new ProjectVersionReference();
1349 ref.setNamespace( usedByNamespace );
1350 ref.setProjectId( usedByProject );
1351 ref.setProjectVersion( usedByProjectVersion );
1352 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
1353 references.add( ref );
1356 catch ( RepositoryException e )
1358 throw new MetadataResolutionException( e.getMessage(), e );
1365 public Collection<String> getRootNamespaces( RepositorySession session, String repositoryId )
1366 throws MetadataResolutionException
1368 return getNamespaces(session , repositoryId, null );
1372 public Collection<String> getNamespaces( RepositorySession session, String repositoryId, String baseNamespace )
1373 throws MetadataResolutionException
1375 String path = baseNamespace != null
1376 ? getNamespacePath( repositoryId, baseNamespace )
1377 : getRepositoryContentPath( repositoryId );
1381 return getNodeNames( getSession(session), path, NAMESPACE_NODE_TYPE );
1383 catch ( MetadataRepositoryException e )
1385 throw new MetadataResolutionException( e.getMessage( ) );
1390 public Collection<String> getProjects( RepositorySession session, String repositoryId, String namespace )
1391 throws MetadataResolutionException
1395 return getNodeNames( getSession(session), getNamespacePath( repositoryId, namespace ), org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_NODE_TYPE );
1397 catch ( MetadataRepositoryException e )
1399 throw new MetadataResolutionException( e.getMessage( ) );
1404 public Collection<String> getProjectVersions( RepositorySession session, String repositoryId, String namespace, String projectId )
1405 throws MetadataResolutionException
1409 return getNodeNames( getSession(session), getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
1411 catch ( MetadataRepositoryException e )
1413 throw new MetadataResolutionException( e.getMessage( ) );
1418 public void removeArtifact( RepositorySession session, ArtifactMetadata artifactMetadata, String baseVersion )
1419 throws MetadataRepositoryException
1421 final Session jcrSession = getSession( session );
1422 String repositoryId = artifactMetadata.getRepositoryId();
1426 Node root = jcrSession.getRootNode();
1428 getProjectVersionPath( repositoryId, artifactMetadata.getNamespace(), artifactMetadata.getProject(),
1431 if ( root.hasNode( path ) )
1433 Node node = root.getNode( path );
1435 for ( Node n : JcrUtils.getChildNodes( node ) )
1437 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1439 if ( n.hasProperty( "version" ) )
1441 String version = n.getProperty( "version" ).getString();
1442 if ( StringUtils.equals( version, artifactMetadata.getVersion() ) )
1452 catch ( RepositoryException e )
1454 throw new MetadataRepositoryException( e.getMessage(), e );
1462 public void removeProjectVersion( RepositorySession session, String repoId, String namespace, String projectId, String projectVersion )
1463 throws MetadataRepositoryException
1465 final Session jcrSession = getSession( session );
1469 String path = getProjectPath( repoId, namespace, projectId );
1470 Node root = jcrSession.getRootNode();
1472 Node nodeAtPath = root.getNode( path );
1474 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1476 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( projectVersion,
1483 catch ( RepositoryException e )
1485 throw new MetadataRepositoryException( e.getMessage(), e );
1490 public void removeArtifact( RepositorySession session, String repositoryId, String namespace, String projectId, String projectVersion,
1492 throws MetadataRepositoryException
1494 final Session jcrSession = getSession( session );
1497 Node root = jcrSession.getRootNode();
1498 String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
1499 if ( root.hasNode( path ) )
1501 root.getNode( path ).remove();
1506 path = getProjectPath( repositoryId, namespace, projectId );
1508 Node nodeAtPath = root.getNode( path );
1510 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1512 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) //
1513 && StringUtils.equals( node.getName(), projectVersion ) )
1519 catch ( RepositoryException e )
1521 throw new MetadataRepositoryException( e.getMessage(), e );
1526 public void removeArtifact( RepositorySession session, String repositoryId, String namespace, String project, String projectVersion,
1527 MetadataFacet metadataFacet )
1528 throws MetadataRepositoryException
1530 final Session jcrSession = getSession( session );
1533 Node root = jcrSession.getRootNode();
1534 String path = getProjectVersionPath( repositoryId, namespace, project, projectVersion );
1536 if ( root.hasNode( path ) )
1538 Node node = root.getNode( path );
1540 for ( Node n : JcrUtils.getChildNodes( node ) )
1542 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1544 ArtifactMetadata artifactMetadata = getArtifactFromNode( repositoryId, n );
1545 log.debug( "artifactMetadata: {}", artifactMetadata );
1546 MetadataFacet metadataFacetToRemove = artifactMetadata.getFacet( metadataFacet.getFacetId() );
1547 if ( metadataFacetToRemove != null && metadataFacet.equals( metadataFacetToRemove ) )
1555 catch ( RepositoryException e )
1557 throw new MetadataRepositoryException( e.getMessage(), e );
1562 public Collection<ArtifactMetadata> getArtifacts( RepositorySession session, String repositoryId, String namespace, String projectId,
1563 String projectVersion )
1564 throws MetadataResolutionException
1566 final Session jcrSession;
1569 jcrSession = getSession( session );
1571 catch ( MetadataRepositoryException e )
1573 throw new MetadataResolutionException( e.getMessage( ) );
1575 List<ArtifactMetadata> artifacts = new ArrayList<>();
1579 Node root = jcrSession.getRootNode();
1580 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1582 if ( root.hasNode( path ) )
1584 Node node = root.getNode( path );
1586 for ( Node n : JcrUtils.getChildNodes( node ) )
1588 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1590 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1595 catch ( RepositoryException e )
1597 throw new MetadataResolutionException( e.getMessage(), e );
1605 public boolean canObtainAccess( Class<?> aClass )
1607 return aClass == Session.class;
1610 @SuppressWarnings( "unchecked" )
1612 public <T> T obtainAccess( RepositorySession session, Class<T> aClass )
1613 throws MetadataRepositoryException
1615 if ( aClass == Session.class )
1617 return (T) getSession( session );
1619 throw new IllegalArgumentException(
1620 "Access using " + aClass + " is not supported on the JCR metadata storage" );
1625 throws MetadataRepositoryException
1631 * Exact is ignored as we can't do exact search in any property, we need a key
1634 public List<ArtifactMetadata> searchArtifacts( RepositorySession session, String repositoryId, String text, boolean exact )
1635 throws MetadataRepositoryException
1637 return searchArtifacts( session, repositoryId, null, text, exact );
1641 public List<ArtifactMetadata> searchArtifacts( RepositorySession session, String repositoryId, String key, String text, boolean exact )
1642 throws MetadataRepositoryException
1644 final Session jcrSession = getSession( session );
1645 String theKey = key == null ? "*" : "[" + key + "]";
1646 String projectVersionCondition =
1647 exact ? "(projectVersion." + theKey + " = $value)" : "contains([projectVersion]." + theKey + ", $value)";
1648 String facetCondition = exact ? "(facet." + theKey + " = $value)" : "contains([facet]." + theKey + ", $value)";
1649 String descendantCondition = repositoryId == null ?
1650 " AND [projectVersion].[jcr:path] LIKE '/repositories/%/content/%'" :
1651 " AND ISDESCENDANTNODE(projectVersion,'/" + getRepositoryContentPath( repositoryId ) + "')";
1652 List<ArtifactMetadata> result = new ArrayList<>( );
1653 if (key==null || (key!=null && Arrays.binarySearch( PROJECT_VERSION_VERSION_PROPERTIES, key )>=0))
1655 // We search only for project version properties if the key is a valid property name
1657 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE
1658 + "] AS projectVersion LEFT OUTER JOIN [" + ARTIFACT_NODE_TYPE
1659 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE " + projectVersionCondition + descendantCondition;
1660 result.addAll(runJcrQuery( jcrSession, repositoryId, q1, ImmutableMap.of( "value", text ), false ));
1663 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE
1664 + "] AS projectVersion LEFT OUTER JOIN [" + ARTIFACT_NODE_TYPE
1665 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) LEFT OUTER JOIN [" + FACET_NODE_TYPE
1666 + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE " + facetCondition + descendantCondition;
1667 result.addAll( runJcrQuery( jcrSession, repositoryId, q2, ImmutableMap.of( "value", text ), false ) );
1671 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1672 throws RepositoryException
1674 String id = artifactNode.getName();
1676 ArtifactMetadata artifact = new ArtifactMetadata();
1677 artifact.setId( id );
1678 artifact.setRepositoryId( repositoryId == null ? artifactNode.getAncestor( 2 ).getName() : repositoryId );
1680 Node projectVersionNode = artifactNode.getParent();
1681 Node projectNode = projectVersionNode.getParent();
1682 Node namespaceNode = projectNode.getParent();
1684 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1685 artifact.setProject( projectNode.getName() );
1686 artifact.setProjectVersion( projectVersionNode.getName() );
1687 artifact.setVersion( artifactNode.hasProperty( "version" )
1688 ? artifactNode.getProperty( "version" ).getString()
1689 : projectVersionNode.getName() );
1691 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1693 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1696 if ( artifactNode.hasProperty( "whenGathered" ) )
1698 Calendar cal = artifactNode.getProperty("whenGathered").getDate();
1699 artifact.setWhenGathered( ZonedDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId()));
1702 if ( artifactNode.hasProperty( "size" ) )
1704 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1707 Node cslistNode = getOrAddNodeByPath( artifactNode, "checksums" );
1708 NodeIterator csNodeIt = cslistNode.getNodes( "*" );
1709 while (csNodeIt.hasNext()) {
1710 Node csNode = csNodeIt.nextNode( );
1711 if (csNode.isNodeType( CHECKSUM_NODE_TYPE ))
1713 addChecksum( artifact, csNode );
1717 retrieveFacetProperties( artifact, artifactNode );
1721 private void addChecksum(ArtifactMetadata artifact, Node n) {
1724 ChecksumAlgorithm alg = ChecksumAlgorithm.valueOf( n.getProperty( "type" ).getString() );
1725 String value = n.getProperty( "value" ).getString( );
1726 artifact.setChecksum( alg, value );
1728 catch ( Throwable e )
1730 log.error( "Could not set checksum from node {}", n );
1734 private static String getPropertyString( Node node, String name )
1735 throws RepositoryException
1737 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1740 private Collection<String> getNodeNames( Session jcrSession, String path, String nodeType )
1741 throws MetadataResolutionException
1744 List<String> names = new ArrayList<>();
1748 Node root = jcrSession.getRootNode();
1750 Node nodeAtPath = root.getNode( path );
1752 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1754 if ( node.isNodeType( nodeType ) )
1756 names.add( node.getName() );
1760 catch ( PathNotFoundException e )
1762 // ignore repo not found for now
1764 catch ( RepositoryException e )
1766 throw new MetadataResolutionException( e.getMessage(), e );
1772 private static String getRepositoryPath( String repositoryId )
1774 return "repositories/" + repositoryId;
1777 private static String getRepositoryContentPath( String repositoryId )
1779 return getRepositoryPath( repositoryId ) + "/content";
1782 private static String getFacetPath( String repositoryId, String facetId )
1784 return StringUtils.isEmpty( facetId ) ? getRepositoryPath( repositoryId ) + "/facets" :
1785 getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1788 private static String getNamespacePath( String repositoryId, String namespace )
1790 return getRepositoryContentPath( repositoryId ) + "/" + namespace.replace( '.', '/' );
1793 private static String getProjectPath( String repositoryId, String namespace, String projectId )
1795 return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1798 private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1799 String projectVersion )
1801 return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1804 private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1805 String projectVersion, String id )
1807 return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1810 private Node getOrAddNodeByPath( Node baseNode, String name )
1811 throws RepositoryException
1813 return getOrAddNodeByPath( baseNode, name, null );
1816 private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1817 throws RepositoryException
1819 log.debug( "getOrAddNodeByPath " + baseNode + " " + name + " " + nodeType );
1820 Node node = baseNode;
1821 for ( String n : name.split( "/" ) )
1823 node = JcrUtils.getOrAddNode( node, n );
1824 if ( nodeType != null && !node.isNodeType( nodeType ))
1826 node.addMixin( nodeType );
1828 if (!node.hasProperty( "id" )) {
1829 node.setProperty( "id", n );
1835 private static String getFacetPath( String repositoryId, String facetId, String name )
1837 return getFacetPath( repositoryId, facetId ) + "/" + name;
1840 private Node getOrAddRepositoryNode( Session jcrSession, String repositoryId )
1841 throws RepositoryException
1843 log.debug( "getOrAddRepositoryNode " + repositoryId );
1844 Node root = jcrSession.getRootNode();
1845 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1846 log.debug( "Repositories " + node );
1847 node = JcrUtils.getOrAddNode( node, repositoryId, JcrConstants.NT_UNSTRUCTURED );
1848 if (!node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.REPOSITORY_NODE_TYPE )) {
1849 node.addMixin( org.apache.archiva.metadata.repository.jcr.JcrConstants.REPOSITORY_NODE_TYPE );
1851 if (!node.hasProperty( "id" )) {
1852 node.setProperty( "id", repositoryId );
1857 private Node getOrAddRepositoryContentNode( Session jcrSession, String repositoryId )
1858 throws RepositoryException
1860 Node node = getOrAddRepositoryNode( jcrSession, repositoryId );
1861 return JcrUtils.getOrAddNode( node, "content" );
1864 private Node getOrAddNamespaceNode( Session jcrSession, String repositoryId, String namespace )
1865 throws RepositoryException
1867 Node repo = getOrAddRepositoryContentNode( jcrSession, repositoryId );
1868 return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1871 private Node getOrAddProjectNode( Session jcrSession, String repositoryId, String namespace, String projectId )
1872 throws RepositoryException
1874 Node namespaceNode = getOrAddNamespaceNode( jcrSession, repositoryId, namespace );
1875 Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1876 if (!node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_NODE_TYPE ))
1878 node.addMixin( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_NODE_TYPE );
1880 if (!node.hasProperty( "id" ))
1882 node.setProperty( "id", projectId );
1887 private Node getOrAddProjectVersionNode( Session jcrSession, String repositoryId, String namespace, String projectId,
1888 String projectVersion )
1889 throws RepositoryException
1891 Node projectNode = getOrAddProjectNode( jcrSession, repositoryId, namespace, projectId );
1892 log.debug( "Project node {}", projectNode );
1893 Node projectVersionNode = JcrUtils.getOrAddNode( projectNode, projectVersion, JcrConstants.NT_UNSTRUCTURED);
1894 if (!projectVersionNode.isNodeType( PROJECT_VERSION_NODE_TYPE ))
1896 projectVersionNode.addMixin( PROJECT_VERSION_NODE_TYPE );
1898 if (!projectVersionNode.hasProperty( "id" ))
1900 projectVersionNode.setProperty( "id", projectVersion );
1903 log.debug( "Project version node {}", projectVersionNode );
1904 return projectVersionNode;
1907 private Node getOrAddArtifactNode( Session jcrSession, String repositoryId, String namespace, String projectId, String projectVersion,
1909 throws RepositoryException
1911 Node versionNode = getOrAddProjectVersionNode( jcrSession, repositoryId, namespace, projectId, projectVersion );
1912 Node node = JcrUtils.getOrAddNode( versionNode, id);
1913 if (!node.isNodeType( ARTIFACT_NODE_TYPE ))
1915 node.addMixin( ARTIFACT_NODE_TYPE );
1917 if (!node.hasProperty( "id" )) {
1918 node.setProperty( "id", id );
1923 private static Calendar createCalendar( ZonedDateTime time )
1925 return GregorianCalendar.from(time);
1928 private String join( Collection<String> ids )
1930 if ( ids != null && !ids.isEmpty() )
1932 StringBuilder s = new StringBuilder();
1933 for ( String id : ids )
1938 return s.substring( 0, s.length() - 1 );
1945 public void populateStatistics( RepositorySession repositorySession, MetadataRepository repository, String repositoryId,
1946 RepositoryStatistics repositoryStatistics )
1947 throws MetadataRepositoryException
1949 if ( !( repository instanceof JcrMetadataRepository ) )
1951 throw new MetadataRepositoryException(
1952 "The statistics population is only possible for JcrMetdataRepository implementations" );
1954 Session session = getSession( repositorySession );
1955 // TODO: these may be best as running totals, maintained by observations on the properties in JCR
1959 QueryManager queryManager = session.getWorkspace().getQueryManager();
1961 // TODO: Check, if this is still the case - Switched to Jackrabbit OAK with archiva 3.0
1962 // Former statement: JCR-SQL2 query will not complete on a large repo in Jackrabbit 2.2.0 - see JCR-2835
1963 // Using the JCR-SQL2 variants gives
1964 // "org.apache.lucene.search.BooleanQuery$TooManyClauses: maxClauseCount is set to 1024"
1965 // String whereClause = "WHERE ISDESCENDANTNODE([/repositories/" + repositoryId + "/content])";
1966 // Query query = queryManager.createQuery( "SELECT size FROM [archiva:artifact] " + whereClause,
1967 // Query.JCR_SQL2 );
1968 String whereClause = "WHERE ISDESCENDANTNODE([/repositories/" + repositoryId + "/content])";
1969 Query query = queryManager.createQuery( "SELECT size FROM [archiva:artifact] " + whereClause, Query.JCR_SQL2 );
1971 QueryResult queryResult = query.execute();
1973 Map<String, Integer> totalByType = new HashMap<>();
1974 long totalSize = 0, totalArtifacts = 0;
1975 for ( Row row : JcrUtils.getRows( queryResult ) )
1977 Node n = row.getNode();
1978 totalSize += row.getValue( "size" ).getLong();
1981 if ( n.hasNode( MavenArtifactFacet.FACET_ID ) )
1983 Node facetNode = n.getNode( MavenArtifactFacet.FACET_ID );
1984 type = facetNode.getProperty( "type" ).getString();
1990 Integer prev = totalByType.get( type );
1991 totalByType.put( type, prev != null ? prev + 1 : 1 );
1996 repositoryStatistics.setTotalArtifactCount( totalArtifacts );
1997 repositoryStatistics.setTotalArtifactFileSize( totalSize );
1998 for ( Map.Entry<String, Integer> entry : totalByType.entrySet() )
2000 log.info( "Setting count for type: {} = {}", entry.getKey(), entry.getValue() );
2001 repositoryStatistics.setTotalCountForType( entry.getKey(), entry.getValue() );
2004 // The query ordering is a trick to ensure that the size is correct, otherwise due to lazy init it will be -1
2005 // query = queryManager.createQuery( "SELECT * FROM [archiva:project] " + whereClause, Query.JCR_SQL2 );
2006 query = queryManager.createQuery( "SELECT * FROM [archiva:project] " + whereClause + " ORDER BY [jcr:score]",
2008 repositoryStatistics.setTotalProjectCount( query.execute().getRows().getSize() );
2010 // query = queryManager.createQuery(
2011 // "SELECT * FROM [archiva:namespace] " + whereClause + " AND namespace IS NOT NULL", Query.JCR_SQL2 );
2012 query = queryManager.createQuery(
2013 "SELECT * FROM [archiva:namespace] " + whereClause + " AND namespace IS NOT NULL ORDER BY [jcr:score]",
2015 repositoryStatistics.setTotalGroupCount( query.execute().getRows().getSize() );
2017 catch ( RepositoryException e )
2019 throw new MetadataRepositoryException( e.getMessage(), e );
2024 public Session login() throws RepositoryException
2026 return repository.login(new SimpleCredentials( "admin", "admin".toCharArray() ) );
2029 private static boolean isArtifactNodeType(Node n) {
2032 return n != null && n.isNodeType( ARTIFACT_NODE_TYPE );
2034 catch ( RepositoryException e )
2040 private Optional<ArtifactMetadata> getArtifactOptional(final String repositoryId, final Node n) {
2043 return Optional.ofNullable( getArtifactFromNode( repositoryId, n ) );
2045 catch ( RepositoryException e )
2047 return Optional.empty( );
2051 private Optional<ArtifactMetadata> getArtifactOptional(final String repositoryId, final Row row) {
2054 return Optional.ofNullable( getArtifactFromNode( repositoryId, row.getNode( "artifact" ) ) );
2056 catch ( RepositoryException e )
2058 return Optional.empty( );
2063 public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId,
2064 final String namespace, final String projectId, final String projectVersion,
2065 final QueryParameter queryParameter ) throws MetadataResolutionException
2067 final Session jcrSession;
2070 jcrSession = getSession( session );
2072 catch ( MetadataRepositoryException e )
2074 throw new MetadataResolutionException( e.getMessage( ) );
2079 Node root = jcrSession.getRootNode();
2080 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
2082 if ( root.hasNode( path ) )
2084 Node node = root.getNode( path );
2085 return StreamSupport.stream( JcrUtils.getChildNodes( node ).spliterator( ), false ).filter(JcrMetadataRepository::isArtifactNodeType)
2086 .map( n -> getArtifactOptional( repositoryId, n ) )
2087 .map( Optional::get ).skip( queryParameter.getOffset( ) ).limit( queryParameter.getLimit( ) );
2089 return Stream.empty( );
2092 catch ( RepositoryException e )
2094 throw new MetadataResolutionException( e.getMessage(), e );
2099 public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId,
2100 final QueryParameter queryParameter ) throws MetadataResolutionException
2102 final Session jcrSession;
2105 jcrSession = getSession( session );
2107 catch ( MetadataRepositoryException e )
2109 throw new MetadataResolutionException( e.getMessage( ), e );
2111 List<ArtifactMetadata> artifacts;
2113 String q = getArtifactQuery( repositoryId ).toString();
2117 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
2118 QueryResult result = query.execute();
2120 return StreamSupport.stream( createResultSpliterator( result, ( Row row ) ->
2121 getArtifactOptional( repositoryId, row ) ), false )
2123 .skip( queryParameter.getOffset( ) ).limit( queryParameter.getLimit( ) );
2126 catch ( RepositoryException | MetadataRepositoryException e )
2128 throw new MetadataResolutionException( e.getMessage(), e );