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.annotation.ParametersAreNonnullByDefault;
45 import javax.jcr.NamespaceRegistry;
46 import javax.jcr.Node;
47 import javax.jcr.NodeIterator;
48 import javax.jcr.PathNotFoundException;
49 import javax.jcr.Property;
50 import javax.jcr.Repository;
51 import javax.jcr.RepositoryException;
52 import javax.jcr.Session;
53 import javax.jcr.SimpleCredentials;
54 import javax.jcr.Value;
55 import javax.jcr.ValueFactory;
56 import javax.jcr.Workspace;
57 import javax.jcr.query.Query;
58 import javax.jcr.query.QueryManager;
59 import javax.jcr.query.QueryResult;
60 import javax.jcr.query.Row;
61 import javax.jcr.query.RowIterator;
62 import java.io.IOException;
63 import java.io.InputStreamReader;
64 import java.io.Reader;
65 import java.time.ZonedDateTime;
67 import java.util.Map.Entry;
68 import java.util.function.Consumer;
69 import java.util.function.Function;
70 import java.util.stream.Stream;
71 import java.util.stream.StreamSupport;
73 import static javax.jcr.Property.JCR_LAST_MODIFIED;
74 import static org.apache.archiva.metadata.repository.jcr.JcrConstants.*;
77 * TODO below: revise storage format for project version metadata
78 * TODO revise reference storage
80 @ParametersAreNonnullByDefault
81 public class JcrMetadataRepository
82 extends AbstractMetadataRepository implements MetadataRepository, RepositoryStatisticsProvider
86 private static final String QUERY_ARTIFACT_1 = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/";
88 static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_1 = "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE
89 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) INNER JOIN [" + FACET_NODE_TYPE
90 + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE ([facet].[";
91 static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_2= "] = $value)";
93 static final String QUERY_ARTIFACTS_BY_METADATA_1 = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact INNER JOIN [" + FACET_NODE_TYPE
94 + "] AS facet ON ISCHILDNODE(facet, artifact) WHERE ([facet].[";
95 static final String QUERY_ARTIFACTS_BY_METADATA_2 = "] = $value)";
97 static final String QUERY_ARTIFACTS_BY_PROPERTY_1 = "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE
98 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE ([projectVersion].[";
99 static final String QUERY_ARTIFACTS_BY_PROPERTY_2 = "] = $value)";
102 private static final String QUERY_ARTIFACT_2 = "')";
104 private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
106 private Repository repository;
108 public JcrMetadataRepository( MetadataService metadataService, Repository repository )
109 throws RepositoryException
111 super( metadataService );
112 this.repository = repository;
116 public static void initializeNodeTypes( 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/" );
135 Reader cndReader = new InputStreamReader(
136 Thread.currentThread( ).getContextClassLoader( ).getResourceAsStream( "org/apache/archiva/metadata/repository/jcr/jcr-schema.cnd" ) ))
138 CndImporter.registerNodeTypes( cndReader, session );
140 catch ( ParseException e )
142 e.printStackTrace( );
144 catch ( IOException e )
146 e.printStackTrace( );
151 private Session getSession(RepositorySession repositorySession) throws MetadataRepositoryException {
152 if (repositorySession instanceof JcrRepositorySession ) {
153 return ( (JcrRepositorySession) repositorySession ).getJcrSession();
155 throw new MetadataRepositoryException( "The given session object is not a JcrSession instance: " + repositorySession.getClass( ).getName( ) );
160 public void updateProject( RepositorySession session, String repositoryId, ProjectMetadata project )
161 throws MetadataRepositoryException
163 final Session jcrSession = getSession( session );
164 updateProject( jcrSession, repositoryId, project.getNamespace(), project.getId() );
167 private void updateProject( Session jcrSession, String repositoryId, String namespace, String projectId )
168 throws MetadataRepositoryException
170 updateNamespace( jcrSession , repositoryId, namespace );
174 getOrAddProjectNode( jcrSession, repositoryId, namespace, projectId );
176 catch ( RepositoryException e )
178 throw new MetadataRepositoryException( e.getMessage(), e );
183 public void updateArtifact( RepositorySession session, String repositoryId, String namespace, String projectId, String projectVersion,
184 ArtifactMetadata artifactMeta )
185 throws MetadataRepositoryException
187 final Session jcrSession = getSession( session );
188 updateNamespace( session, repositoryId, namespace );
193 getOrAddArtifactNode( jcrSession, repositoryId, namespace, projectId, projectVersion, artifactMeta.getId() );
195 node.setProperty( "id", artifactMeta.getId( ) );
196 Calendar cal = GregorianCalendar.from(artifactMeta.getFileLastModified());
197 node.setProperty( JCR_LAST_MODIFIED, cal );
199 cal = GregorianCalendar.from(artifactMeta.getWhenGathered());
200 node.setProperty( "whenGathered", cal );
202 node.setProperty( "size", artifactMeta.getSize() );
205 Node cslistNode = getOrAddNodeByPath( node, "checksums" );
206 NodeIterator nit = cslistNode.getNodes("*");
207 while (nit.hasNext()) {
208 Node csNode = nit.nextNode();
209 if (csNode.isNodeType( CHECKSUM_NODE_TYPE )) {
213 for ( Map.Entry<ChecksumAlgorithm, String> entry : artifactMeta.getChecksums().entrySet()) {
214 String type = entry.getKey( ).name( );
215 Node csNode = cslistNode.addNode( type , CHECKSUM_NODE_TYPE);
216 csNode.setProperty( "type", type );
217 csNode.setProperty( "value", entry.getValue( ) );
220 node.setProperty( "version", artifactMeta.getVersion() );
222 // iterate over available facets to update/add/remove from the artifactMetadata
223 for ( String facetId : metadataService.getSupportedFacets() )
225 MetadataFacet metadataFacet = artifactMeta.getFacet( facetId );
226 if ( metadataFacet == null )
230 if ( node.hasNode( facetId ) )
232 node.getNode( facetId ).remove();
234 if ( metadataFacet != null )
236 // recreate, to ensure properties are removed
237 Node n = node.addNode( facetId);
238 n.addMixin( FACET_NODE_TYPE );
239 n.setProperty( "facetId", facetId );
241 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
243 n.setProperty( entry.getKey(), entry.getValue() );
248 catch ( RepositoryException e )
250 throw new MetadataRepositoryException( e.getMessage(), e );
255 public void updateProjectVersion( RepositorySession session, String repositoryId, String namespace, String projectId,
256 ProjectVersionMetadata versionMetadata )
257 throws MetadataRepositoryException
259 final Session jcrSession = getSession( session );
260 updateProject( jcrSession, repositoryId, namespace, projectId );
265 getOrAddProjectVersionNode( jcrSession, repositoryId, namespace, projectId, versionMetadata.getId() );
266 versionNode.setProperty( "id", versionMetadata.getId( ) );
267 versionNode.setProperty( "name", StringUtils.isEmpty( versionMetadata.getName() ) ? "" : versionMetadata.getName() );
268 versionNode.setProperty( "description", StringUtils.isEmpty( versionMetadata.getDescription() ) ? "" : versionMetadata.getDescription() );
269 versionNode.setProperty( "url", versionMetadata.getUrl() );
270 versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
272 // FIXME: decide how to treat these in the content repo
273 if ( versionMetadata.getScm() != null )
275 versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
276 versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
277 versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
279 if ( versionMetadata.getCiManagement() != null )
281 versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
282 versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
284 if ( versionMetadata.getIssueManagement() != null )
286 versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
287 versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
289 if ( versionMetadata.getOrganization() != null )
291 versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
292 versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
295 for ( License license : versionMetadata.getLicenses() )
297 versionNode.setProperty( "license." + i + ".name", license.getName() );
298 versionNode.setProperty( "license." + i + ".url", license.getUrl() );
302 for ( MailingList mailingList : versionMetadata.getMailingLists() )
304 versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
305 versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
306 versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
307 versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
308 versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
309 versionNode.setProperty( "mailingList." + i + ".otherArchives",
310 join( mailingList.getOtherArchives() ) );
314 if ( !versionMetadata.getDependencies().isEmpty() )
316 Node dependenciesNode = JcrUtils.getOrAddNode( versionNode, "dependencies" );
318 for ( Dependency dependency : versionMetadata.getDependencies() )
320 // Note that we deliberately don't alter the namespace path - not enough dependencies for
321 // number of nodes at a given depth to be an issue. Similarly, we don't add subnodes for each
322 // component of the ID as that creates extra depth and causes a great cost in space and memory
324 // FIXME: change group ID to namespace
325 // FIXME: change to artifact's ID - this is constructed by the Maven 2 format for now.
326 // This won't support types where the extension doesn't match the type.
327 // (see also Maven2RepositoryStorage#readProjectVersionMetadata construction of POM)
329 dependency.getGroupId() + ";" + dependency.getArtifactId() + "-" + dependency.getVersion();
330 if ( dependency.getClassifier() != null )
332 id += "-" + dependency.getClassifier();
334 id += "." + dependency.getType();
336 Node n = JcrUtils.getOrAddNode( dependenciesNode, id, DEPENDENCY_NODE_TYPE );
337 n.setProperty( "id", id );
339 // FIXME: remove temp code just to make it keep working
340 n.setProperty( "groupId", dependency.getGroupId() );
341 n.setProperty( "artifactId", dependency.getArtifactId() );
342 n.setProperty( "version", dependency.getVersion() );
343 n.setProperty( "type", dependency.getType() );
344 n.setProperty( "classifier", dependency.getClassifier() );
345 n.setProperty( "scope", dependency.getScope() );
346 n.setProperty( "systemPath", dependency.getSystemPath() );
347 n.setProperty( "optional", dependency.isOptional() );
349 // node has no native content at this time, just facets
350 // no need to list a type as it's implied by the path. Parents are Maven specific.
352 // FIXME: add scope, systemPath, type, version, classifier & maven2 specific IDs as a facet
353 // (should also have been added to the Dependency)
355 // TODO: add a property that is a weak reference to the originating artifact, creating it if
356 // necessary (without adding the archiva:artifact mixin so that it doesn't get listed as an
357 // artifact, which gives a different meaning to "incomplete" which is a known local project
358 // that doesn't have metadata yet but has artifacts). (Though we may want to give it the
359 // artifact mixin and another property to identify all non-local artifacts for the closure
364 for ( MetadataFacet facet : versionMetadata.getFacetList() )
366 // recreate, to ensure properties are removed
367 if ( versionNode.hasNode( facet.getFacetId() ) )
369 versionNode.getNode( facet.getFacetId() ).remove();
371 Node n = versionNode.addNode( facet.getFacetId() );
372 n.addMixin( FACET_NODE_TYPE );
374 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
376 n.setProperty( entry.getKey(), entry.getValue() );
380 catch ( RepositoryException e )
382 throw new MetadataRepositoryException( e.getMessage(), e );
386 private void updateNamespace(Session jcrSession, String repositoryId, String namespace) throws MetadataRepositoryException
390 Node node = getOrAddNamespaceNode( jcrSession, repositoryId, namespace );
391 node.setProperty( "id", namespace );
392 node.setProperty( "namespace", namespace );
394 catch ( RepositoryException e )
396 throw new MetadataRepositoryException( e.getMessage(), e );
401 public void updateNamespace( RepositorySession session, String repositoryId, String namespace )
402 throws MetadataRepositoryException
404 updateNamespace( getSession(session), repositoryId, namespace );
408 public void removeProject( RepositorySession session, String repositoryId, String namespace, String projectId )
409 throws MetadataRepositoryException
411 final Session jcrSession = getSession( session );
414 Node root = jcrSession.getRootNode();
415 String namespacePath = getNamespacePath( repositoryId, namespace );
417 if ( root.hasNode( namespacePath ) )
419 Iterator<Node> nodeIterator = JcrUtils.getChildNodes( root.getNode( namespacePath ) ).iterator();
420 while ( nodeIterator.hasNext() )
422 Node node = nodeIterator.next();
423 if ( node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_NODE_TYPE ) && projectId.equals( node.getName() ) )
431 catch ( RepositoryException e )
433 throw new MetadataRepositoryException( e.getMessage(), e );
440 public boolean hasMetadataFacet( RepositorySession session, String repositoryId, String facetId )
441 throws MetadataRepositoryException
443 final Session jcrSession = getSession( session );
446 Node node = jcrSession.getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
447 return node.getNodes().hasNext();
449 catch ( PathNotFoundException e )
451 // ignored - the facet doesn't exist, so return false
454 catch ( RepositoryException e )
456 throw new MetadataRepositoryException( e.getMessage(), e );
461 public List<String> getMetadataFacets( RepositorySession session, String repositoryId, String facetId )
462 throws MetadataRepositoryException
464 final Session jcrSession = getSession( session );
465 List<String> facets = new ArrayList<>();
469 // no need to construct node-by-node here, as we'll find in the next instance, the facet names have / and
470 // are paths themselves
471 Node node = jcrSession.getRootNode().getNode( getFacetPath( repositoryId, facetId ) );
473 // TODO: this is a bit awkward. Might be better to review the purpose of this function - why is the list of
475 recurse( facets, "", node );
477 catch ( PathNotFoundException e )
479 // ignored - the facet doesn't exist, so return the empty list
481 catch ( RepositoryException e )
483 throw new MetadataRepositoryException( e.getMessage(), e );
488 private <T> Spliterator<T> createResultSpliterator(QueryResult result, Function<Row, T> converter) throws MetadataRepositoryException
490 final RowIterator rowIterator;
493 rowIterator = result.getRows();
495 catch ( RepositoryException e )
497 throw new MetadataRepositoryException( e.getMessage( ), e );
499 return new Spliterator<T>( )
502 public boolean tryAdvance( Consumer<? super T> action )
504 while (rowIterator.hasNext()) {
505 T item = converter.apply( rowIterator.nextRow() );
508 action.accept( item );
516 public Spliterator<T> trySplit( )
522 public long estimateSize( )
528 public int characteristics( )
530 return ORDERED+NONNULL;
535 private StringBuilder appendQueryParams(StringBuilder query, String selector, String defaultProperty, QueryParameter queryParameter) {
536 if (queryParameter.getSortFields().size()==0) {
537 query.append(" ORDER BY [").append(selector).append("].[").append(defaultProperty).append("]");
538 if (queryParameter.isAscending()) {
539 query.append(" ASC");
541 query.append(" DESC");
544 query.append(" ORDER BY");
545 for (String property : queryParameter.getSortFields()) {
546 query.append(" [").append(selector).append("].[").append(property).append("]");
547 if (queryParameter.isAscending()) {
548 query.append(" ASC");
550 query.append(" DESC");
558 public <T extends MetadataFacet> Stream<T> getMetadataFacetStream(RepositorySession session, String repositoryId, Class<T> facetClazz, QueryParameter queryParameter) throws MetadataRepositoryException
560 final Session jcrSession = getSession( session );
561 final MetadataFacetFactory<T> factory = metadataService.getFactory( facetClazz );
562 final String facetId = factory.getFacetId( );
563 final String facetPath = '/'+getFacetPath( repositoryId, facetId );
564 StringBuilder query = new StringBuilder("SELECT * FROM [");
565 query.append(FACET_NODE_TYPE).append("] AS facet WHERE ISDESCENDANTNODE(facet, [")
566 .append(facetPath).append("])");
567 appendQueryParams(query, "facet", "archiva:name", queryParameter);
568 String q = query.toString();
569 Map<String, String> params = new HashMap<>( );
570 QueryResult result = runNativeJcrQuery( jcrSession, q, params, queryParameter.getOffset(), queryParameter.getLimit());
571 return StreamSupport.stream( createResultSpliterator( result, (Row row)-> {
574 Node node = row.getNode( "facet" );
575 String facetName = node.getProperty( "archiva:name" ).getString();
576 return createFacetFromNode( factory, node, repositoryId, facetName );
578 catch ( RepositoryException e )
586 private void recurse( List<String> facets, String prefix, Node node )
587 throws RepositoryException
589 for ( Node n : JcrUtils.getChildNodes( node ) )
591 String name = prefix + "/" + n.getName();
594 recurse( facets, name, n );
598 // strip leading / first
599 facets.add( name.substring( 1 ) );
606 public <T extends MetadataFacet> T getMetadataFacet( RepositorySession session, String repositoryId, Class<T> clazz, String name ) throws MetadataRepositoryException
608 if (!metadataService.supportsFacet( clazz )) {
609 log.warn( "The required metadata class is not supported: " + clazz );
612 final Session jcrSession = getSession( session );
613 final MetadataFacetFactory<T> factory = getFacetFactory( clazz );
614 final String facetId = factory.getFacetId( );
617 Node root = jcrSession.getRootNode();
618 Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
620 if ( getSupportedFacets().size()==0)
625 return createFacetFromNode( factory, node, repositoryId, name );
627 catch ( PathNotFoundException e )
629 // ignored - the facet doesn't exist, so return null
631 catch ( RepositoryException e )
633 throw new MetadataRepositoryException( e.getMessage(), e );
638 private <T extends MetadataFacet> T createFacetFromNode(final MetadataFacetFactory<T> factory, final Node node) throws RepositoryException {
639 return createFacetFromNode(factory, node, null, null);
642 private <T extends MetadataFacet> T createFacetFromNode(final MetadataFacetFactory<T> factory, final Node node,
643 final String repositoryId, final String name ) throws RepositoryException
645 if ( factory != null )
648 if (repositoryId!=null) {
649 metadataFacet = factory.createMetadataFacet( repositoryId, name );
651 metadataFacet = factory.createMetadataFacet();
653 Map<String, String> map = new HashMap<>();
654 for ( Property property : JcrUtils.getProperties( node ) )
656 String p = property.getName();
657 if ( !p.startsWith( "jcr:" ) )
659 map.put( p, property.getString() );
662 metadataFacet.fromProperties( map );
663 return metadataFacet;
669 public void addMetadataFacet( RepositorySession session, String repositoryId, MetadataFacet metadataFacet )
670 throws MetadataRepositoryException
672 final Session jcrSession = getSession( session );
675 Node repo = getOrAddRepositoryNode( jcrSession, repositoryId );
676 Node facets = JcrUtils.getOrAddNode( repo, "facets" );
678 String id = metadataFacet.getFacetId();
679 Node facetNode = JcrUtils.getOrAddNode( facets, id );
681 Node facetInstance = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
682 if (!facetInstance.isNodeType( FACET_NODE_TYPE ))
684 facetInstance.addMixin( FACET_NODE_TYPE );
685 facetInstance.setProperty( "archiva:facetId", id );
686 facetInstance.setProperty( "archiva:name", metadataFacet.getName( ) );
689 for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
691 facetInstance.setProperty( entry.getKey(), entry.getValue() );
695 catch ( RepositoryException | MetadataSessionException e )
697 throw new MetadataRepositoryException( e.getMessage(), e );
702 public void removeNamespace( RepositorySession session, String repositoryId, String projectId )
703 throws MetadataRepositoryException
705 final Session jcrSession = getSession( session );
708 Node root = jcrSession.getRootNode();
709 String path = getNamespacePath( repositoryId, projectId );
710 if ( root.hasNode( path ) )
712 Node node = root.getNode( path );
713 if ( node.isNodeType( NAMESPACE_NODE_TYPE ) )
719 catch ( RepositoryException e )
721 throw new MetadataRepositoryException( e.getMessage(), e );
726 public void removeMetadataFacets( RepositorySession session, String repositoryId, String facetId )
727 throws MetadataRepositoryException
729 final Session jcrSession = getSession( session );
732 Node root = jcrSession.getRootNode();
733 String path = getFacetPath( repositoryId, facetId );
734 if ( root.hasNode( path ) )
736 root.getNode( path ).remove();
739 catch ( RepositoryException e )
741 throw new MetadataRepositoryException( e.getMessage(), e );
746 public void removeMetadataFacet( RepositorySession session, String repositoryId, String facetId, String name )
747 throws MetadataRepositoryException
749 final Session jcrSession = getSession( session );
752 Node root = jcrSession.getRootNode();
753 String path = getFacetPath( repositoryId, facetId, name );
754 if ( root.hasNode( path ) )
756 Node node = root.getNode( path );
759 // also remove empty container nodes
760 Node parent = node.getParent();
764 while ( !node.hasNodes() );
767 catch ( RepositoryException e )
769 throw new MetadataRepositoryException( e.getMessage(), e );
773 private StringBuilder buildArtifactByDateRangeQuery(String repoId, ZonedDateTime startTime, ZonedDateTime endTime,
774 QueryParameter queryParameter) {
775 StringBuilder q = getArtifactQuery( repoId );
777 if ( startTime != null )
779 q.append(" AND [artifact].[whenGathered] >= $start");
781 if ( endTime != null )
783 q.append(" AND [artifact].[whenGathered] <= $end");
785 appendQueryParams(q, "artifact", "whenGathered", queryParameter);
789 private QueryResult queryArtifactByDateRange(Session jcrSession, String repositoryId,
790 ZonedDateTime startTime, ZonedDateTime endTime,
791 QueryParameter queryParameter) throws MetadataRepositoryException {
792 String q = buildArtifactByDateRangeQuery(repositoryId, startTime, endTime, queryParameter).toString();
795 Query query = jcrSession.getWorkspace().getQueryManager().createQuery(q, Query.JCR_SQL2);
796 query.setOffset(queryParameter.getOffset());
797 query.setLimit(queryParameter.getLimit());
798 ValueFactory valueFactory = jcrSession.getValueFactory();
799 if (startTime != null) {
800 query.bindValue("start", valueFactory.createValue(createCalendar(startTime.withZoneSameInstant(ModelInfo.STORAGE_TZ))));
802 if (endTime != null) {
803 query.bindValue("end", valueFactory.createValue(createCalendar(endTime.withZoneSameInstant(ModelInfo.STORAGE_TZ))));
805 return query.execute();
806 } catch (RepositoryException e) {
807 throw new MetadataRepositoryException(e.getMessage(), e);
812 public List<ArtifactMetadata> getArtifactsByDateRange(RepositorySession session, String repoId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter )
813 throws MetadataRepositoryException
815 final Session jcrSession = getSession( session );
817 List<ArtifactMetadata> artifacts;
820 QueryResult result = queryArtifactByDateRange(jcrSession, repoId, startTime, endTime, queryParameter);
822 artifacts = new ArrayList<>();
823 for ( Node n : JcrUtils.getNodes( result ) )
825 artifacts.add( getArtifactFromNode( repoId, n ) );
828 catch ( RepositoryException e )
830 throw new MetadataRepositoryException( e.getMessage(), e );
836 public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
838 final Session jcrSession = getSession( session );
839 QueryResult result = queryArtifactByDateRange(jcrSession, repositoryId, startTime, endTime, queryParameter);
840 return StreamSupport.stream(createResultSpliterator(result, (row) -> {
842 return getArtifactFromNode(repositoryId, row.getNode("artifact"));
843 } catch (RepositoryException e) {
851 public List<ArtifactMetadata> getArtifactsByChecksum(RepositorySession session, String repositoryId, String checksum )
852 throws MetadataRepositoryException
854 final Session jcrSession = getSession( session );
855 List<ArtifactMetadata> artifacts;
857 String q = getArtifactQuery( repositoryId ).append(" AND ([artifact].[checksums/*/value] = $checksum)").toString();
861 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
862 ValueFactory valueFactory = jcrSession.getValueFactory();
863 query.bindValue( "checksum", valueFactory.createValue( checksum ) );
864 QueryResult result = query.execute();
866 artifacts = new ArrayList<>();
867 for ( Node n : JcrUtils.getNodes( result ) )
869 artifacts.add( getArtifactFromNode( repositoryId, n ) );
872 catch ( RepositoryException e )
874 throw new MetadataRepositoryException( e.getMessage(), e );
879 public List<ArtifactMetadata> runJcrQuery( Session jcrSession, String repositoryId, String q, Map<String, String> bindingParam)
880 throws MetadataRepositoryException
882 return runJcrQuery( jcrSession, repositoryId, q, bindingParam, true );
885 public List<ArtifactMetadata> runJcrQuery( final Session jcrSession, final String repositoryId, final String qParam,
886 final Map<String, String> bindingParam, final boolean checkPath )
887 throws MetadataRepositoryException
891 List<ArtifactMetadata> artifacts;
892 if ( repositoryId != null && checkPath )
894 q += " AND ISDESCENDANTNODE(artifact,'/" + getRepositoryContentPath( repositoryId ) + "')";
897 log.info( "Running JCR Query: {}", q );
901 QueryResult result = runNativeJcrQuery( jcrSession, q, bindingParam );
902 artifacts = new ArrayList<>();
903 RowIterator rows = result.getRows();
904 while ( rows.hasNext() )
906 Row row = rows.nextRow();
907 Node node = row.getNode( "artifact" );
908 artifacts.add( getArtifactFromNode( repositoryId, node ) );
911 catch ( RepositoryException e )
913 throw new MetadataRepositoryException( e.getMessage(), e );
915 log.info( "Artifacts found {}", artifacts.size() );
916 for ( ArtifactMetadata meta : artifacts )
918 log.info( "Artifact: " + meta.getVersion() + " " + meta.getFacetList() );
923 public QueryResult runNativeJcrQuery( final Session jcrSession, final String q, final Map<String, String> bindingParam) throws MetadataRepositoryException
925 return runNativeJcrQuery( jcrSession, q, bindingParam, 0, Long.MAX_VALUE );
928 public QueryResult runNativeJcrQuery( final Session jcrSession, final String q, final Map<String, String> bindingParam, long offset, long maxEntries)
929 throws MetadataRepositoryException
931 Map<String, String> bindings;
932 if (bindingParam==null) {
933 bindings = new HashMap<>( );
935 bindings = bindingParam;
940 log.debug( "Query: offset={}, limit={}, query={}", offset, maxEntries, q );
941 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
942 query.setLimit( maxEntries );
943 query.setOffset( offset );
944 ValueFactory valueFactory = jcrSession.getValueFactory();
945 for ( Entry<String, String> entry : bindings.entrySet() )
947 log.debug( "Binding: {}={}", entry.getKey( ), entry.getValue( ) );
948 Value value = valueFactory.createValue( entry.getValue( ) );
949 log.debug( "Binding value {}={}", entry.getKey( ), value);
950 query.bindValue( entry.getKey(), value );
952 long start = System.currentTimeMillis( );
953 log.debug( "Execute query {}", query );
954 QueryResult result = query.execute();
955 long end = System.currentTimeMillis( );
956 log.info( "JCR Query ran in {} milliseconds: {}", end - start, q );
959 catch ( RepositoryException e )
961 throw new MetadataRepositoryException( e.getMessage(), e );
966 public List<ArtifactMetadata> getArtifactsByProjectVersionFacet( RepositorySession session, String key, String value, String repositoryId )
967 throws MetadataRepositoryException
969 final Session jcrSession = getSession( session );
970 final String q = new StringBuilder( QUERY_ARTIFACTS_BY_PROJECT_VERSION_1 ).append( key ).append( QUERY_ARTIFACTS_BY_PROJECT_VERSION_2 ).toString();
971 return runJcrQuery( jcrSession, repositoryId, q, ImmutableMap.of( "value", value ) );
976 public List<ArtifactMetadata> getArtifactsByAttribute( RepositorySession session, String key, String value, String repositoryId )
977 throws MetadataRepositoryException
979 final Session jcrSession = getSession( session );
980 final String q = new StringBuilder( QUERY_ARTIFACTS_BY_METADATA_1 ).append( key ).append( QUERY_ARTIFACTS_BY_METADATA_2 ).toString( );
981 return runJcrQuery( jcrSession, repositoryId, q, ImmutableMap.of( "value", value ) );
986 public List<ArtifactMetadata> getArtifactsByProjectVersionAttribute( RepositorySession session, String key, String value, String repositoryId )
987 throws MetadataRepositoryException
989 final Session jcrSession = getSession( session );
990 final String q = new StringBuilder( QUERY_ARTIFACTS_BY_PROPERTY_1 ).append( key ).append( QUERY_ARTIFACTS_BY_PROPERTY_2 ).toString();
991 return runJcrQuery( jcrSession, repositoryId, q, ImmutableMap.of( "value", value ) );
996 public void removeRepository( RepositorySession session, String repositoryId )
997 throws MetadataRepositoryException
999 final Session jcrSession = getSession( session );
1002 Node root = jcrSession.getRootNode();
1003 String path = getRepositoryPath( repositoryId );
1004 if ( root.hasNode( path ) )
1006 root.getNode( path ).remove();
1009 catch ( RepositoryException e )
1011 throw new MetadataRepositoryException( e.getMessage(), e );
1016 public List<ArtifactMetadata> getArtifacts( RepositorySession session, String repositoryId )
1017 throws MetadataRepositoryException
1019 final Session jcrSession = getSession( session );
1020 List<ArtifactMetadata> artifacts;
1022 String q = getArtifactQuery( repositoryId ).toString();
1026 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
1027 QueryResult result = query.execute();
1029 artifacts = new ArrayList<>();
1030 for ( Node n : JcrUtils.getNodes( result ) )
1032 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1034 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1038 catch ( RepositoryException e )
1040 throw new MetadataRepositoryException( e.getMessage(), e );
1045 private static StringBuilder getArtifactQuery( String repositoryId )
1047 return new StringBuilder(QUERY_ARTIFACT_1).append(getRepositoryContentPath( repositoryId )).append(QUERY_ARTIFACT_2);
1051 public ProjectMetadata getProject( RepositorySession session, String repositoryId, String namespace, String projectId )
1052 throws MetadataResolutionException
1054 final Session jcrSession;
1057 jcrSession = getSession( session );
1059 catch ( MetadataRepositoryException e )
1061 throw new MetadataResolutionException( e.getMessage() );
1063 ProjectMetadata metadata = null;
1067 Node root = jcrSession.getRootNode();
1069 // basically just checking it exists
1070 String path = getProjectPath( repositoryId, namespace, projectId );
1071 if ( root.hasNode( path ) )
1073 metadata = new ProjectMetadata();
1074 metadata.setId( projectId );
1075 metadata.setNamespace( namespace );
1078 catch ( RepositoryException e )
1080 throw new MetadataResolutionException( e.getMessage(), e );
1087 public ProjectVersionMetadata getProjectVersion( RepositorySession session, String repositoryId, String namespace, String projectId,
1088 String projectVersion )
1089 throws MetadataResolutionException
1091 final Session jcrSession;
1094 jcrSession = getSession( session );
1096 catch ( MetadataRepositoryException e )
1098 throw new MetadataResolutionException( e.getMessage() );
1100 ProjectVersionMetadata versionMetadata;
1104 Node root = jcrSession.getRootNode();
1106 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1107 if ( !root.hasNode( path ) )
1112 Node node = root.getNode( path );
1114 versionMetadata = new ProjectVersionMetadata();
1115 versionMetadata.setId( projectVersion );
1116 versionMetadata.setName( getPropertyString( node, "name" ) );
1117 versionMetadata.setDescription( getPropertyString( node, "description" ) );
1118 versionMetadata.setUrl( getPropertyString( node, "url" ) );
1119 versionMetadata.setIncomplete(
1120 node.hasProperty( "incomplete" ) && node.getProperty( "incomplete" ).getBoolean() );
1122 // FIXME: decide how to treat these in the content repo
1123 String scmConnection = getPropertyString( node, "scm.connection" );
1124 String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
1125 String scmUrl = getPropertyString( node, "scm.url" );
1126 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
1128 Scm scm = new Scm();
1129 scm.setConnection( scmConnection );
1130 scm.setDeveloperConnection( scmDeveloperConnection );
1131 scm.setUrl( scmUrl );
1132 versionMetadata.setScm( scm );
1135 String ciSystem = getPropertyString( node, "ci.system" );
1136 String ciUrl = getPropertyString( node, "ci.url" );
1137 if ( ciSystem != null || ciUrl != null )
1139 CiManagement ci = new CiManagement();
1140 ci.setSystem( ciSystem );
1142 versionMetadata.setCiManagement( ci );
1145 String issueSystem = getPropertyString( node, "issue.system" );
1146 String issueUrl = getPropertyString( node, "issue.url" );
1147 if ( issueSystem != null || issueUrl != null )
1149 IssueManagement issueManagement = new IssueManagement();
1150 issueManagement.setSystem( issueSystem );
1151 issueManagement.setUrl( issueUrl );
1152 versionMetadata.setIssueManagement( issueManagement );
1155 String orgName = getPropertyString( node, "org.name" );
1156 String orgUrl = getPropertyString( node, "org.url" );
1157 if ( orgName != null || orgUrl != null )
1159 Organization org = new Organization();
1160 org.setName( orgName );
1161 org.setUrl( orgUrl );
1162 versionMetadata.setOrganization( org );
1165 boolean done = false;
1169 String licenseName = getPropertyString( node, "license." + i + ".name" );
1170 String licenseUrl = getPropertyString( node, "license." + i + ".url" );
1171 if ( licenseName != null || licenseUrl != null )
1173 License license = new License();
1174 license.setName( licenseName );
1175 license.setUrl( licenseUrl );
1176 versionMetadata.addLicense( license );
1189 String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
1190 if ( mailingListName != null )
1192 MailingList mailingList = new MailingList();
1193 mailingList.setName( mailingListName );
1194 mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
1195 String n = "mailingList." + i + ".otherArchives";
1196 if ( node.hasProperty( n ) )
1198 mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
1202 mailingList.setOtherArchives( Collections.<String>emptyList() );
1204 mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
1205 mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
1206 mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
1207 versionMetadata.addMailingList( mailingList );
1216 if ( node.hasNode( "dependencies" ) )
1218 Node dependenciesNode = node.getNode( "dependencies" );
1219 for ( Node n : JcrUtils.getChildNodes( dependenciesNode ) )
1221 if ( n.isNodeType( DEPENDENCY_NODE_TYPE ) )
1223 Dependency dependency = new Dependency();
1224 // FIXME: correct these properties
1225 dependency.setArtifactId( getPropertyString( n, "artifactId" ) );
1226 dependency.setGroupId( getPropertyString( n, "groupId" ) );
1227 dependency.setClassifier( getPropertyString( n, "classifier" ) );
1228 dependency.setOptional( Boolean.valueOf( getPropertyString( n, "optional" ) ) );
1229 dependency.setScope( getPropertyString( n, "scope" ) );
1230 dependency.setSystemPath( getPropertyString( n, "systemPath" ) );
1231 dependency.setType( getPropertyString( n, "type" ) );
1232 dependency.setVersion( getPropertyString( n, "version" ) );
1233 versionMetadata.addDependency( dependency );
1238 retrieveFacetProperties( versionMetadata, node );
1240 catch ( RepositoryException e )
1242 throw new MetadataResolutionException( e.getMessage(), e );
1245 return versionMetadata;
1248 private void retrieveFacetProperties( FacetedMetadata metadata, Node node ) throws RepositoryException
1250 for ( Node n : JcrUtils.getChildNodes( node ) )
1252 if ( n.isNodeType( FACET_NODE_TYPE ) )
1254 String name = n.getName();
1255 MetadataFacetFactory factory = metadataService.getFactory( name );
1256 if ( factory == null )
1258 log.error( "Attempted to load unknown project version metadata facet: {}", name );
1262 MetadataFacet facet = createFacetFromNode(factory, n);
1263 metadata.addFacet( facet );
1270 public Collection<String> getArtifactVersions( RepositorySession session, String repositoryId, String namespace, String projectId,
1271 String projectVersion )
1272 throws MetadataResolutionException
1274 final Session jcrSession;
1277 jcrSession = getSession( session );
1279 catch ( MetadataRepositoryException e )
1281 throw new MetadataResolutionException( e.getMessage() );
1283 Set<String> versions = new LinkedHashSet<String>();
1287 Node root = jcrSession.getRootNode();
1289 Node node = root.getNode( getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) );
1291 for ( Node n : JcrUtils.getChildNodes( node ) )
1293 versions.add( n.getProperty( "version" ).getString() );
1296 catch ( PathNotFoundException e )
1298 // ignore repo not found for now
1300 catch ( RepositoryException e )
1302 throw new MetadataResolutionException( e.getMessage(), e );
1309 public Collection<ProjectVersionReference> getProjectReferences( RepositorySession session, String repositoryId, String namespace,
1310 String projectId, String projectVersion )
1311 throws MetadataResolutionException
1313 final Session jcrSession;
1316 jcrSession = getSession( session );
1318 catch ( MetadataRepositoryException e )
1320 throw new MetadataResolutionException( e.getMessage() );
1323 List<ProjectVersionReference> references = new ArrayList<>();
1325 // TODO: bind variables instead
1326 String q = "SELECT * FROM [archiva:dependency] WHERE ISDESCENDANTNODE([/repositories/" + repositoryId
1327 + "/content]) AND [groupId]='" + namespace + "' AND [artifactId]='" + projectId + "'";
1328 if ( projectVersion != null )
1330 q += " AND [version]='" + projectVersion + "'";
1334 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
1335 QueryResult result = query.execute();
1337 for ( Node n : JcrUtils.getNodes( result ) )
1339 n = n.getParent(); // dependencies grouping element
1341 n = n.getParent(); // project version
1342 String usedByProjectVersion = n.getName();
1344 n = n.getParent(); // project
1345 String usedByProject = n.getName();
1347 n = n.getParent(); // namespace
1348 String usedByNamespace = n.getProperty( "namespace" ).getString();
1350 ProjectVersionReference ref = new ProjectVersionReference();
1351 ref.setNamespace( usedByNamespace );
1352 ref.setProjectId( usedByProject );
1353 ref.setProjectVersion( usedByProjectVersion );
1354 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
1355 references.add( ref );
1358 catch ( RepositoryException e )
1360 throw new MetadataResolutionException( e.getMessage(), e );
1367 public Collection<String> getRootNamespaces( RepositorySession session, String repositoryId )
1368 throws MetadataResolutionException
1370 return this.getChildNamespaces(session , repositoryId, null );
1374 public Collection<String> getChildNamespaces( RepositorySession session, String repositoryId, String baseNamespace )
1375 throws MetadataResolutionException
1377 String path = baseNamespace != null
1378 ? getNamespacePath( repositoryId, baseNamespace )
1379 : getRepositoryContentPath( repositoryId );
1383 return getNodeNames( getSession(session), path, NAMESPACE_NODE_TYPE );
1385 catch ( MetadataRepositoryException e )
1387 throw new MetadataResolutionException( e.getMessage( ) );
1392 public Collection<String> getProjects( RepositorySession session, String repositoryId, String namespace )
1393 throws MetadataResolutionException
1397 return getNodeNames( getSession(session), getNamespacePath( repositoryId, namespace ), org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_NODE_TYPE );
1399 catch ( MetadataRepositoryException e )
1401 throw new MetadataResolutionException( e.getMessage( ) );
1406 public Collection<String> getProjectVersions( RepositorySession session, String repositoryId, String namespace, String projectId )
1407 throws MetadataResolutionException
1411 return getNodeNames( getSession(session), getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
1413 catch ( MetadataRepositoryException e )
1415 throw new MetadataResolutionException( e.getMessage( ) );
1420 public void removeTimestampedArtifact( RepositorySession session, ArtifactMetadata artifactMetadata, String baseVersion )
1421 throws MetadataRepositoryException
1423 final Session jcrSession = getSession( session );
1424 String repositoryId = artifactMetadata.getRepositoryId();
1428 Node root = jcrSession.getRootNode();
1430 getProjectVersionPath( repositoryId, artifactMetadata.getNamespace(), artifactMetadata.getProject(),
1433 if ( root.hasNode( path ) )
1435 Node node = root.getNode( path );
1437 for ( Node n : JcrUtils.getChildNodes( node ) )
1439 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1441 if ( n.hasProperty( "version" ) )
1443 String version = n.getProperty( "version" ).getString();
1444 if ( StringUtils.equals( version, artifactMetadata.getVersion() ) )
1454 catch ( RepositoryException e )
1456 throw new MetadataRepositoryException( e.getMessage(), e );
1464 public void removeProjectVersion( RepositorySession session, String repoId, String namespace, String projectId, String projectVersion )
1465 throws MetadataRepositoryException
1467 final Session jcrSession = getSession( session );
1471 String path = getProjectPath( repoId, namespace, projectId );
1472 Node root = jcrSession.getRootNode();
1474 Node nodeAtPath = root.getNode( path );
1476 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1478 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( projectVersion,
1485 catch ( RepositoryException e )
1487 throw new MetadataRepositoryException( e.getMessage(), e );
1492 public void removeArtifact( RepositorySession session, String repositoryId, String namespace, String projectId, String projectVersion,
1494 throws MetadataRepositoryException
1496 final Session jcrSession = getSession( session );
1499 Node root = jcrSession.getRootNode();
1500 String path = getArtifactPath( repositoryId, namespace, projectId, projectVersion, id );
1501 if ( root.hasNode( path ) )
1503 root.getNode( path ).remove();
1508 path = getProjectPath( repositoryId, namespace, projectId );
1510 Node nodeAtPath = root.getNode( path );
1512 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1514 if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) //
1515 && StringUtils.equals( node.getName(), projectVersion ) )
1521 catch ( RepositoryException e )
1523 throw new MetadataRepositoryException( e.getMessage(), e );
1528 public void removeFacetFromArtifact( RepositorySession session, String repositoryId, String namespace, String project, String projectVersion,
1529 MetadataFacet metadataFacet )
1530 throws MetadataRepositoryException
1532 final Session jcrSession = getSession( session );
1535 Node root = jcrSession.getRootNode();
1536 String path = getProjectVersionPath( repositoryId, namespace, project, projectVersion );
1538 if ( root.hasNode( path ) )
1540 Node node = root.getNode( path );
1542 for ( Node n : JcrUtils.getChildNodes( node ) )
1544 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1546 ArtifactMetadata artifactMetadata = getArtifactFromNode( repositoryId, n );
1547 log.debug( "artifactMetadata: {}", artifactMetadata );
1548 MetadataFacet metadataFacetToRemove = artifactMetadata.getFacet( metadataFacet.getFacetId() );
1549 if ( metadataFacetToRemove != null && metadataFacet.equals( metadataFacetToRemove ) )
1557 catch ( RepositoryException e )
1559 throw new MetadataRepositoryException( e.getMessage(), e );
1564 public Collection<ArtifactMetadata> getArtifacts( RepositorySession session, String repositoryId, String namespace, String projectId,
1565 String projectVersion )
1566 throws MetadataResolutionException
1568 final Session jcrSession;
1571 jcrSession = getSession( session );
1573 catch ( MetadataRepositoryException e )
1575 throw new MetadataResolutionException( e.getMessage( ) );
1577 List<ArtifactMetadata> artifacts = new ArrayList<>();
1581 Node root = jcrSession.getRootNode();
1582 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
1584 if ( root.hasNode( path ) )
1586 Node node = root.getNode( path );
1588 for ( Node n : JcrUtils.getChildNodes( node ) )
1590 if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
1592 artifacts.add( getArtifactFromNode( repositoryId, n ) );
1597 catch ( RepositoryException e )
1599 throw new MetadataResolutionException( e.getMessage(), e );
1608 throws MetadataRepositoryException
1614 * Exact is ignored as we can't do exact search in any property, we need a key
1617 public List<ArtifactMetadata> searchArtifacts( RepositorySession session, String repositoryId, String text, boolean exact )
1618 throws MetadataRepositoryException
1620 return searchArtifacts( session, repositoryId, null, text, exact );
1624 public List<ArtifactMetadata> searchArtifacts( RepositorySession session, String repositoryId, String key, String text, boolean exact )
1625 throws MetadataRepositoryException
1627 final Session jcrSession = getSession( session );
1628 String theKey = key == null ? "*" : "[" + key + "]";
1629 String projectVersionCondition =
1630 exact ? "(projectVersion." + theKey + " = $value)" : "contains([projectVersion]." + theKey + ", $value)";
1631 String facetCondition = exact ? "(facet." + theKey + " = $value)" : "contains([facet]." + theKey + ", $value)";
1632 String descendantCondition = repositoryId == null ?
1633 " AND [projectVersion].[jcr:path] LIKE '/repositories/%/content/%'" :
1634 " AND ISDESCENDANTNODE(projectVersion,'/" + getRepositoryContentPath( repositoryId ) + "')";
1635 List<ArtifactMetadata> result = new ArrayList<>( );
1636 if (key==null || (key!=null && Arrays.binarySearch( PROJECT_VERSION_VERSION_PROPERTIES, key )>=0))
1638 // We search only for project version properties if the key is a valid property name
1640 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE
1641 + "] AS projectVersion LEFT OUTER JOIN [" + ARTIFACT_NODE_TYPE
1642 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE " + projectVersionCondition + descendantCondition;
1643 result.addAll(runJcrQuery( jcrSession, repositoryId, q1, ImmutableMap.of( "value", text ), false ));
1646 "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE
1647 + "] AS projectVersion LEFT OUTER JOIN [" + ARTIFACT_NODE_TYPE
1648 + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) LEFT OUTER JOIN [" + FACET_NODE_TYPE
1649 + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE " + facetCondition + descendantCondition;
1650 result.addAll( runJcrQuery( jcrSession, repositoryId, q2, ImmutableMap.of( "value", text ), false ) );
1654 private ArtifactMetadata getArtifactFromNode( String repositoryId, Node artifactNode )
1655 throws RepositoryException
1657 String id = artifactNode.getName();
1659 ArtifactMetadata artifact = new ArtifactMetadata();
1660 artifact.setId( id );
1661 artifact.setRepositoryId( repositoryId == null ? artifactNode.getAncestor( 2 ).getName() : repositoryId );
1663 Node projectVersionNode = artifactNode.getParent();
1664 Node projectNode = projectVersionNode.getParent();
1665 Node namespaceNode = projectNode.getParent();
1667 artifact.setNamespace( namespaceNode.getProperty( "namespace" ).getString() );
1668 artifact.setProject( projectNode.getName() );
1669 artifact.setProjectVersion( projectVersionNode.getName() );
1670 artifact.setVersion( artifactNode.hasProperty( "version" )
1671 ? artifactNode.getProperty( "version" ).getString()
1672 : projectVersionNode.getName() );
1674 if ( artifactNode.hasProperty( JCR_LAST_MODIFIED ) )
1676 artifact.setFileLastModified( artifactNode.getProperty( JCR_LAST_MODIFIED ).getDate().getTimeInMillis() );
1679 if ( artifactNode.hasProperty( "whenGathered" ) )
1681 Calendar cal = artifactNode.getProperty("whenGathered").getDate();
1682 artifact.setWhenGathered( ZonedDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId()));
1685 if ( artifactNode.hasProperty( "size" ) )
1687 artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
1690 Node cslistNode = getOrAddNodeByPath( artifactNode, "checksums" );
1691 NodeIterator csNodeIt = cslistNode.getNodes( "*" );
1692 while (csNodeIt.hasNext()) {
1693 Node csNode = csNodeIt.nextNode( );
1694 if (csNode.isNodeType( CHECKSUM_NODE_TYPE ))
1696 addChecksum( artifact, csNode );
1700 retrieveFacetProperties( artifact, artifactNode );
1704 private void addChecksum(ArtifactMetadata artifact, Node n) {
1707 ChecksumAlgorithm alg = ChecksumAlgorithm.valueOf( n.getProperty( "type" ).getString() );
1708 String value = n.getProperty( "value" ).getString( );
1709 artifact.setChecksum( alg, value );
1711 catch ( Throwable e )
1713 log.error( "Could not set checksum from node {}", n );
1717 private static String getPropertyString( Node node, String name )
1718 throws RepositoryException
1720 return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
1723 private Collection<String> getNodeNames( Session jcrSession, String path, String nodeType )
1724 throws MetadataResolutionException
1727 List<String> names = new ArrayList<>();
1731 Node root = jcrSession.getRootNode();
1733 Node nodeAtPath = root.getNode( path );
1735 for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
1737 if ( node.isNodeType( nodeType ) )
1739 names.add( node.getName() );
1743 catch ( PathNotFoundException e )
1745 // ignore repo not found for now
1747 catch ( RepositoryException e )
1749 throw new MetadataResolutionException( e.getMessage(), e );
1755 private static String getRepositoryPath( String repositoryId )
1757 return "repositories/" + repositoryId;
1760 private static String getRepositoryContentPath( String repositoryId )
1762 return getRepositoryPath( repositoryId ) + "/content";
1765 private static String getFacetPath( String repositoryId, String facetId )
1767 return StringUtils.isEmpty( facetId ) ? getRepositoryPath( repositoryId ) + "/facets" :
1768 getRepositoryPath( repositoryId ) + "/facets/" + facetId;
1771 private static String getNamespacePath( String repositoryId, String namespace )
1773 return getRepositoryContentPath( repositoryId ) + "/" + namespace.replace( '.', '/' );
1776 private static String getProjectPath( String repositoryId, String namespace, String projectId )
1778 return getNamespacePath( repositoryId, namespace ) + "/" + projectId;
1781 private static String getProjectVersionPath( String repositoryId, String namespace, String projectId,
1782 String projectVersion )
1784 return getProjectPath( repositoryId, namespace, projectId ) + "/" + projectVersion;
1787 private static String getArtifactPath( String repositoryId, String namespace, String projectId,
1788 String projectVersion, String id )
1790 return getProjectVersionPath( repositoryId, namespace, projectId, projectVersion ) + "/" + id;
1793 private Node getOrAddNodeByPath( Node baseNode, String name )
1794 throws RepositoryException
1796 return getOrAddNodeByPath( baseNode, name, null );
1799 private Node getOrAddNodeByPath( Node baseNode, String name, String nodeType )
1800 throws RepositoryException
1802 log.debug( "getOrAddNodeByPath " + baseNode + " " + name + " " + nodeType );
1803 Node node = baseNode;
1804 for ( String n : name.split( "/" ) )
1806 node = JcrUtils.getOrAddNode( node, n );
1807 if ( nodeType != null && !node.isNodeType( nodeType ))
1809 node.addMixin( nodeType );
1811 if (!node.hasProperty( "id" )) {
1812 node.setProperty( "id", n );
1818 private static String getFacetPath( String repositoryId, String facetId, String name )
1820 return getFacetPath( repositoryId, facetId ) + "/" + name;
1823 private Node getOrAddRepositoryNode( Session jcrSession, String repositoryId )
1824 throws RepositoryException
1826 log.debug( "getOrAddRepositoryNode " + repositoryId );
1827 Node root = jcrSession.getRootNode();
1828 Node node = JcrUtils.getOrAddNode( root, "repositories" );
1829 log.debug( "Repositories " + node );
1830 node = JcrUtils.getOrAddNode( node, repositoryId, JcrConstants.NT_UNSTRUCTURED );
1831 if (!node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.REPOSITORY_NODE_TYPE )) {
1832 node.addMixin( org.apache.archiva.metadata.repository.jcr.JcrConstants.REPOSITORY_NODE_TYPE );
1834 if (!node.hasProperty( "id" )) {
1835 node.setProperty( "id", repositoryId );
1840 private Node getOrAddRepositoryContentNode( Session jcrSession, String repositoryId )
1841 throws RepositoryException
1843 Node node = getOrAddRepositoryNode( jcrSession, repositoryId );
1844 return JcrUtils.getOrAddNode( node, "content" );
1847 private Node getOrAddNamespaceNode( Session jcrSession, String repositoryId, String namespace )
1848 throws RepositoryException
1850 Node repo = getOrAddRepositoryContentNode( jcrSession, repositoryId );
1851 return getOrAddNodeByPath( repo, namespace.replace( '.', '/' ), NAMESPACE_NODE_TYPE );
1854 private Node getOrAddProjectNode( Session jcrSession, String repositoryId, String namespace, String projectId )
1855 throws RepositoryException
1857 Node namespaceNode = getOrAddNamespaceNode( jcrSession, repositoryId, namespace );
1858 Node node = JcrUtils.getOrAddNode( namespaceNode, projectId );
1859 if (!node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_NODE_TYPE ))
1861 node.addMixin( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_NODE_TYPE );
1863 if (!node.hasProperty( "id" ))
1865 node.setProperty( "id", projectId );
1870 private Node getOrAddProjectVersionNode( Session jcrSession, String repositoryId, String namespace, String projectId,
1871 String projectVersion )
1872 throws RepositoryException
1874 Node projectNode = getOrAddProjectNode( jcrSession, repositoryId, namespace, projectId );
1875 log.debug( "Project node {}", projectNode );
1876 Node projectVersionNode = JcrUtils.getOrAddNode( projectNode, projectVersion, JcrConstants.NT_UNSTRUCTURED);
1877 if (!projectVersionNode.isNodeType( PROJECT_VERSION_NODE_TYPE ))
1879 projectVersionNode.addMixin( PROJECT_VERSION_NODE_TYPE );
1881 if (!projectVersionNode.hasProperty( "id" ))
1883 projectVersionNode.setProperty( "id", projectVersion );
1886 log.debug( "Project version node {}", projectVersionNode );
1887 return projectVersionNode;
1890 private Node getOrAddArtifactNode( Session jcrSession, String repositoryId, String namespace, String projectId, String projectVersion,
1892 throws RepositoryException
1894 Node versionNode = getOrAddProjectVersionNode( jcrSession, repositoryId, namespace, projectId, projectVersion );
1895 Node node = JcrUtils.getOrAddNode( versionNode, id);
1896 if (!node.isNodeType( ARTIFACT_NODE_TYPE ))
1898 node.addMixin( ARTIFACT_NODE_TYPE );
1900 if (!node.hasProperty( "id" )) {
1901 node.setProperty( "id", id );
1906 private static Calendar createCalendar( ZonedDateTime time )
1908 return GregorianCalendar.from(time);
1911 private String join( Collection<String> ids )
1913 if ( ids != null && !ids.isEmpty() )
1915 StringBuilder s = new StringBuilder();
1916 for ( String id : ids )
1921 return s.substring( 0, s.length() - 1 );
1928 public void populateStatistics( RepositorySession repositorySession, MetadataRepository repository, String repositoryId,
1929 RepositoryStatistics repositoryStatistics )
1930 throws MetadataRepositoryException
1932 if ( !( repository instanceof JcrMetadataRepository ) )
1934 throw new MetadataRepositoryException(
1935 "The statistics population is only possible for JcrMetdataRepository implementations" );
1937 Session session = getSession( repositorySession );
1938 // TODO: these may be best as running totals, maintained by observations on the properties in JCR
1942 QueryManager queryManager = session.getWorkspace().getQueryManager();
1944 // TODO: Check, if this is still the case - Switched to Jackrabbit OAK with archiva 3.0
1945 // Former statement: JCR-SQL2 query will not complete on a large repo in Jackrabbit 2.2.0 - see JCR-2835
1946 // Using the JCR-SQL2 variants gives
1947 // "org.apache.lucene.search.BooleanQuery$TooManyClauses: maxClauseCount is set to 1024"
1948 // String whereClause = "WHERE ISDESCENDANTNODE([/repositories/" + repositoryId + "/content])";
1949 // Query query = queryManager.createQuery( "SELECT size FROM [archiva:artifact] " + whereClause,
1950 // Query.JCR_SQL2 );
1951 String whereClause = "WHERE ISDESCENDANTNODE([/repositories/" + repositoryId + "/content])";
1952 Query query = queryManager.createQuery( "SELECT size FROM [archiva:artifact] " + whereClause, Query.JCR_SQL2 );
1954 QueryResult queryResult = query.execute();
1956 Map<String, Integer> totalByType = new HashMap<>();
1957 long totalSize = 0, totalArtifacts = 0;
1958 for ( Row row : JcrUtils.getRows( queryResult ) )
1960 Node n = row.getNode();
1961 totalSize += row.getValue( "size" ).getLong();
1964 if ( n.hasNode( MavenArtifactFacet.FACET_ID ) )
1966 Node facetNode = n.getNode( MavenArtifactFacet.FACET_ID );
1967 type = facetNode.getProperty( "type" ).getString();
1973 Integer prev = totalByType.get( type );
1974 totalByType.put( type, prev != null ? prev + 1 : 1 );
1979 repositoryStatistics.setTotalArtifactCount( totalArtifacts );
1980 repositoryStatistics.setTotalArtifactFileSize( totalSize );
1981 for ( Map.Entry<String, Integer> entry : totalByType.entrySet() )
1983 log.info( "Setting count for type: {} = {}", entry.getKey(), entry.getValue() );
1984 repositoryStatistics.setTotalCountForType( entry.getKey(), entry.getValue() );
1987 // The query ordering is a trick to ensure that the size is correct, otherwise due to lazy init it will be -1
1988 // query = queryManager.createQuery( "SELECT * FROM [archiva:project] " + whereClause, Query.JCR_SQL2 );
1989 query = queryManager.createQuery( "SELECT * FROM [archiva:project] " + whereClause + " ORDER BY [jcr:score]",
1991 repositoryStatistics.setTotalProjectCount( query.execute().getRows().getSize() );
1993 // query = queryManager.createQuery(
1994 // "SELECT * FROM [archiva:namespace] " + whereClause + " AND namespace IS NOT NULL", Query.JCR_SQL2 );
1995 query = queryManager.createQuery(
1996 "SELECT * FROM [archiva:namespace] " + whereClause + " AND namespace IS NOT NULL ORDER BY [jcr:score]",
1998 repositoryStatistics.setTotalGroupCount( query.execute().getRows().getSize() );
2000 catch ( RepositoryException e )
2002 throw new MetadataRepositoryException( e.getMessage(), e );
2007 public Session login() throws RepositoryException
2009 return repository.login(new SimpleCredentials( "admin", "admin".toCharArray() ) );
2012 private static boolean isArtifactNodeType(Node n) {
2015 return n != null && n.isNodeType( ARTIFACT_NODE_TYPE );
2017 catch ( RepositoryException e )
2023 private Optional<ArtifactMetadata> getArtifactOptional(final String repositoryId, final Node n) {
2026 return Optional.ofNullable( getArtifactFromNode( repositoryId, n ) );
2028 catch ( RepositoryException e )
2030 return Optional.empty( );
2034 private Optional<ArtifactMetadata> getArtifactOptional(final String repositoryId, final Row row) {
2037 return Optional.ofNullable( getArtifactFromNode( repositoryId, row.getNode( "artifact" ) ) );
2039 catch ( RepositoryException e )
2041 return Optional.empty( );
2046 public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId,
2047 final String namespace, final String projectId, final String projectVersion,
2048 final QueryParameter queryParameter ) throws MetadataResolutionException
2050 final Session jcrSession;
2053 jcrSession = getSession( session );
2055 catch ( MetadataRepositoryException e )
2057 throw new MetadataResolutionException( e.getMessage( ) );
2062 Node root = jcrSession.getRootNode();
2063 String path = getProjectVersionPath( repositoryId, namespace, projectId, projectVersion );
2065 if ( root.hasNode( path ) )
2067 Node node = root.getNode( path );
2068 return StreamSupport.stream( JcrUtils.getChildNodes( node ).spliterator( ), false ).filter(JcrMetadataRepository::isArtifactNodeType)
2069 .map( n -> getArtifactOptional( repositoryId, n ) )
2070 .map( Optional::get ).skip( queryParameter.getOffset( ) ).limit( queryParameter.getLimit( ) );
2072 return Stream.empty( );
2075 catch ( RepositoryException e )
2077 throw new MetadataResolutionException( e.getMessage(), e );
2082 public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId,
2083 final QueryParameter queryParameter ) throws MetadataResolutionException
2085 final Session jcrSession;
2088 jcrSession = getSession( session );
2090 catch ( MetadataRepositoryException e )
2092 throw new MetadataResolutionException( e.getMessage( ), e );
2094 List<ArtifactMetadata> artifacts;
2096 String q = getArtifactQuery( repositoryId ).toString();
2100 Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
2101 QueryResult result = query.execute();
2103 return StreamSupport.stream( createResultSpliterator( result, ( Row row ) ->
2104 getArtifactOptional( repositoryId, row ) ), false )
2106 .skip( queryParameter.getOffset( ) ).limit( queryParameter.getLimit( ) );
2109 catch ( RepositoryException | MetadataRepositoryException e )
2111 throw new MetadataResolutionException( e.getMessage(), e );