1 package org.apache.archiva.metadata.repository;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.archiva.metadata.model.ArtifactMetadata;
23 import org.apache.archiva.metadata.model.ProjectMetadata;
24 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
25 import org.apache.archiva.metadata.model.ProjectVersionReference;
26 import org.apache.archiva.filter.ExcludesFilter;
27 import org.apache.archiva.metadata.repository.storage.ReadMetadataRequest;
28 import org.apache.archiva.metadata.repository.storage.RepositoryStorage;
29 import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataInvalidException;
30 import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataNotFoundException;
31 import org.apache.archiva.metadata.repository.storage.RepositoryStorageRuntimeException;
32 import org.apache.archiva.redback.components.cache.Cache;
33 import org.apache.archiva.repository.events.RepositoryListener;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.springframework.beans.factory.annotation.Autowired;
37 import org.springframework.stereotype.Service;
39 import javax.inject.Inject;
40 import javax.inject.Named;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.List;
47 * Default implementation of the metadata resolver API. At present it will handle updating the content repository
48 * from new or changed information in the model and artifacts from the repository storage.
51 * This is a singleton component to allow an alternate implementation to be provided. It is intended to be the same
52 * system-wide for the whole content repository instead of on a per-managed-repository basis. Therefore, the session is
53 * passed in as an argument to obtain any necessary resources, rather than the class being instantiated within the
54 * session in the context of a single managed repository's resolution needs.
57 * Note that the caller is responsible for the session, such as closing and saving (which is implied by the resolver
58 * being obtained from within the session). The {@link RepositorySession#markDirty()} method is used as a hint to ensure
59 * that the session knows we've made changes at close. We cannot ensure the changes will be persisted if the caller
60 * chooses to revert first. This is preferable to storing the metadata immediately - a separate session would require
61 * having a bi-directional link with the session factory, and saving the existing session might save other changes
62 * unknowingly by the caller.
65 @Service("metadataResolver#default")
66 public class DefaultMetadataResolver
67 implements MetadataResolver
70 private Logger log = LoggerFactory.getLogger( DefaultMetadataResolver.class );
74 * FIXME: this needs to be configurable based on storage type - and could also be instantiated per repo. Change to a
75 * factory, and perhaps retrieve from the session. We should avoid creating one per request, however.
78 * TODO: Also need to accommodate availability of proxy module
79 * ... could be a different type since we need methods to modify the storage metadata, which would also allow more
80 * appropriate methods to pass in the already determined repository configuration, for example, instead of the ID
84 @Named(value = "repositoryStorage#maven2")
85 private RepositoryStorage repositoryStorage;
88 @Autowired(required = false)
89 private List<RepositoryListener> listeners = new ArrayList<>();
92 * Cache used for namespaces
95 @Named( value = "cache#namespaces" )
96 private Cache<String, Collection<String>> namespacesCache;
99 public ProjectVersionMetadata resolveProjectVersion( RepositorySession session, String repoId, String namespace,
100 String projectId, String projectVersion )
101 throws MetadataResolutionException
103 MetadataRepository metadataRepository = session.getRepository();
105 ProjectVersionMetadata metadata =
106 metadataRepository.getProjectVersion( session, repoId, namespace, projectId, projectVersion );
107 // TODO: do we want to detect changes as well by comparing timestamps? isProjectVersionNewerThan(updated)
108 // in such cases we might also remove/update stale metadata, including adjusting plugin-based facets
109 // This would also be better than checking for completeness - we can then refresh only when fixed (though
110 // sometimes this has an additional dependency - such as a parent - requesting the user to force an update
111 // may then work here and be more efficient than always trying again)
112 if ( metadata == null || metadata.isIncomplete() )
116 ReadMetadataRequest readMetadataRequest =
117 new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
118 projectId ).projectVersion( projectVersion ).browsingRequest( true );
119 metadata = repositoryStorage.readProjectVersionMetadata( readMetadataRequest );
121 log.debug( "Resolved project version metadata from storage: {}", metadata );
123 // FIXME: make this a more generic post-processing that plugins can take advantage of
124 // eg. maven projects should be able to process parent here
125 if ( !metadata.getDependencies().isEmpty() )
127 ProjectVersionReference ref = new ProjectVersionReference();
128 ref.setNamespace( namespace );
129 ref.setProjectId( projectId );
130 ref.setProjectVersion( projectVersion );
131 ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
135 for ( RepositoryListener listener : listeners )
137 listener.addArtifact( session, repoId, namespace, projectId, metadata );
139 metadataRepository.updateProjectVersion( session, repoId, namespace, projectId, metadata );
141 catch ( MetadataRepositoryException e )
143 log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
148 catch ( RepositoryStorageMetadataInvalidException e )
150 for ( RepositoryListener listener : listeners )
152 listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
154 throw new MetadataResolutionException( e.getMessage(), e );
156 catch ( RepositoryStorageMetadataNotFoundException e )
158 for ( RepositoryListener listener : listeners )
160 listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
162 // no need to rethrow - return null
164 catch ( RepositoryStorageRuntimeException e )
166 for ( RepositoryListener listener : listeners )
168 listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
170 throw new MetadataResolutionException( e.getMessage(), e );
178 public Collection<ProjectVersionReference> resolveProjectReferences( RepositorySession session, String repoId,
179 String namespace, String projectId,
180 String projectVersion )
181 throws MetadataResolutionException
183 // TODO: is this assumption correct? could a storage mech. actually know all references in a non-Maven scenario?
184 // not passed to the storage mechanism as resolving references would require iterating all artifacts
185 MetadataRepository metadataRepository = session.getRepository();
186 return metadataRepository.getProjectReferences( session, repoId, namespace, projectId, projectVersion );
190 public Collection<String> resolveRootNamespaces( RepositorySession session, String repoId )
191 throws MetadataResolutionException
196 Collection<String> namespaces = namespacesCache.get( repoId );
197 if ( namespaces != null )
202 MetadataRepository metadataRepository = session.getRepository();
203 namespaces = metadataRepository.getRootNamespaces( session, repoId );
204 Collection<String> storageNamespaces =
205 repositoryStorage.listRootNamespaces( repoId, new ExcludesFilter<String>( namespaces ) );
206 if ( storageNamespaces != null && !storageNamespaces.isEmpty() )
209 log.debug( "Resolved root namespaces from storage: {}", storageNamespaces );
211 for ( String n : storageNamespaces )
215 metadataRepository.updateNamespace( session, repoId, n );
216 // just invalidate cache entry
217 String cacheKey = repoId + "-" + n;
218 namespacesCache.remove( cacheKey );
220 catch ( MetadataRepositoryException e )
222 log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
227 namespaces = new ArrayList<>( namespaces );
228 namespaces.addAll( storageNamespaces );
231 namespacesCache.put( repoId, namespaces );
235 catch ( RepositoryStorageRuntimeException e )
237 throw new MetadataResolutionException( e.getMessage(), e );
242 public Collection<String> resolveNamespaces( RepositorySession session, String repoId, String namespace )
243 throws MetadataResolutionException
247 MetadataRepository metadataRepository = session.getRepository();
248 String cacheKey = repoId + "-" + namespace;
249 Collection<String> namespaces = namespacesCache.get( cacheKey );
250 if ( namespaces == null )
252 namespaces = metadataRepository.getChildNamespaces( session, repoId, namespace );
253 namespacesCache.put( cacheKey, namespaces );
255 Collection<String> exclusions = new ArrayList<>( namespaces );
256 exclusions.addAll( metadataRepository.getProjects( session, repoId, namespace ) );
257 Collection<String> storageNamespaces =
258 repositoryStorage.listNamespaces( repoId, namespace, new ExcludesFilter<String>( exclusions ) );
259 if ( storageNamespaces != null && !storageNamespaces.isEmpty() )
262 log.debug( "Resolved namespaces from storage: {}", storageNamespaces );
264 for ( String n : storageNamespaces )
268 metadataRepository.updateNamespace( session, repoId, namespace + "." + n );
269 // just invalidate cache entry
270 cacheKey = repoId + "-" + namespace + "." + n;
271 namespacesCache.remove( cacheKey );
273 catch ( MetadataRepositoryException e )
275 log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
280 namespaces = new ArrayList<>( namespaces );
281 namespaces.addAll( storageNamespaces );
285 catch ( RepositoryStorageRuntimeException e )
287 throw new MetadataResolutionException( e.getMessage(), e );
292 public Collection<String> resolveProjects( RepositorySession session, String repoId, String namespace )
293 throws MetadataResolutionException
297 MetadataRepository metadataRepository = session.getRepository();
298 Collection<String> projects = metadataRepository.getProjects( session, repoId, namespace );
299 Collection<String> exclusions = new ArrayList<>( projects );
301 String cacheKey = repoId + "-" + namespace;
302 Collection<String> namespaces = namespacesCache.get( cacheKey );
303 if ( namespaces == null )
305 namespaces = metadataRepository.getChildNamespaces( session, repoId, namespace );
306 namespacesCache.put( cacheKey, namespaces );
309 exclusions.addAll( namespaces );
311 Collection<String> storageProjects =
312 repositoryStorage.listProjects( repoId, namespace, new ExcludesFilter<>( exclusions ) );
313 if ( storageProjects != null && !storageProjects.isEmpty() )
316 log.debug( "Resolved projects from storage: {}", storageProjects );
317 for ( String projectId : storageProjects )
319 ProjectMetadata projectMetadata =
320 repositoryStorage.readProjectMetadata( repoId, namespace, projectId );
321 if ( projectMetadata != null )
325 metadataRepository.updateProject( session, repoId, projectMetadata );
327 catch ( MetadataRepositoryException e )
329 log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
335 projects = new ArrayList<>( projects );
336 projects.addAll( storageProjects );
340 catch ( RepositoryStorageRuntimeException e )
342 throw new MetadataResolutionException( e.getMessage(), e );
347 public Collection<String> resolveProjectVersions( RepositorySession session, String repoId, String namespace,
349 throws MetadataResolutionException
353 MetadataRepository metadataRepository = session.getRepository();
355 Collection<String> projectVersions = metadataRepository.getProjectVersions( session, repoId, namespace, projectId );
356 Collection<String> storageProjectVersions =
357 repositoryStorage.listProjectVersions( repoId, namespace, projectId,
358 new ExcludesFilter<String>( projectVersions ) );
359 if ( storageProjectVersions != null && !storageProjectVersions.isEmpty() )
361 log.debug( "Resolved project versions from storage: {}", storageProjectVersions );
363 for ( String projectVersion : storageProjectVersions )
367 ReadMetadataRequest readMetadataRequest =
368 new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
369 projectId ).projectVersion( projectVersion );
370 ProjectVersionMetadata versionMetadata =
371 repositoryStorage.readProjectVersionMetadata( readMetadataRequest );
372 for ( RepositoryListener listener : listeners )
374 listener.addArtifact( session, repoId, namespace, projectId, versionMetadata );
377 metadataRepository.updateProjectVersion( session, repoId, namespace, projectId, versionMetadata );
379 catch ( MetadataRepositoryException e )
381 log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
383 catch ( RepositoryStorageMetadataInvalidException e )
386 "Not update project in metadata repository due to an error resolving it from storage: {}",
389 for ( RepositoryListener listener : listeners )
391 listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
394 catch ( RepositoryStorageMetadataNotFoundException e )
396 for ( RepositoryListener listener : listeners )
398 listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
404 projectVersions = new ArrayList<>( projectVersions );
405 projectVersions.addAll( storageProjectVersions );
407 return projectVersions;
409 catch ( RepositoryStorageRuntimeException e )
411 throw new MetadataResolutionException( e.getMessage(), e );
416 public Collection<ArtifactMetadata> resolveArtifacts( RepositorySession session, String repoId, String namespace,
417 String projectId, String projectVersion )
418 throws MetadataResolutionException
422 MetadataRepository metadataRepository = session.getRepository();
423 Collection<ArtifactMetadata> artifacts =
424 metadataRepository.getArtifacts( session, repoId, namespace, projectId, projectVersion );
425 ExcludesFilter<String> filter = new ExcludesFilter<String>( createArtifactIdList( artifacts ) );
427 ReadMetadataRequest readMetadataRequest =
428 new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
429 projectId ).projectVersion( projectVersion ).filter( filter );
431 Collection<ArtifactMetadata> storageArtifacts =
432 repositoryStorage.readArtifactsMetadata( readMetadataRequest );
433 if ( storageArtifacts != null && !storageArtifacts.isEmpty() )
436 log.debug( "Resolved artifacts from storage: {}", storageArtifacts );
438 for ( ArtifactMetadata artifact : storageArtifacts )
442 metadataRepository.updateArtifact( session, repoId, namespace, projectId, projectVersion, artifact );
444 catch ( MetadataRepositoryException e )
446 log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
451 artifacts = new ArrayList<>( artifacts );
452 artifacts.addAll( storageArtifacts );
456 catch ( RepositoryStorageRuntimeException e )
458 for ( RepositoryListener listener : listeners )
460 listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
462 throw new MetadataResolutionException( e.getMessage(), e );
466 private Collection<String> createArtifactIdList( Collection<ArtifactMetadata> artifacts )
468 Collection<String> artifactIds = new ArrayList<>();
469 for ( ArtifactMetadata artifact : artifacts )
471 artifactIds.add( artifact.getId() );