123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- package org.apache.archiva.metadata.repository;
-
- /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
- import org.apache.archiva.metadata.model.ArtifactMetadata;
- import org.apache.archiva.metadata.model.ProjectMetadata;
- import org.apache.archiva.metadata.model.ProjectVersionMetadata;
- import org.apache.archiva.metadata.model.ProjectVersionReference;
- import org.apache.archiva.filter.ExcludesFilter;
- import org.apache.archiva.metadata.repository.storage.ReadMetadataRequest;
- import org.apache.archiva.metadata.repository.storage.RepositoryStorage;
- import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataInvalidException;
- import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataNotFoundException;
- import org.apache.archiva.metadata.repository.storage.RepositoryStorageRuntimeException;
- import org.apache.archiva.redback.components.cache.Cache;
- import org.apache.archiva.repository.events.RepositoryListener;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import javax.inject.Inject;
- import javax.inject.Named;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.List;
-
- /**
- * <p>
- * Default implementation of the metadata resolver API. At present it will handle updating the content repository
- * from new or changed information in the model and artifacts from the repository storage.
- * </p>
- * <p>
- * This is a singleton component to allow an alternate implementation to be provided. It is intended to be the same
- * system-wide for the whole content repository instead of on a per-managed-repository basis. Therefore, the session is
- * passed in as an argument to obtain any necessary resources, rather than the class being instantiated within the
- * session in the context of a single managed repository's resolution needs.
- * </p>
- * <p>
- * Note that the caller is responsible for the session, such as closing and saving (which is implied by the resolver
- * being obtained from within the session). The {@link RepositorySession#markDirty()} method is used as a hint to ensure
- * that the session knows we've made changes at close. We cannot ensure the changes will be persisted if the caller
- * chooses to revert first. This is preferable to storing the metadata immediately - a separate session would require
- * having a bi-directional link with the session factory, and saving the existing session might save other changes
- * unknowingly by the caller.
- * </p>
- */
- @Service("metadataResolver#default")
- public class DefaultMetadataResolver
- implements MetadataResolver
- {
-
- private Logger log = LoggerFactory.getLogger( DefaultMetadataResolver.class );
-
- /**
- * <p>
- * FIXME: this needs to be configurable based on storage type - and could also be instantiated per repo. Change to a
- * factory, and perhaps retrieve from the session. We should avoid creating one per request, however.
- * </p>
- * <p>
- * TODO: Also need to accommodate availability of proxy module
- * ... could be a different type since we need methods to modify the storage metadata, which would also allow more
- * appropriate methods to pass in the already determined repository configuration, for example, instead of the ID
- * </p>
- */
- @Inject
- @Named(value = "repositoryStorage#maven2")
- private RepositoryStorage repositoryStorage;
-
- @Inject
- @Autowired(required = false)
- private List<RepositoryListener> listeners = new ArrayList<>();
-
- /**
- * Cache used for namespaces
- */
- @Inject
- @Named( value = "cache#namespaces" )
- private Cache<String, Collection<String>> namespacesCache;
-
- @Override
- public ProjectVersionMetadata resolveProjectVersion( RepositorySession session, String repoId, String namespace,
- String projectId, String projectVersion )
- throws MetadataResolutionException
- {
- MetadataRepository metadataRepository = session.getRepository();
-
- ProjectVersionMetadata metadata =
- metadataRepository.getProjectVersion( repoId, namespace, projectId, projectVersion );
- // TODO: do we want to detect changes as well by comparing timestamps? isProjectVersionNewerThan(updated)
- // in such cases we might also remove/update stale metadata, including adjusting plugin-based facets
- // This would also be better than checking for completeness - we can then refresh only when fixed (though
- // sometimes this has an additional dependency - such as a parent - requesting the user to force an update
- // may then work here and be more efficient than always trying again)
- if ( metadata == null || metadata.isIncomplete() )
- {
- try
- {
- ReadMetadataRequest readMetadataRequest =
- new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
- projectId ).projectVersion( projectVersion ).browsingRequest( true );
- metadata = repositoryStorage.readProjectVersionMetadata( readMetadataRequest );
-
- log.debug( "Resolved project version metadata from storage: {}", metadata );
-
- // FIXME: make this a more generic post-processing that plugins can take advantage of
- // eg. maven projects should be able to process parent here
- if ( !metadata.getDependencies().isEmpty() )
- {
- ProjectVersionReference ref = new ProjectVersionReference();
- ref.setNamespace( namespace );
- ref.setProjectId( projectId );
- ref.setProjectVersion( projectVersion );
- ref.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
- }
- try
- {
- for ( RepositoryListener listener : listeners )
- {
- listener.addArtifact( session, repoId, namespace, projectId, metadata );
- }
- metadataRepository.updateProjectVersion( repoId, namespace, projectId, metadata );
- }
- catch ( MetadataRepositoryException e )
- {
- log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
- }
-
- session.markDirty();
- }
- catch ( RepositoryStorageMetadataInvalidException e )
- {
- for ( RepositoryListener listener : listeners )
- {
- listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
- }
- throw new MetadataResolutionException( e.getMessage(), e );
- }
- catch ( RepositoryStorageMetadataNotFoundException e )
- {
- for ( RepositoryListener listener : listeners )
- {
- listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
- }
- // no need to rethrow - return null
- }
- catch ( RepositoryStorageRuntimeException e )
- {
- for ( RepositoryListener listener : listeners )
- {
- listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
- }
- throw new MetadataResolutionException( e.getMessage(), e );
- }
-
- }
- return metadata;
- }
-
- @Override
- public Collection<ProjectVersionReference> resolveProjectReferences( RepositorySession session, String repoId,
- String namespace, String projectId,
- String projectVersion )
- throws MetadataResolutionException
- {
- // TODO: is this assumption correct? could a storage mech. actually know all references in a non-Maven scenario?
- // not passed to the storage mechanism as resolving references would require iterating all artifacts
- MetadataRepository metadataRepository = session.getRepository();
- return metadataRepository.getProjectReferences( repoId, namespace, projectId, projectVersion );
- }
-
- @Override
- public Collection<String> resolveRootNamespaces( RepositorySession session, String repoId )
- throws MetadataResolutionException
- {
- try
- {
-
- Collection<String> namespaces = namespacesCache.get( repoId );
- if ( namespaces != null )
- {
- return namespaces;
- }
-
- MetadataRepository metadataRepository = session.getRepository();
- namespaces = metadataRepository.getRootNamespaces( repoId );
- Collection<String> storageNamespaces =
- repositoryStorage.listRootNamespaces( repoId, new ExcludesFilter<String>( namespaces ) );
- if ( storageNamespaces != null && !storageNamespaces.isEmpty() )
- {
-
- log.debug( "Resolved root namespaces from storage: {}", storageNamespaces );
-
- for ( String n : storageNamespaces )
- {
- try
- {
- metadataRepository.updateNamespace( repoId, n );
- // just invalidate cache entry
- String cacheKey = repoId + "-" + n;
- namespacesCache.remove( cacheKey );
- }
- catch ( MetadataRepositoryException e )
- {
- log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
- }
- }
- session.markDirty();
-
- namespaces = new ArrayList<>( namespaces );
- namespaces.addAll( storageNamespaces );
- }
-
- namespacesCache.put( repoId, namespaces );
-
- return namespaces;
- }
- catch ( RepositoryStorageRuntimeException e )
- {
- throw new MetadataResolutionException( e.getMessage(), e );
- }
- }
-
- @Override
- public Collection<String> resolveNamespaces( RepositorySession session, String repoId, String namespace )
- throws MetadataResolutionException
- {
- try
- {
- MetadataRepository metadataRepository = session.getRepository();
- String cacheKey = repoId + "-" + namespace;
- Collection<String> namespaces = namespacesCache.get( cacheKey );
- if ( namespaces == null )
- {
- namespaces = metadataRepository.getNamespaces( repoId, namespace );
- namespacesCache.put( cacheKey, namespaces );
- }
- Collection<String> exclusions = new ArrayList<>( namespaces );
- exclusions.addAll( metadataRepository.getProjects( repoId, namespace ) );
- Collection<String> storageNamespaces =
- repositoryStorage.listNamespaces( repoId, namespace, new ExcludesFilter<String>( exclusions ) );
- if ( storageNamespaces != null && !storageNamespaces.isEmpty() )
- {
-
- log.debug( "Resolved namespaces from storage: {}", storageNamespaces );
-
- for ( String n : storageNamespaces )
- {
- try
- {
- metadataRepository.updateNamespace( repoId, namespace + "." + n );
- // just invalidate cache entry
- cacheKey = repoId + "-" + namespace + "." + n;
- namespacesCache.remove( cacheKey );
- }
- catch ( MetadataRepositoryException e )
- {
- log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
- }
- }
- session.markDirty();
-
- namespaces = new ArrayList<>( namespaces );
- namespaces.addAll( storageNamespaces );
- }
- return namespaces;
- }
- catch ( RepositoryStorageRuntimeException e )
- {
- throw new MetadataResolutionException( e.getMessage(), e );
- }
- }
-
- @Override
- public Collection<String> resolveProjects( RepositorySession session, String repoId, String namespace )
- throws MetadataResolutionException
- {
- try
- {
- MetadataRepository metadataRepository = session.getRepository();
- Collection<String> projects = metadataRepository.getProjects( repoId, namespace );
- Collection<String> exclusions = new ArrayList<>( projects );
-
- String cacheKey = repoId + "-" + namespace;
- Collection<String> namespaces = namespacesCache.get( cacheKey );
- if ( namespaces == null )
- {
- namespaces = metadataRepository.getNamespaces( repoId, namespace );
- namespacesCache.put( cacheKey, namespaces );
- }
-
- exclusions.addAll( namespaces );
-
- Collection<String> storageProjects =
- repositoryStorage.listProjects( repoId, namespace, new ExcludesFilter<>( exclusions ) );
- if ( storageProjects != null && !storageProjects.isEmpty() )
- {
-
- log.debug( "Resolved projects from storage: {}", storageProjects );
- for ( String projectId : storageProjects )
- {
- ProjectMetadata projectMetadata =
- repositoryStorage.readProjectMetadata( repoId, namespace, projectId );
- if ( projectMetadata != null )
- {
- try
- {
- metadataRepository.updateProject( repoId, projectMetadata );
- }
- catch ( MetadataRepositoryException e )
- {
- log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
- }
- }
- }
- session.markDirty();
-
- projects = new ArrayList<>( projects );
- projects.addAll( storageProjects );
- }
- return projects;
- }
- catch ( RepositoryStorageRuntimeException e )
- {
- throw new MetadataResolutionException( e.getMessage(), e );
- }
- }
-
- @Override
- public Collection<String> resolveProjectVersions( RepositorySession session, String repoId, String namespace,
- String projectId )
- throws MetadataResolutionException
- {
- try
- {
- MetadataRepository metadataRepository = session.getRepository();
-
- Collection<String> projectVersions = metadataRepository.getProjectVersions( repoId, namespace, projectId );
- Collection<String> storageProjectVersions =
- repositoryStorage.listProjectVersions( repoId, namespace, projectId,
- new ExcludesFilter<String>( projectVersions ) );
- if ( storageProjectVersions != null && !storageProjectVersions.isEmpty() )
- {
- log.debug( "Resolved project versions from storage: {}", storageProjectVersions );
-
- for ( String projectVersion : storageProjectVersions )
- {
- try
- {
- ReadMetadataRequest readMetadataRequest =
- new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
- projectId ).projectVersion( projectVersion );
- ProjectVersionMetadata versionMetadata =
- repositoryStorage.readProjectVersionMetadata( readMetadataRequest );
- for ( RepositoryListener listener : listeners )
- {
- listener.addArtifact( session, repoId, namespace, projectId, versionMetadata );
- }
-
- metadataRepository.updateProjectVersion( repoId, namespace, projectId, versionMetadata );
- }
- catch ( MetadataRepositoryException e )
- {
- log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
- }
- catch ( RepositoryStorageMetadataInvalidException e )
- {
- log.warn(
- "Not update project in metadata repository due to an error resolving it from storage: {}",
- e.getMessage() );
-
- for ( RepositoryListener listener : listeners )
- {
- listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
- }
- }
- catch ( RepositoryStorageMetadataNotFoundException e )
- {
- for ( RepositoryListener listener : listeners )
- {
- listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
- }
- }
- }
- session.markDirty();
-
- projectVersions = new ArrayList<>( projectVersions );
- projectVersions.addAll( storageProjectVersions );
- }
- return projectVersions;
- }
- catch ( RepositoryStorageRuntimeException e )
- {
- throw new MetadataResolutionException( e.getMessage(), e );
- }
- }
-
- @Override
- public Collection<ArtifactMetadata> resolveArtifacts( RepositorySession session, String repoId, String namespace,
- String projectId, String projectVersion )
- throws MetadataResolutionException
- {
- try
- {
- MetadataRepository metadataRepository = session.getRepository();
- Collection<ArtifactMetadata> artifacts =
- metadataRepository.getArtifacts( repoId, namespace, projectId, projectVersion );
- ExcludesFilter<String> filter = new ExcludesFilter<String>( createArtifactIdList( artifacts ) );
-
- ReadMetadataRequest readMetadataRequest =
- new ReadMetadataRequest().repositoryId( repoId ).namespace( namespace ).projectId(
- projectId ).projectVersion( projectVersion ).filter( filter );
-
- Collection<ArtifactMetadata> storageArtifacts =
- repositoryStorage.readArtifactsMetadata( readMetadataRequest );
- if ( storageArtifacts != null && !storageArtifacts.isEmpty() )
- {
-
- log.debug( "Resolved artifacts from storage: {}", storageArtifacts );
-
- for ( ArtifactMetadata artifact : storageArtifacts )
- {
- try
- {
- metadataRepository.updateArtifact( repoId, namespace, projectId, projectVersion, artifact );
- }
- catch ( MetadataRepositoryException e )
- {
- log.warn( "Unable to persist resolved information: {}", e.getMessage(), e );
- }
- }
- session.markDirty();
-
- artifacts = new ArrayList<>( artifacts );
- artifacts.addAll( storageArtifacts );
- }
- return artifacts;
- }
- catch ( RepositoryStorageRuntimeException e )
- {
- for ( RepositoryListener listener : listeners )
- {
- listener.addArtifactProblem( session, repoId, namespace, projectId, projectVersion, e );
- }
- throw new MetadataResolutionException( e.getMessage(), e );
- }
- }
-
- private Collection<String> createArtifactIdList( Collection<ArtifactMetadata> artifacts )
- {
- Collection<String> artifactIds = new ArrayList<>();
- for ( ArtifactMetadata artifact : artifacts )
- {
- artifactIds.add( artifact.getId() );
- }
- return artifactIds;
- }
- }
|