diff options
author | Martin Stockhammer <martin_s@apache.org> | 2020-06-11 14:33:00 +0200 |
---|---|---|
committer | Martin Stockhammer <martin_s@apache.org> | 2020-06-11 14:33:00 +0200 |
commit | 46fd585f40f0264b8889154e1924eb8df991b0f3 (patch) | |
tree | 09408c7375f7e57d0760ef48b0ddae7be768e4ca | |
parent | b942314aa27330a179acf17300fea6849bf89d4d (diff) | |
download | archiva-46fd585f40f0264b8889154e1924eb8df991b0f3.tar.gz archiva-46fd585f40f0264b8889154e1924eb8df991b0f3.zip |
Improving repository API
15 files changed, 458 insertions, 166 deletions
diff --git a/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/common/utils/VersionUtil.java b/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/common/utils/VersionUtil.java index c1a3ac694..408d7b4e1 100644 --- a/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/common/utils/VersionUtil.java +++ b/archiva-modules/archiva-base/archiva-common/src/main/java/org/apache/archiva/common/utils/VersionUtil.java @@ -194,4 +194,6 @@ public class VersionUtil } return version; } + + } diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/DaysOldRepositoryPurge.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/DaysOldRepositoryPurge.java index fb42390bb..282f45c41 100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/DaysOldRepositoryPurge.java +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/DaysOldRepositoryPurge.java @@ -23,9 +23,9 @@ import org.apache.archiva.common.utils.VersionComparator; import org.apache.archiva.common.utils.VersionUtil; import org.apache.archiva.metadata.audit.RepositoryListener; import org.apache.archiva.metadata.repository.RepositorySession; -import org.apache.archiva.repository.ManagedRepositoryContent; -import org.apache.archiva.repository.LayoutException; import org.apache.archiva.repository.BaseRepositoryContentLayout; +import org.apache.archiva.repository.LayoutException; +import org.apache.archiva.repository.ManagedRepositoryContent; import org.apache.archiva.repository.content.Artifact; import org.apache.archiva.repository.content.ContentItem; import org.apache.archiva.repository.content.base.ArchivaItemSelector; @@ -74,94 +74,94 @@ public class DaysOldRepositoryPurge { ContentItem item = repository.toItem( path ); - if ( item instanceof Artifact ) + Artifact artifactItem = repository.getLayout( BaseRepositoryContentLayout.class ).adaptItem( Artifact.class, item ); + + if ( !artifactItem.exists( ) ) { - Artifact artifactItem = (Artifact) item; + return; + } - if ( !artifactItem.exists( ) ) - { - return; - } + // ArtifactReference artifact = repository.toArtifactReference( path ); - // ArtifactReference artifact = repository.toArtifactReference( path ); - - Calendar olderThanThisDate = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); - olderThanThisDate.add( Calendar.DATE, -retentionPeriod ); - - ArchivaItemSelector selector = ArchivaItemSelector.builder( ) - .withNamespace( artifactItem.getVersion( ).getProject( ).getNamespace( ).getId( ) ) - .withProjectId( artifactItem.getVersion( ).getProject( ).getId( ) ) - .withVersion( artifactItem.getVersion( ).getId( ) ) - .withClassifier( "*" ) - .includeRelatedArtifacts( ) - .build( ); - - List<String> artifactVersions; - try( Stream<? extends Artifact> stream = repository.getLayout( BaseRepositoryContentLayout.class ).newArtifactStream( selector )){ - artifactVersions = stream.map( a -> a.getArtifactVersion( ) ) - .filter( StringUtils::isNotEmpty ) - .distinct() - .collect( Collectors.toList( ) ); - } + Calendar olderThanThisDate = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); + olderThanThisDate.add( Calendar.DATE, -retentionPeriod ); - Collections.sort( artifactVersions, VersionComparator.getInstance( ) ); + ArchivaItemSelector selector = ArchivaItemSelector.builder( ) + .withNamespace( artifactItem.getVersion( ).getProject( ).getNamespace( ).getId( ) ) + .withProjectId( artifactItem.getVersion( ).getProject( ).getId( ) ) + .withVersion( artifactItem.getVersion( ).getId( ) ) + .withClassifier( "*" ) + .includeRelatedArtifacts( ) + .build( ); - if ( retentionCount > artifactVersions.size( ) ) - { - // Done. nothing to do here. skip it. - return; - } + List<String> artifactVersions; + try ( Stream<? extends Artifact> stream = repository.getLayout( BaseRepositoryContentLayout.class ).newArtifactStream( selector ) ) + { + artifactVersions = stream.map( a -> a.getArtifactVersion( ) ) + .filter( StringUtils::isNotEmpty ) + .distinct( ) + .collect( Collectors.toList( ) ); + } + + Collections.sort( artifactVersions, VersionComparator.getInstance( ) ); - int countToPurge = artifactVersions.size( ) - retentionCount; + if ( retentionCount > artifactVersions.size( ) ) + { + // Done. nothing to do here. skip it. + return; + } + int countToPurge = artifactVersions.size( ) - retentionCount; - ArchivaItemSelector.Builder artifactSelectorBuilder = ArchivaItemSelector.builder( ) - .withNamespace( artifactItem.getVersion( ).getProject( ).getNamespace( ).getId( ) ) - .withProjectId( artifactItem.getVersion( ).getProject( ).getId( ) ) - .withVersion( artifactItem.getVersion( ).getId( ) ) - .withArtifactId( artifactItem.getId() ) - .withClassifier( "*" ) - .includeRelatedArtifacts( ); - Set<Artifact> artifactsToDelete = new HashSet<>( ); - for ( String version : artifactVersions ) + ArchivaItemSelector.Builder artifactSelectorBuilder = ArchivaItemSelector.builder( ) + .withNamespace( artifactItem.getVersion( ).getProject( ).getNamespace( ).getId( ) ) + .withProjectId( artifactItem.getVersion( ).getProject( ).getId( ) ) + .withVersion( artifactItem.getVersion( ).getId( ) ) + .withArtifactId( artifactItem.getId( ) ) + .withClassifier( "*" ) + .includeRelatedArtifacts( ); + + Set<Artifact> artifactsToDelete = new HashSet<>( ); + for ( String version : artifactVersions ) + { + if ( countToPurge-- <= 0 ) { - if ( countToPurge-- <= 0 ) - { - break; - } + break; + } - ArchivaItemSelector artifactSelector = artifactSelectorBuilder.withArtifactVersion( version ).build( ); - try - { + ArchivaItemSelector artifactSelector = artifactSelectorBuilder.withArtifactVersion( version ).build( ); + try + { - // Is this a generic snapshot "1.0-SNAPSHOT" ? - if ( VersionUtil.isGenericSnapshot( version ) ) + // Is this a generic snapshot "1.0-SNAPSHOT" ? + if ( VersionUtil.isGenericSnapshot( version ) ) + { + List<? extends Artifact> artifactList = repository.getLayout( BaseRepositoryContentLayout.class ).getArtifacts( artifactSelector ); + if ( artifactList.size( ) > 0 && artifactList.get( 0 ).getAsset( ).getModificationTime( ).toEpochMilli( ) < olderThanThisDate.getTimeInMillis( ) ) { - List<? extends Artifact> artifactList = repository.getLayout( BaseRepositoryContentLayout.class ).getArtifacts( artifactSelector ); - if ( artifactList.size()>0 && artifactList.get(0).getAsset().getModificationTime( ).toEpochMilli( ) < olderThanThisDate.getTimeInMillis( ) ) - { - artifactsToDelete.addAll( artifactList ); - } + artifactsToDelete.addAll( artifactList ); } - // Is this a timestamp snapshot "1.0-20070822.123456-42" ? - else if ( VersionUtil.isUniqueSnapshot( version ) ) - { - Calendar timestampCal = uniqueSnapshotToCalendar( version ); + } + // Is this a timestamp snapshot "1.0-20070822.123456-42" ? + else if ( VersionUtil.isUniqueSnapshot( version ) ) + { + Calendar timestampCal = uniqueSnapshotToCalendar( version ); - if ( timestampCal.getTimeInMillis( ) < olderThanThisDate.getTimeInMillis( ) ) - { - artifactsToDelete.addAll( repository.getLayout( BaseRepositoryContentLayout.class ).getArtifacts( artifactSelector ) ); - } + if ( timestampCal.getTimeInMillis( ) < olderThanThisDate.getTimeInMillis( ) ) + { + artifactsToDelete.addAll( repository.getLayout( BaseRepositoryContentLayout.class ).getArtifacts( artifactSelector ) ); } - } catch ( IllegalArgumentException e ) { - log.error( "Bad selector for artifact: {}", e.getMessage( ), e ); - // continue } } - purge( artifactsToDelete ); + catch ( IllegalArgumentException e ) + { + log.error( "Bad selector for artifact: {}", e.getMessage( ), e ); + // continue + } } + purge( artifactsToDelete ); } catch ( LayoutException e ) { diff --git a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/RetentionCountRepositoryPurge.java b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/RetentionCountRepositoryPurge.java index ec3a242f4..74df6a679 100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/RetentionCountRepositoryPurge.java +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-core-consumers/src/main/java/org/apache/archiva/consumers/core/repository/RetentionCountRepositoryPurge.java @@ -21,12 +21,12 @@ package org.apache.archiva.consumers.core.repository; import org.apache.archiva.common.utils.VersionComparator; import org.apache.archiva.common.utils.VersionUtil; +import org.apache.archiva.metadata.audit.RepositoryListener; import org.apache.archiva.metadata.repository.RepositorySession; import org.apache.archiva.model.ArtifactReference; -import org.apache.archiva.repository.ManagedRepositoryContent; -import org.apache.archiva.repository.LayoutException; import org.apache.archiva.repository.BaseRepositoryContentLayout; -import org.apache.archiva.metadata.audit.RepositoryListener; +import org.apache.archiva.repository.LayoutException; +import org.apache.archiva.repository.ManagedRepositoryContent; import org.apache.archiva.repository.content.Artifact; import org.apache.archiva.repository.content.ContentItem; import org.apache.archiva.repository.content.base.ArchivaItemSelector; @@ -61,67 +61,65 @@ public class RetentionCountRepositoryPurge try { ContentItem item = repository.toItem( path ); - if (item instanceof Artifact ) + BaseRepositoryContentLayout layout = repository.getLayout( BaseRepositoryContentLayout.class ); + Artifact artifact = layout.adaptItem( Artifact.class, item ); + if ( !artifact.exists( ) ) { - Artifact artifact = (Artifact) item; - if (!artifact.exists()) { - return; - } + return; + } - if ( VersionUtil.isSnapshot( artifact.getVersion( ).getId() ) ) - { - ArchivaItemSelector selector = ArchivaItemSelector.builder( ) - .withNamespace( artifact.getVersion( ).getProject( ).getNamespace( ).getId( ) ) - .withProjectId( artifact.getVersion( ).getProject( ).getId( ) ) - .withArtifactId( artifact.getId( ) ) - .withVersion( artifact.getVersion( ).getId( ) ) - .withClassifier( "*" ) - .includeRelatedArtifacts() - .build( ); + if ( VersionUtil.isSnapshot( artifact.getVersion( ).getId( ) ) ) + { + ArchivaItemSelector selector = ArchivaItemSelector.builder( ) + .withNamespace( artifact.getVersion( ).getProject( ).getNamespace( ).getId( ) ) + .withProjectId( artifact.getVersion( ).getProject( ).getId( ) ) + .withArtifactId( artifact.getId( ) ) + .withVersion( artifact.getVersion( ).getId( ) ) + .withClassifier( "*" ) + .includeRelatedArtifacts( ) + .build( ); - List<String> versions; - try( Stream<? extends Artifact> stream = repository.getLayout( BaseRepositoryContentLayout.class ).newArtifactStream( selector) ){ - versions = stream.map( a -> a.getArtifactVersion( ) ) - .filter( StringUtils::isNotEmpty ) - .distinct() - .collect( Collectors.toList( ) ); - } + List<String> versions; + try ( Stream<? extends Artifact> stream = repository.getLayout( BaseRepositoryContentLayout.class ).newArtifactStream( selector ) ) + { + versions = stream.map( a -> a.getArtifactVersion( ) ) + .filter( StringUtils::isNotEmpty ) + .distinct( ) + .collect( Collectors.toList( ) ); + } + + Collections.sort( versions, VersionComparator.getInstance( ) ); - Collections.sort( versions, VersionComparator.getInstance( ) ); + if ( retentionCount > versions.size( ) ) + { + log.trace( "No deletion, because retention count is higher than actual number of artifacts." ); + // Done. nothing to do here. skip it. + return; + } - if ( retentionCount > versions.size( ) ) + ArchivaItemSelector.Builder selectorBuilder = ArchivaItemSelector.builder( ) + .withNamespace( artifact.getVersion( ).getProject( ).getNamespace( ).getId( ) ) + .withProjectId( artifact.getVersion( ).getProject( ).getId( ) ) + .withArtifactId( artifact.getId( ) ) + .withClassifier( "*" ) + .includeRelatedArtifacts( ) + .withVersion( artifact.getVersion( ).getId( ) ); + int countToPurge = versions.size( ) - retentionCount; + Set<Artifact> artifactsToDelete = new HashSet<>( ); + for ( String version : versions ) + { + if ( countToPurge-- <= 0 ) { - log.trace( "No deletion, because retention count is higher than actual number of artifacts." ); - // Done. nothing to do here. skip it. - return; + break; } - - ArchivaItemSelector.Builder selectorBuilder = ArchivaItemSelector.builder( ) - .withNamespace( artifact.getVersion( ).getProject( ).getNamespace( ).getId( ) ) - .withProjectId( artifact.getVersion( ).getProject( ).getId( ) ) - .withArtifactId( artifact.getId( ) ) - .withClassifier( "*" ) - .includeRelatedArtifacts() - .withVersion( artifact.getVersion( ).getId( ) ); - int countToPurge = versions.size( ) - retentionCount; - Set<Artifact> artifactsToDelete = new HashSet<>( ); - for ( String version : versions ) + List<? extends Artifact> delArtifacts = repository.getLayout( BaseRepositoryContentLayout.class ).getArtifacts( selectorBuilder.withArtifactVersion( version ).build( ) ); + if ( delArtifacts != null && delArtifacts.size( ) > 0 ) { - if ( countToPurge-- <= 0 ) - { - break; - } - List<? extends Artifact> delArtifacts = repository.getLayout( BaseRepositoryContentLayout.class ).getArtifacts( selectorBuilder.withArtifactVersion( version ).build( ) ); - if (delArtifacts!=null && delArtifacts.size()>0) - { - artifactsToDelete.addAll( delArtifacts ); - } + artifactsToDelete.addAll( delArtifacts ); } - purge( artifactsToDelete ); } - } else { - throw new RepositoryPurgeException( "Bad artifact path " + path ); + purge( artifactsToDelete ); } } catch ( LayoutException le ) diff --git a/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/RepositoryProxyHandler.java b/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/RepositoryProxyHandler.java index f775c3924..f6c9259a3 100644 --- a/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/RepositoryProxyHandler.java +++ b/archiva-modules/archiva-base/archiva-proxy-api/src/main/java/org/apache/archiva/proxy/model/RepositoryProxyHandler.java @@ -25,6 +25,7 @@ import org.apache.archiva.policies.ProxyDownloadException; import org.apache.archiva.repository.ManagedRepository; import org.apache.archiva.repository.RepositoryType; import org.apache.archiva.repository.content.Artifact; +import org.apache.archiva.repository.content.ItemSelector; import org.apache.archiva.repository.storage.StorageAsset; import java.util.List; @@ -90,6 +91,21 @@ public interface RepositoryProxyHandler throws ProxyDownloadException; /** + * Performs the artifact fetch operation against the target repositories + * of the provided source repository. + * <p> + * If the artifact is found, it is downloaded and placed into the source repository + * filesystem. + * + * @param repository the source repository to use. (must be a managed repository) + * @param artifactSelector the artifact to fetch. + * @return the file that was obtained, or null if no content was obtained + * @throws ProxyDownloadException if there was a problem fetching the content from the target repositories. + */ + StorageAsset fetchFromProxies( ManagedRepository repository, ItemSelector artifactSelector ) + throws ProxyDownloadException; + + /** * Performs the metadata fetch operation against the target repositories * of the provided source repository. * <p> diff --git a/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java b/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java index 8cf2e97d2..2373fb938 100644 --- a/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java +++ b/archiva-modules/archiva-base/archiva-proxy/src/main/java/org/apache/archiva/proxy/DefaultRepositoryProxyHandler.java @@ -50,6 +50,7 @@ import org.apache.archiva.repository.RemoteRepository; import org.apache.archiva.repository.RemoteRepositoryContent; import org.apache.archiva.repository.RepositoryType; import org.apache.archiva.repository.content.Artifact; +import org.apache.archiva.repository.content.ContentItem; import org.apache.archiva.repository.content.ItemSelector; import org.apache.archiva.repository.content.base.ArchivaItemSelector; import org.apache.archiva.repository.metadata.base.MetadataTools; @@ -207,6 +208,73 @@ public abstract class DefaultRepositoryProxyHandler implements RepositoryProxyHa } @Override + public StorageAsset fetchFromProxies( ManagedRepository repository, ItemSelector artifactSelector ) + throws ProxyDownloadException + { + Map<String, Exception> previousExceptions = new LinkedHashMap<>(); + ContentItem item = repository.getContent( ).getItem( artifactSelector ); + StorageAsset localFile = item.getAsset( ); + + Properties requestProperties = new Properties(); + requestProperties.setProperty( "filetype", "artifact" ); + requestProperties.setProperty( "version", artifactSelector.getVersion() ); + requestProperties.setProperty( "managedRepositoryId", repository.getId() ); + + List<ProxyConnector> connectors = getProxyConnectors( repository ); + for ( ProxyConnector connector : connectors ) + { + if ( !connector.isEnabled() ) + { + continue; + } + + RemoteRepository targetRepository = connector.getTargetRepository(); + requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() ); + + StorageAsset targetFile = targetRepository.getAsset( localFile.getPath( ) ); + // Removing the leading '/' from the path + String targetPath = targetFile.getPath( ).substring( 1 ); + try + { + StorageAsset downloadedFile = + transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties, + true ); + + if ( fileExists(downloadedFile) ) + { + log.debug( "Successfully transferred: {}", downloadedFile.getPath() ); + return downloadedFile; + } + } + catch ( NotFoundException e ) + { + log.debug( "Artifact {} not found on repository \"{}\".", item, + targetRepository.getId() ); + } + catch ( NotModifiedException e ) + { + log.debug( "Artifact {} not updated on repository \"{}\".", item, + targetRepository.getId() ); + } + catch ( ProxyException e ) + { + validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, item, + targetRepository.getContent(), localFile, e, previousExceptions ); + } + } + + if ( !previousExceptions.isEmpty() ) + { + throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories", + previousExceptions ); + } + + log.debug( "Exhausted all target repositories, artifact {} not found.", item ); + + return null; + } + + @Override public StorageAsset fetchFromProxies( ManagedRepository repository, ArtifactReference artifact ) throws ProxyDownloadException { @@ -736,7 +804,7 @@ public abstract class DefaultRepositoryProxyHandler implements RepositoryProxyHa } private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<Policy, PolicyOption> settings, - Properties request, Artifact artifact, RemoteRepositoryContent content, + Properties request, ContentItem artifact, RemoteRepositoryContent content, StorageAsset localFile, Exception exception, Map<String, Exception> previousExceptions ) throws ProxyDownloadException { @@ -784,7 +852,7 @@ public abstract class DefaultRepositoryProxyHandler implements RepositoryProxyHa log.warn( "Transfer error from repository {} for artifact {} , continuing to next repository. Error message: {}", - content.getRepository().getId(), artifact.getId(), exception.getMessage() ); + content.getRepository().getId(), artifact, exception.getMessage() ); log.debug( "Full stack trace", exception ); } diff --git a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryRequestInfo.java b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryRequestInfo.java index 04700e76b..52cfb2c22 100644 --- a/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryRequestInfo.java +++ b/archiva-modules/archiva-base/archiva-repository-api/src/main/java/org/apache/archiva/repository/RepositoryRequestInfo.java @@ -20,6 +20,7 @@ package org.apache.archiva.repository; */ import org.apache.archiva.model.ArtifactReference; +import org.apache.archiva.repository.content.ItemSelector; import org.apache.archiva.repository.features.RepositoryFeature; /** @@ -42,6 +43,15 @@ public interface RepositoryRequestInfo */ ArtifactReference toArtifactReference( String requestPath ) throws LayoutException; + + /** + * Returns the item selector that matches the given path. + * @param requestPath the request path which may be different from the filesystem structure + * @return the item selector + * @throws LayoutException if the path is not valid for the given repository layout + */ + ItemSelector toItemSelector( String requestPath ) throws LayoutException; + /** * <p> * Tests the path to see if it conforms to the expectations of a metadata request. diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java index 734174732..b66207dd8 100644 --- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java +++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContent.java @@ -20,6 +20,7 @@ package org.apache.archiva.repository.maven.content; import org.apache.archiva.common.filelock.FileLockManager; import org.apache.archiva.common.utils.FileUtils; +import org.apache.archiva.common.utils.VersionUtil; import org.apache.archiva.configuration.FileTypes; import org.apache.archiva.metadata.maven.MavenMetadataReader; import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator; @@ -186,30 +187,42 @@ public class ManagedDefaultRepositoryContent @Override public <T extends ContentItem> T adaptItem( Class<T> clazz, ContentItem item ) throws LayoutException { - if (clazz.isAssignableFrom( Version.class )) + try { - if ( !item.hasCharacteristic( Version.class ) ) + if ( clazz.isAssignableFrom( Version.class ) ) { - item.setCharacteristic( Version.class, createVersionFromPath( item.getAsset() ) ); + if ( !item.hasCharacteristic( Version.class ) ) + { + item.setCharacteristic( Version.class, createVersionFromPath( item.getAsset( ) ) ); + } + return (T) item.adapt( Version.class ); } - return (T) item.adapt( Version.class ); - } else if ( clazz.isAssignableFrom( Project.class )) { - if ( !item.hasCharacteristic( Project.class ) ) + else if ( clazz.isAssignableFrom( Project.class ) ) { - item.setCharacteristic( Project.class, createProjectFromPath( item.getAsset() ) ); + if ( !item.hasCharacteristic( Project.class ) ) + { + item.setCharacteristic( Project.class, createProjectFromPath( item.getAsset( ) ) ); + } + return (T) item.adapt( Project.class ); } - return (T) item.adapt( Project.class ); - } else if ( clazz.isAssignableFrom( Namespace.class )) { - if ( !item.hasCharacteristic( Namespace.class ) ) + else if ( clazz.isAssignableFrom( Namespace.class ) ) { - item.setCharacteristic( Namespace.class, createNamespaceFromPath( item.getAsset() ) ); + if ( !item.hasCharacteristic( Namespace.class ) ) + { + item.setCharacteristic( Namespace.class, createNamespaceFromPath( item.getAsset( ) ) ); + } + return (T) item.adapt( Namespace.class ); } - return (T) item.adapt( Namespace.class ); - } else if ( clazz.isAssignableFrom( Artifact.class )) { - if (!item.hasCharacteristic( Artifact.class )) { - item.setCharacteristic( Artifact.class, createArtifactFromPath( item.getAsset( ) ) ); + else if ( clazz.isAssignableFrom( Artifact.class ) ) + { + if ( !item.hasCharacteristic( Artifact.class ) ) + { + item.setCharacteristic( Artifact.class, createArtifactFromPath( item.getAsset( ) ) ); + } + return (T) item.adapt( Artifact.class ); } - return (T) item.adapt( Artifact.class ); + } catch (LayoutRuntimeException e) { + throw new LayoutException( e.getMessage( ), e ); } throw new LayoutException( "Could not convert item to class " + clazz); } @@ -593,6 +606,7 @@ public class ManagedDefaultRepositoryContent } } + private DataItem getDataItemFromPath( final StorageAsset artifactPath ) { final String contentType = getContentType( artifactPath ); @@ -644,13 +658,13 @@ public class ManagedDefaultRepositoryContent private ArtifactType artifactType = BaseArtifactTypes.MAIN; } - private ArtifactInfo getArtifactInfoFromPath( String genericVersion, StorageAsset path ) + private ArtifactInfo getArtifactInfoFromPath( final String genericVersion, final StorageAsset path ) { final ArtifactInfo info = new ArtifactInfo( ); info.asset = path; info.id = path.getParent( ).getParent( ).getName( ); final String fileName = path.getName( ); - if ( genericVersion.endsWith( "-" + SNAPSHOT ) ) + if ( VersionUtil.isGenericSnapshot( genericVersion ) ) { String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT ); String prefix = info.id + "-" + baseVersion + "-"; @@ -722,7 +736,7 @@ public class ManagedDefaultRepositoryContent else { String prefix = info.id + "-" + genericVersion; - if ( fileName.startsWith( prefix ) ) + if ( fileName.startsWith( prefix + "-") ) { info.version = genericVersion; String classPostfix = StringUtils.removeStart( fileName, prefix ); @@ -737,6 +751,24 @@ public class ManagedDefaultRepositoryContent info.classifier = ""; info.remainder = classPostfix; } + } else if (fileName.startsWith(prefix + ".")) { + info.version = genericVersion; + info.remainder = StringUtils.removeStart( fileName, prefix ); + info.classifier = ""; + } else if (fileName.startsWith(info.id+"-")) { + String postFix = StringUtils.removeStart( fileName, info.id + "-" ); + String versionPart = StringUtils.substringBefore( postFix, "." ); + if (VersionUtil.isVersion(versionPart)) { + info.version = versionPart; + info.remainder = StringUtils.removeStart( postFix, versionPart ); + info.classifier = ""; + } else { + info.version = ""; + info.classifier = ""; + int dotPos = fileName.indexOf( "." ); + info.remainder = fileName.substring( dotPos ); + } + } else { @@ -747,10 +779,10 @@ public class ManagedDefaultRepositoryContent else { info.id = fileName; + info.version = ""; } log.debug( "Artifact does not match the version pattern {}", path ); info.artifactType = BaseArtifactTypes.UNKNOWN; - info.version = ""; info.classifier = ""; info.remainder = StringUtils.substringAfterLast( fileName, "." ); } @@ -1454,16 +1486,40 @@ public class ManagedDefaultRepositoryContent @Override public ContentItem toItem( String path ) throws LayoutException { + StorageAsset asset = getRepository( ).getAsset( path ); - if ( asset.isLeaf( ) ) - { - ItemSelector selector = getPathParser( ).toItemSelector( path ); - return getItem( selector ); - } - else - { - return getItemFromPath( asset ); + ContentItem item = getItemFromPath( asset ); + if (item instanceof DataItem) { + Artifact artifact = adaptItem( Artifact.class, item ); + if (asset.getParent()==null) { + throw new LayoutException( "Path too short for maven artifact "+path ); + } + String version = asset.getParent( ).getName( ); + if (asset.getParent().getParent()==null) { + throw new LayoutException( "Path too short for maven artifact " + path ); + } + String project = item.getAsset( ).getParent( ).getParent( ).getName( ); + DataItem dataItem = (DataItem) item; + if (StringUtils.isEmpty( dataItem.getExtension())) { + throw new LayoutException( "Missing type on maven artifact" ); + } + if (!project.equals(artifact.getId())) { + throw new LayoutException( "The maven artifact id "+artifact.getId() +" does not match the project id: " + project); + } + boolean versionIsGenericSnapshot = VersionUtil.isGenericSnapshot( version ); + boolean artifactVersionIsSnapshot = VersionUtil.isSnapshot( artifact.getArtifactVersion() ); + if ( versionIsGenericSnapshot && !artifactVersionIsSnapshot ) { + throw new LayoutException( "The maven artifact has no snapshot version in snapshot directory " + dataItem ); + } + if ( !versionIsGenericSnapshot && artifactVersionIsSnapshot) { + throw new LayoutException( "The maven artifact version " + artifact.getArtifactVersion() + " is a snapshot version but inside a non snapshot directory " + version ); + } + if ( !versionIsGenericSnapshot && !version.equals( artifact.getArtifactVersion() ) ) + { + throw new LayoutException( "The maven artifact version " + artifact.getArtifactVersion() + " does not match the version directory " + version ); + } } + return item; } @Override diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/MavenRepositoryRequestInfo.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/MavenRepositoryRequestInfo.java index 08afdcfbe..3a6145567 100644 --- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/MavenRepositoryRequestInfo.java +++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/content/MavenRepositoryRequestInfo.java @@ -20,6 +20,7 @@ package org.apache.archiva.repository.maven.content; import org.apache.archiva.model.ArtifactReference; import org.apache.archiva.repository.*; +import org.apache.archiva.repository.content.ItemSelector; import org.apache.archiva.repository.content.PathParser; import org.apache.archiva.repository.features.RepositoryFeature; import org.apache.archiva.repository.metadata.base.MetadataTools; @@ -82,6 +83,12 @@ public class MavenRepositoryRequestInfo implements RepositoryRequestInfo } } + @Override + public ItemSelector toItemSelector( String requestPath ) throws LayoutException + { + return repository.getContent( ).toItemSelector( requestPath ); + } + /** * <p> * Tests the path to see if it conforms to the expectations of a metadata request. @@ -275,12 +282,18 @@ public class MavenRepositoryRequestInfo implements RepositoryRequestInfo * Default layout is the only layout that can contain maven-metadata.xml files, and * if the managedRepository is layout legacy, this request would never occur. */ - return requestedPath; + if (requestedPath.startsWith( "/" )) { + return requestedPath; + } else + { + return "/"+requestedPath; + } } + + // Treat as an artifact reference. - ArtifactReference ref = toArtifactReference( referencedResource ); - String adjustedPath = repository.getContent().toPath( ref ); + String adjustedPath = repository.getContent( ).toPath( repository.getContent( ).toItem( requestedPath ) ); return adjustedPath + supportfile; } diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/metadata/storage/Maven2RepositoryStorage.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/metadata/storage/Maven2RepositoryStorage.java index 51dec603f..e1c61aad2 100644 --- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/metadata/storage/Maven2RepositoryStorage.java +++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/repository/maven/metadata/storage/Maven2RepositoryStorage.java @@ -644,6 +644,98 @@ public class Maven2RepositoryStorage } } + @Override + public ItemSelector applyServerSideRelocation(ManagedRepository managedRepository, ItemSelector artifactSelector) + throws ProxyDownloadException { + if ("pom".equals(artifactSelector.getType())) { + return artifactSelector; + } + + // Build the artifact POM reference + BaseRepositoryContentLayout layout; + try + { + layout = managedRepository.getContent( ).getLayout( BaseRepositoryContentLayout.class ); + } + catch ( LayoutException e ) + { + throw new ProxyDownloadException( "Could not set layout " + e.getMessage( ), new HashMap<>( ) ); + } + + RepositoryType repositoryType = managedRepository.getType(); + if (!proxyRegistry.hasHandler(repositoryType)) { + throw new ProxyDownloadException("No proxy handler found for repository type " + repositoryType, new HashMap<>()); + } + + + + ItemSelector selector = ArchivaItemSelector.builder( ) + .withNamespace( artifactSelector.getNamespace( ) ) + .withProjectId( artifactSelector.getArtifactId( ) ) + .withArtifactId( artifactSelector.getArtifactId( ) ) + .withVersion( artifactSelector.getVersion( ) ) + .withArtifactVersion( artifactSelector.getVersion( ) ) + .withType( "pom" ).build( ); + + Artifact pom = layout.getArtifact( selector ); + + RepositoryProxyHandler proxyHandler = proxyRegistry.getHandler(repositoryType).get(0); + + // Get the artifact POM from proxied repositories if needed + proxyHandler.fetchFromProxies(managedRepository, pom); + + // Open and read the POM from the managed repo + + if (!pom.exists()) { + return artifactSelector; + } + + try { + // MavenXpp3Reader leaves the file open, so we need to close it ourselves. + + Model model; + try (Reader reader = Channels.newReader(pom.getAsset().getReadChannel(), Charset.defaultCharset().name())) { + model = MAVEN_XPP_3_READER.read(reader); + } + + DistributionManagement dist = model.getDistributionManagement(); + if (dist != null) { + Relocation relocation = dist.getRelocation(); + if (relocation != null) { + ArchivaItemSelector.Builder relocatedBuilder = ArchivaItemSelector.builder( ); + // artifact is relocated : update the repositoryPath + if (relocation.getGroupId() != null) { + relocatedBuilder.withNamespace( relocation.getGroupId( ) ); + } else { + relocatedBuilder.withNamespace( artifactSelector.getNamespace( ) ); + } + if (relocation.getArtifactId() != null) { + relocatedBuilder.withArtifactId( relocation.getArtifactId( ) ); + } else { + relocatedBuilder.withArtifactId( artifactSelector.getArtifactId( ) ); + } + if (relocation.getVersion() != null) + { + relocatedBuilder.withVersion( relocation.getVersion( ) ); + } else { + relocatedBuilder.withVersion( artifactSelector.getVersion( ) ); + } + return relocatedBuilder.withArtifactVersion( artifactSelector.getArtifactVersion( ) ) + .withClassifier( artifactSelector.getClassifier( ) ) + .withType( artifactSelector.getType( ) ) + .withProjectId( artifactSelector.getProjectId( ) ) + .withExtension( artifactSelector.getExtension( ) ) + .build( ); + } + } + } catch (IOException e) { + // Unable to read POM : ignore. + } catch (XmlPullParserException e) { + // Invalid POM : ignore + } + return artifactSelector; + } + @Override public String getFilePath(String requestPath, org.apache.archiva.repository.ManagedRepository managedRepository) { diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java index 532997b46..d86e75930 100644 --- a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java +++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/ManagedDefaultRepositoryContentTest.java @@ -1014,7 +1014,7 @@ public class ManagedDefaultRepositoryContentTest path = "/org/apache/maven/shared/maven-downloader/1.1/maven-downloader-1.1.jar"; item = repoContent.toItem( path ); assertNotNull( item ); - assertTrue( item instanceof Artifact ); + assertTrue( item instanceof DataItem ); } diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/MavenRepositoryRequestInfoTest.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/MavenRepositoryRequestInfoTest.java index 6e8fe08b5..7317e57f3 100644 --- a/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/MavenRepositoryRequestInfoTest.java +++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/test/java/org/apache/archiva/repository/maven/content/MavenRepositoryRequestInfoTest.java @@ -72,6 +72,9 @@ public class MavenRepositoryRequestInfoTest @Inject FileLockManager fileLockManager; + @Inject + MavenContentHelper mavenContentHelper; + private MavenRepositoryRequestInfo repoRequest; @@ -109,6 +112,8 @@ public class MavenRepositoryRequestInfoTest ManagedDefaultRepositoryContent repoContent = new ManagedDefaultRepositoryContent(repository, artifactMappingProviders, fileTypes, fileLockManager); //repoContent = (ManagedRepositoryContent) lookup( ManagedRepositoryContent.class, "default" ); repository.setContent(repoContent); + repoContent.setMavenContentHelper( mavenContentHelper ); + repoRequest = new MavenRepositoryRequestInfo(repository); } @@ -430,7 +435,7 @@ public class MavenRepositoryRequestInfoTest ManagedRepositoryContent repository = createManagedRepo( "default" ); // Test (artifact) default to default - dual extension - assertEquals( "org/project/example-presentation/3.2/example-presentation-3.2.xml.zip", + assertEquals( "/org/project/example-presentation/3.2/example-presentation-3.2.xml.zip", repoRequest.toNativePath( "org/project/example-presentation/3.2/example-presentation-3.2.xml.zip") ); } @@ -442,7 +447,7 @@ public class MavenRepositoryRequestInfoTest ManagedRepositoryContent repository = createManagedRepo( "default" ); // Test (metadata) default to default - assertEquals( "org/apache/derby/derby/10.2.2.0/maven-metadata.xml.sha1", + assertEquals( "/org/apache/derby/derby/10.2.2.0/maven-metadata.xml.sha1", repoRequest.toNativePath( "org/apache/derby/derby/10.2.2.0/maven-metadata.xml.sha1") ); } diff --git a/archiva-modules/archiva-web/archiva-security/src/test/java/org/apache/archiva/security/mock/MockBeanServices.java b/archiva-modules/archiva-web/archiva-security/src/test/java/org/apache/archiva/security/mock/MockBeanServices.java index 9fb1184c6..edb686bff 100644 --- a/archiva-modules/archiva-web/archiva-security/src/test/java/org/apache/archiva/security/mock/MockBeanServices.java +++ b/archiva-modules/archiva-web/archiva-security/src/test/java/org/apache/archiva/security/mock/MockBeanServices.java @@ -31,6 +31,7 @@ import org.apache.archiva.components.taskqueue.TaskQueueException; import org.apache.archiva.repository.ManagedRepositoryContent; import org.apache.archiva.repository.ManagedRepository; import org.apache.archiva.metadata.audit.RepositoryListener; +import org.apache.archiva.repository.content.ItemSelector; import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler; import org.apache.archiva.scheduler.repository.model.RepositoryTask; import org.apache.archiva.xml.XMLException; @@ -108,6 +109,12 @@ public class MockBeanServices } + @Override + public ItemSelector applyServerSideRelocation( ManagedRepository managedRepository, ItemSelector selector ) throws ProxyDownloadException + { + return null; + } + @Override public void deleteArtifact( MetadataRepository metadataRepository, String repositoryId, String namespace, diff --git a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResourceFactory.java b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResourceFactory.java index 8528b658c..b83dcdb61 100644 --- a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResourceFactory.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/ArchivaDavResourceFactory.java @@ -68,6 +68,7 @@ import org.apache.archiva.repository.RepositoryRegistry; import org.apache.archiva.repository.RepositoryRequestInfo; import org.apache.archiva.repository.content.Artifact; import org.apache.archiva.repository.content.ContentItem; +import org.apache.archiva.repository.content.ItemSelector; import org.apache.archiva.repository.storage.fs.FilesystemStorage; import org.apache.archiva.repository.storage.StorageAsset; import org.apache.archiva.metadata.audit.AuditListener; @@ -791,22 +792,23 @@ public class ArchivaDavResourceFactory try { // Get the artifact reference in a layout neutral way. - ArtifactReference artifact = repositoryRequestInfo.toArtifactReference( path ); +// ArtifactReference artifact = repositoryRequestInfo.toArtifactReference( path ); + ItemSelector selector = repositoryRequestInfo.toItemSelector( path ); - if ( artifact != null ) + if ( selector != null ) { String repositoryLayout = managedRepository.getLayout(); RepositoryStorage repositoryStorage = this.applicationContext.getBean( "repositoryStorage#" + repositoryLayout, RepositoryStorage.class ); - repositoryStorage.applyServerSideRelocation( managedRepository, artifact ); + selector = repositoryStorage.applyServerSideRelocation( managedRepository, selector ); - StorageAsset proxiedFile = proxyHandler.fetchFromProxies( managedRepository, artifact ); + StorageAsset proxiedFile = proxyHandler.fetchFromProxies( managedRepository, selector ); - resource.setPath( managedRepository.getContent().toPath( artifact ) ); + resource.setPath( managedRepository.getContent().toPath( selector ) ); - log.debug( "Proxied artifact '{}:{}:{}'", artifact.getGroupId(), artifact.getArtifactId(), - artifact.getVersion() ); + log.debug( "Proxied artifact '{}:{}:{}:{}'", selector.getNamespace(), selector.getArtifactId(), + selector.getVersion(), selector.getArtifactVersion() ); return ( proxiedFile != null ); } diff --git a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/storage/RepositoryStorage.java b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/storage/RepositoryStorage.java index 6559be1c6..e56311506 100644 --- a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/storage/RepositoryStorage.java +++ b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/storage/RepositoryStorage.java @@ -27,6 +27,7 @@ import org.apache.archiva.model.ArtifactReference; import org.apache.archiva.policies.ProxyDownloadException; import org.apache.archiva.repository.ManagedRepositoryContent; import org.apache.archiva.repository.ManagedRepository; +import org.apache.archiva.repository.content.ItemSelector; import org.apache.archiva.xml.XMLException; import java.io.IOException; @@ -75,6 +76,21 @@ public interface RepositoryStorage throws ProxyDownloadException; /** + * A relocation capable client will request the POM prior to the artifact, and will then read meta-data and do + * client side relocation. A simplier client (like maven 1) will only request the artifact and not use the + * metadatas. + * <p> + * For such clients, archiva does server-side relocation by reading itself the <relocation> element in + * metadatas and serving the expected artifact. + * @param managedRepository the used managed repository + * @param artifact the artifact reference + * @throws org.apache.archiva.policies.ProxyDownloadException + */ + ItemSelector applyServerSideRelocation( ManagedRepository managedRepository, ItemSelector selector ) + throws ProxyDownloadException; + + + /** * add an other method to evaluate real path as when receiving -SNAPSHOT (for maven storage) * request redirect to the last build * @param requestPath the web uri request diff --git a/archiva-modules/plugins/metadata-store-cassandra/src/test/java/org/apache/archiva/metadata/repository/cassandra/MockRepositoryStorage.java b/archiva-modules/plugins/metadata-store-cassandra/src/test/java/org/apache/archiva/metadata/repository/cassandra/MockRepositoryStorage.java index ab520236f..d320ecbb4 100644 --- a/archiva-modules/plugins/metadata-store-cassandra/src/test/java/org/apache/archiva/metadata/repository/cassandra/MockRepositoryStorage.java +++ b/archiva-modules/plugins/metadata-store-cassandra/src/test/java/org/apache/archiva/metadata/repository/cassandra/MockRepositoryStorage.java @@ -37,6 +37,7 @@ import org.apache.archiva.policies.ProxyDownloadException; import org.apache.archiva.repository.ManagedRepositoryContent; import org.apache.archiva.repository.ManagedRepository; import org.apache.archiva.metadata.audit.RepositoryListener; +import org.apache.archiva.repository.content.ItemSelector; import org.apache.archiva.xml.XMLException; import java.io.IOException; @@ -113,6 +114,12 @@ public class MockRepositoryStorage } @Override + public ItemSelector applyServerSideRelocation( ManagedRepository managedRepository, ItemSelector selector ) throws ProxyDownloadException + { + return null; + } + + @Override public String getFilePath( String requestPath, org.apache.archiva.repository.ManagedRepository managedRepository ) { return null; |