diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java index a338c6d6c..c7619414c 100644 --- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java +++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java @@ -50,6 +50,7 @@ import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmHelper; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; +import org.eclipse.aether.spi.connector.checksum.ProvidedChecksumsSource; import org.eclipse.aether.spi.connector.layout.RepositoryLayout; import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; import org.eclipse.aether.spi.connector.transport.GetTask; @@ -88,6 +89,8 @@ final class BasicRepositoryConnector private static final Logger LOGGER = LoggerFactory.getLogger( BasicRepositoryConnector.class ); + private final Map providedChecksumsSources; + private final FileProcessor fileProcessor; private final RemoteRepository repository; @@ -117,7 +120,8 @@ final class BasicRepositoryConnector TransporterProvider transporterProvider, RepositoryLayoutProvider layoutProvider, ChecksumPolicyProvider checksumPolicyProvider, - FileProcessor fileProcessor ) + FileProcessor fileProcessor, + Map providedChecksumsSources ) throws NoRepositoryConnectorException { try @@ -141,6 +145,7 @@ final class BasicRepositoryConnector this.session = session; this.repository = repository; this.fileProcessor = fileProcessor; + this.providedChecksumsSources = providedChecksumsSources; maxThreads = ConfigUtils.getInteger( session, 5, CONFIG_PROP_THREADS, "maven.artifact.threads" ); smartChecksums = ConfigUtils.getBoolean( session, true, CONFIG_PROP_SMART_CHECKSUMS ); @@ -239,12 +244,25 @@ public void get( Collection artifactDownloads, } Runnable task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy, - checksumLocations, listener ); + checksumLocations, null, listener ); executor.execute( errorForwarder.wrap( task ) ); } for ( ArtifactDownload transfer : safe( artifactDownloads ) ) { + Map providedChecksums = Collections.emptyMap(); + for ( ProvidedChecksumsSource providedChecksumsSource : providedChecksumsSources.values() ) + { + Map provided = providedChecksumsSource.getProvidedArtifactChecksums( + session, transfer, layout.getChecksumAlgorithmFactories() ); + + if ( provided != null ) + { + providedChecksums = provided; + break; + } + } + URI location = layout.getLocation( transfer.getArtifact(), false ); TransferResource resource = newTransferResource( location, transfer.getFile(), transfer.getTrace() ); @@ -265,7 +283,8 @@ public void get( Collection artifactDownloads, checksumLocations = layout.getChecksumLocations( transfer.getArtifact(), false, location ); } - task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy, checksumLocations, listener ); + task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy, + checksumLocations, providedChecksums, listener ); } executor.execute( errorForwarder.wrap( task ) ); } @@ -416,12 +435,13 @@ class GetTaskRunner GetTaskRunner( URI path, File file, ChecksumPolicy checksumPolicy, List checksumLocations, + Map providedChecksums, TransferTransportListener listener ) { super( path, listener ); this.file = requireNonNull( file, "destination file cannot be null" ); checksumValidator = new ChecksumValidator( file, fileProcessor, this, - checksumPolicy, safe( checksumLocations ) ); + checksumPolicy, providedChecksums, safe( checksumLocations ) ); } @Override diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java index aa747a98c..280df6d0a 100644 --- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java +++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java @@ -19,6 +19,9 @@ * under the License. */ +import java.util.Collections; +import java.util.Map; + import javax.inject.Inject; import javax.inject.Named; @@ -29,6 +32,7 @@ import org.eclipse.aether.spi.connector.RepositoryConnector; import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; +import org.eclipse.aether.spi.connector.checksum.ProvidedChecksumsSource; import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; import org.eclipse.aether.spi.connector.transport.TransporterProvider; import org.eclipse.aether.spi.io.FileProcessor; @@ -53,6 +57,8 @@ public final class BasicRepositoryConnectorFactory private FileProcessor fileProcessor; + private Map providedChecksumsSources; + private float priority; /** @@ -66,13 +72,17 @@ public BasicRepositoryConnectorFactory() } @Inject - BasicRepositoryConnectorFactory( TransporterProvider transporterProvider, RepositoryLayoutProvider layoutProvider, - ChecksumPolicyProvider checksumPolicyProvider, FileProcessor fileProcessor ) + BasicRepositoryConnectorFactory( TransporterProvider transporterProvider, + RepositoryLayoutProvider layoutProvider, + ChecksumPolicyProvider checksumPolicyProvider, + FileProcessor fileProcessor, + Map providedChecksumsSources ) { setTransporterProvider( transporterProvider ); setRepositoryLayoutProvider( layoutProvider ); setChecksumPolicyProvider( checksumPolicyProvider ); setFileProcessor( fileProcessor ); + setProvidedChecksumSources( providedChecksumsSources ); } public void initService( ServiceLocator locator ) @@ -81,6 +91,7 @@ public void initService( ServiceLocator locator ) setRepositoryLayoutProvider( locator.getService( RepositoryLayoutProvider.class ) ); setChecksumPolicyProvider( locator.getService( ChecksumPolicyProvider.class ) ); setFileProcessor( locator.getService( FileProcessor.class ) ); + setProvidedChecksumSources( Collections.emptyMap() ); } /** @@ -132,6 +143,22 @@ public BasicRepositoryConnectorFactory setFileProcessor( FileProcessor fileProce return this; } + /** + * Sets the provided checksum sources to use for this component. + * + * @param providedChecksumsSources The provided checksum sources to use, must not be {@code null}. + * @return This component for chaining, never {@code null}. + * @since 1.8.0 + */ + public BasicRepositoryConnectorFactory setProvidedChecksumSources( + Map providedChecksumsSources ) + { + this.providedChecksumsSources = requireNonNull( + providedChecksumsSources, "provided checksum sources cannot be null" + ); + return this; + } + public float getPriority() { return priority; @@ -156,7 +183,7 @@ public RepositoryConnector newInstance( RepositorySystemSession session, RemoteR requireNonNull( "repository", "repository cannot be null" ); return new BasicRepositoryConnector( session, repository, transporterProvider, layoutProvider, - checksumPolicyProvider, fileProcessor ); + checksumPolicyProvider, fileProcessor, providedChecksumsSources ); } } diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java index 2e4e3b7ce..cd2ddf7d2 100644 --- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java +++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java @@ -30,6 +30,7 @@ import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy; +import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind; import org.eclipse.aether.spi.connector.layout.RepositoryLayout.ChecksumLocation; import org.eclipse.aether.spi.io.FileProcessor; import org.eclipse.aether.transfer.ChecksumFailureException; @@ -45,6 +46,10 @@ final class ChecksumValidator interface ChecksumFetcher { + /** + * Fetches the checksums from remote location into provided local file. The checksums fetched in this way + * are of kind {@link ChecksumKind#REMOTE_EXTERNAL}. + */ boolean fetchChecksum( URI remote, File local ) throws Exception; @@ -62,6 +67,8 @@ boolean fetchChecksum( URI remote, File local ) private final ChecksumPolicy checksumPolicy; + private final Map providedChecksums; + private final Collection checksumLocations; private final Map checksumFiles; @@ -70,6 +77,7 @@ boolean fetchChecksum( URI remote, File local ) FileProcessor fileProcessor, ChecksumFetcher checksumFetcher, ChecksumPolicy checksumPolicy, + Map providedChecksums, Collection checksumLocations ) { this.dataFile = dataFile; @@ -77,6 +85,7 @@ boolean fetchChecksum( URI remote, File local ) this.fileProcessor = fileProcessor; this.checksumFetcher = checksumFetcher; this.checksumPolicy = checksumPolicy; + this.providedChecksums = providedChecksums; this.checksumLocations = checksumLocations; this.checksumFiles = new HashMap<>(); } @@ -90,14 +99,20 @@ public ChecksumCalculator newChecksumCalculator( File targetFile ) return null; } - public void validate( Map actualChecksums, Map inlinedChecksums ) + public void validate( Map actualChecksums, Map includedChecksums ) throws ChecksumFailureException { if ( checksumPolicy == null ) { return; } - if ( inlinedChecksums != null && validateInlinedChecksums( actualChecksums, inlinedChecksums ) ) + if ( providedChecksums != null + && validateChecksums( actualChecksums, ChecksumKind.PROVIDED, providedChecksums ) ) + { + return; + } + if ( includedChecksums != null + && validateChecksums( actualChecksums, ChecksumKind.REMOTE_INCLUDED, includedChecksums ) ) { return; } @@ -108,10 +123,10 @@ public void validate( Map actualChecksums, Map inlinedChec checksumPolicy.onNoMoreChecksums(); } - private boolean validateInlinedChecksums( Map actualChecksums, Map inlinedChecksums ) + private boolean validateChecksums( Map actualChecksums, ChecksumKind kind, Map checksums ) throws ChecksumFailureException { - for ( Map.Entry entry : inlinedChecksums.entrySet() ) + for ( Map.Entry entry : checksums.entrySet() ) { String algo = entry.getKey(); Object calculated = actualChecksums.get( algo ); @@ -135,10 +150,10 @@ private boolean validateInlinedChecksums( Map actualChecksums, Map actualChecksums ) if ( calculated instanceof Exception ) { checksumPolicy.onChecksumError( - factory.getName(), 0, new ChecksumFailureException( (Exception) calculated ) ); + factory.getName(), ChecksumKind.REMOTE_EXTERNAL, + new ChecksumFailureException( (Exception) calculated ) + ); continue; } try @@ -165,14 +182,18 @@ private boolean validateExternalChecksums( Map actualChecksums ) File tmp = createTempFile( checksumFile ); try { - if ( !checksumFetcher.fetchChecksum( checksumLocation.getLocation(), tmp ) ) + if ( !checksumFetcher.fetchChecksum( + checksumLocation.getLocation(), tmp + ) ) { continue; } } catch ( Exception e ) { - checksumPolicy.onChecksumError( factory.getName(), 0, new ChecksumFailureException( e ) ); + checksumPolicy.onChecksumError( + factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException( e ) + ); continue; } @@ -183,16 +204,20 @@ private boolean validateExternalChecksums( Map actualChecksums ) if ( !isEqualChecksum( expected, actual ) ) { checksumPolicy.onChecksumMismatch( - factory.getName(), 0, new ChecksumFailureException( expected, actual ) ); + factory.getName(), ChecksumKind.REMOTE_EXTERNAL, + new ChecksumFailureException( expected, actual ) + ); } - else if ( checksumPolicy.onChecksumMatch( factory.getName(), 0 ) ) + else if ( checksumPolicy.onChecksumMatch( factory.getName(), ChecksumKind.REMOTE_EXTERNAL ) ) { return true; } } catch ( IOException e ) { - checksumPolicy.onChecksumError( factory.getName(), 0, new ChecksumFailureException( e ) ); + checksumPolicy.onChecksumError( + factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException( e ) + ); } } return false; diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java index aac3c1b0a..5e4f121dd 100644 --- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java +++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java @@ -57,9 +57,9 @@ private static class StubChecksumPolicy private Object conclusion; @Override - public boolean onChecksumMatch( String algorithm, int kind ) + public boolean onChecksumMatch( String algorithm, ChecksumKind kind ) { - callbacks.add( String.format( "match(%s, %04x)", algorithm, kind ) ); + callbacks.add( String.format( "match(%s, %s)", algorithm, kind ) ); if ( inspectAll ) { if ( conclusion == null ) @@ -72,10 +72,10 @@ public boolean onChecksumMatch( String algorithm, int kind ) } @Override - public void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception ) + public void onChecksumMismatch( String algorithm, ChecksumKind kind, ChecksumFailureException exception ) throws ChecksumFailureException { - callbacks.add( String.format( "mismatch(%s, %04x)", algorithm, kind ) ); + callbacks.add( String.format( "mismatch(%s, %s)", algorithm, kind ) ); if ( inspectAll ) { conclusion = exception; @@ -85,9 +85,9 @@ public void onChecksumMismatch( String algorithm, int kind, ChecksumFailureExcep } @Override - public void onChecksumError( String algorithm, int kind, ChecksumFailureException exception ) + public void onChecksumError( String algorithm, ChecksumKind kind, ChecksumFailureException exception ) { - callbacks.add( String.format( "error(%s, %04x, %s)", algorithm, kind, exception.getCause().getMessage() ) ); + callbacks.add( String.format( "error(%s, %s, %s)", algorithm, kind, exception.getCause().getMessage() ) ); } @Override @@ -201,7 +201,12 @@ private List newChecksums( String... factorie private ChecksumValidator newValidator( String... factories ) { - return new ChecksumValidator( dataFile, new TestFileProcessor(), fetcher, policy, newChecksums( factories ) ); + return newValidator( null, factories ); + } + + private ChecksumValidator newValidator( Map providedChecksums, String... factories ) + { + return new ChecksumValidator( dataFile, new TestFileProcessor(), fetcher, policy, providedChecksums, newChecksums( factories ) ); } private Map checksums( String... algoDigestPairs ) @@ -251,7 +256,7 @@ public void testValidate_AcceptOnFirstMatch() fetcher.mock( SHA1, "foo" ); validator.validate( checksums( SHA1, "foo" ), null ); fetcher.assertFetchedFiles( SHA1 ); - policy.assertCallbacks( "match(SHA-1, 0000)" ); + policy.assertCallbacks( "match(SHA-1, REMOTE_EXTERNAL)" ); } @Test @@ -271,7 +276,7 @@ public void testValidate_FailOnFirstMismatch() assertTrue( e.isRetryWorthy() ); } fetcher.assertFetchedFiles( SHA1 ); - policy.assertCallbacks( "mismatch(SHA-1, 0000)" ); + policy.assertCallbacks( "mismatch(SHA-1, REMOTE_EXTERNAL)" ); } @Test @@ -284,7 +289,7 @@ public void testValidate_AcceptOnEnd() fetcher.mock( MD5, "bar" ); validator.validate( checksums( SHA1, "foo", MD5, "bar" ), null ); fetcher.assertFetchedFiles( SHA1, MD5 ); - policy.assertCallbacks( "match(SHA-1, 0000)", "match(MD5, 0000)", "noMore()" ); + policy.assertCallbacks( "match(SHA-1, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()" ); } @Test @@ -306,21 +311,23 @@ public void testValidate_FailOnEnd() assertTrue( e.isRetryWorthy() ); } fetcher.assertFetchedFiles( SHA1, MD5 ); - policy.assertCallbacks( "mismatch(SHA-1, 0000)", "match(MD5, 0000)", "noMore()" ); + policy.assertCallbacks( "mismatch(SHA-1, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()" ); } @Test - public void testValidate_InlinedBeforeExternal() + public void testValidate_IncludedBeforeExternal() throws Exception { policy.inspectAll = true; - ChecksumValidator validator = newValidator( SHA1, MD5 ); + HashMap provided = new HashMap<>(); + provided.put( SHA1, "foo" ); + ChecksumValidator validator = newValidator( provided, SHA1, MD5 ); fetcher.mock( SHA1, "foo" ); fetcher.mock( MD5, "bar" ); validator.validate( checksums( SHA1, "foo", MD5, "bar" ), checksums( SHA1, "foo", MD5, "bar" ) ); fetcher.assertFetchedFiles( SHA1, MD5 ); - policy.assertCallbacks( "match(SHA-1, 0001)", "match(MD5, 0001)", "match(SHA-1, 0000)", "match(MD5, 0000)", - "noMore()" ); + policy.assertCallbacks( "match(SHA-1, PROVIDED)", "match(SHA-1, REMOTE_INCLUDED)", "match(MD5, REMOTE_INCLUDED)", + "match(SHA-1, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()" ); } @Test @@ -331,7 +338,7 @@ public void testValidate_CaseInsensitive() ChecksumValidator validator = newValidator( SHA1 ); fetcher.mock( SHA1, "FOO" ); validator.validate( checksums( SHA1, "foo" ), checksums( SHA1, "foo" ) ); - policy.assertCallbacks( "match(SHA-1, 0001)", "match(SHA-1, 0000)", "noMore()" ); + policy.assertCallbacks( "match(SHA-1, REMOTE_INCLUDED)", "match(SHA-1, REMOTE_EXTERNAL)", "noMore()" ); } @Test @@ -342,7 +349,7 @@ public void testValidate_MissingRemoteChecksum() fetcher.mock( MD5, "bar" ); validator.validate( checksums( MD5, "bar" ), null ); fetcher.assertFetchedFiles( SHA1, MD5 ); - policy.assertCallbacks( "match(MD5, 0000)" ); + policy.assertCallbacks( "match(MD5, REMOTE_EXTERNAL)" ); } @Test @@ -354,7 +361,7 @@ public void testValidate_InaccessibleRemoteChecksum() fetcher.mock( MD5, "bar" ); validator.validate( checksums( MD5, "bar" ), null ); fetcher.assertFetchedFiles( SHA1, MD5 ); - policy.assertCallbacks( "error(SHA-1, 0000, inaccessible)", "match(MD5, 0000)" ); + policy.assertCallbacks( "error(SHA-1, REMOTE_EXTERNAL, inaccessible)", "match(MD5, REMOTE_EXTERNAL)" ); } @Test @@ -366,7 +373,7 @@ public void testValidate_InaccessibleLocalChecksum() fetcher.mock( MD5, "bar" ); validator.validate( checksums( SHA1, null, MD5, "bar" ), null ); fetcher.assertFetchedFiles( MD5 ); - policy.assertCallbacks( "error(SHA-1, 0000, error)", "match(MD5, 0000)" ); + policy.assertCallbacks( "error(SHA-1, REMOTE_EXTERNAL, error)", "match(MD5, REMOTE_EXTERNAL)" ); } @Test diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java index 8ebddaaf7..6a88470f7 100644 --- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java +++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java @@ -51,7 +51,7 @@ public class TestChecksumAlgorithmSelector public static final String TEST_CHECKSUM_VALUE = "01020304"; @Override - public Set getChecksumAlgorithmNames() + public Set getChecksumAlgorithmFactories() { return Collections.emptySet(); // irrelevant } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java index c324fa9e8..9629dc979 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java @@ -41,6 +41,7 @@ import org.eclipse.aether.impl.RepositoryConnectorProvider; import org.eclipse.aether.impl.RepositoryEventDispatcher; import org.eclipse.aether.internal.impl.DefaultTrackingFileManager; +import org.eclipse.aether.internal.impl.FileProvidedChecksumsSource; import org.eclipse.aether.internal.impl.TrackingFileManager; import org.eclipse.aether.internal.impl.checksum.Md5ChecksumAlgorithmFactory; import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory; @@ -83,6 +84,7 @@ import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory; import org.eclipse.aether.named.providers.NoopNamedLockFactory; +import org.eclipse.aether.spi.connector.checksum.ProvidedChecksumsSource; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector; import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; @@ -168,6 +170,9 @@ protected void configure() .to( EnhancedLocalRepositoryManagerFactory.class ).in( Singleton.class ); bind( TrackingFileManager.class ).to( DefaultTrackingFileManager.class ).in( Singleton.class ); + bind( ProvidedChecksumsSource.class ).annotatedWith( Names.named( FileProvidedChecksumsSource.NAME ) ) // + .to( FileProvidedChecksumsSource.class ).in( Singleton.class ); + bind( ChecksumAlgorithmFactory.class ).annotatedWith( Names.named( Md5ChecksumAlgorithmFactory.NAME ) ) .to( Md5ChecksumAlgorithmFactory.class ); bind( ChecksumAlgorithmFactory.class ).annotatedWith( Names.named( Sha1ChecksumAlgorithmFactory.NAME ) ) @@ -207,6 +212,17 @@ protected void configure() } + @Provides + @Singleton + Map provideChecksumSources( + @Named( FileProvidedChecksumsSource.NAME ) ProvidedChecksumsSource fileProvidedChecksumSource + ) + { + Map providedChecksumsSource = new HashMap<>(); + providedChecksumsSource.put( FileProvidedChecksumsSource.NAME, fileProvidedChecksumSource ); + return providedChecksumsSource; + } + @Provides @Singleton Map provideChecksumTypes( diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/AbstractChecksumPolicy.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/AbstractChecksumPolicy.java index d5e1ecb28..edb00f7a7 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/AbstractChecksumPolicy.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/AbstractChecksumPolicy.java @@ -41,26 +41,26 @@ protected AbstractChecksumPolicy( TransferResource resource ) } @Override - public boolean onChecksumMatch( String algorithm, int kind ) + public boolean onChecksumMatch( String algorithm, ChecksumKind kind ) { requireNonNull( algorithm, "algorithm cannot be null" ); return true; } @Override - public void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception ) + public void onChecksumMismatch( String algorithm, ChecksumKind kind, ChecksumFailureException exception ) throws ChecksumFailureException { requireNonNull( algorithm, "algorithm cannot be null" ); requireNonNull( exception, "exception cannot be null" ); - if ( ( kind & KIND_UNOFFICIAL ) == 0 ) + if ( !kind.isIgnoreOnMismatch() ) { throw exception; } } @Override - public void onChecksumError( String algorithm, int kind, ChecksumFailureException exception ) + public void onChecksumError( String algorithm, ChecksumKind kind, ChecksumFailureException exception ) throws ChecksumFailureException { requireNonNull( algorithm, "algorithm cannot be null" ); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FileProvidedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FileProvidedChecksumsSource.java new file mode 100644 index 000000000..2289fa568 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FileProvidedChecksumsSource.java @@ -0,0 +1,169 @@ +package org.eclipse.aether.internal.impl; + +/* + * 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.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.spi.connector.ArtifactDownload; +import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; +import org.eclipse.aether.spi.connector.checksum.ProvidedChecksumsSource; +import org.eclipse.aether.spi.io.FileProcessor; +import org.eclipse.aether.util.ConfigUtils; +import org.eclipse.aether.util.artifact.ArtifactIdUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +/** + * Local filesystem backed {@link ProvidedChecksumsSource} implementation that use specified directory as base + * directory, where it expects artifacts checksums on standard Maven2 "local" layout. This implementation uses Artifact + * (and Metadata) coordinates solely to form path from baseDir (for Metadata file name is + * {@code maven-metadata-local.xml.sha1} in case of SHA-1 checksum). + * + * @since 1.8.0 + */ +@Singleton +@Named( FileProvidedChecksumsSource.NAME ) +public final class FileProvidedChecksumsSource + implements ProvidedChecksumsSource +{ + public static final String NAME = "file"; + + static final String CONFIG_PROP_BASE_DIR = "aether.artifactResolver.providedChecksumsSource.file.baseDir"; + + static final String LOCAL_REPO_PREFIX = ".checksums"; + + private static final Logger LOGGER = LoggerFactory.getLogger( FileProvidedChecksumsSource.class ); + + private final FileProcessor fileProcessor; + + private final SimpleLocalRepositoryManager simpleLocalRepositoryManager; + + @Inject + public FileProvidedChecksumsSource( FileProcessor fileProcessor ) + { + this.fileProcessor = requireNonNull( fileProcessor ); + // we really needs just "local layout" from it (relative paths), so baseDir here is irrelevant + this.simpleLocalRepositoryManager = new SimpleLocalRepositoryManager( new File( "" ) ); + } + + @Override + public Map getProvidedArtifactChecksums( RepositorySystemSession session, + ArtifactDownload transfer, + List checksumAlgorithmFactories ) + { + Path baseDir = getBaseDir( session ); + if ( baseDir == null ) + { + return null; + } + ArrayList checksumFilePaths = new ArrayList<>( checksumAlgorithmFactories.size() ); + for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories ) + { + checksumFilePaths.add( new ChecksumFilePath( + simpleLocalRepositoryManager.getPathForArtifact( transfer.getArtifact(), false ) + '.' + + checksumAlgorithmFactory.getFileExtension(), checksumAlgorithmFactory ) ); + } + return getProvidedChecksums( baseDir, checksumFilePaths, ArtifactIdUtils.toId( transfer.getArtifact() ) ); + } + + /** + * May return {@code null}. + */ + private Map getProvidedChecksums( Path baseDir, + List checksumFilePaths, + String subjectId ) + { + HashMap checksums = new HashMap<>(); + for ( ChecksumFilePath checksumFilePath : checksumFilePaths ) + { + Path checksumPath = baseDir.resolve( checksumFilePath.path ); + if ( Files.isReadable( checksumPath ) ) + { + try + { + String checksum = fileProcessor.readChecksum( checksumPath.toFile() ); + if ( checksum != null ) + { + LOGGER.debug( "Resolved provided checksum '{}:{}' for '{}'", + checksumFilePath.checksumAlgorithmFactory.getName(), checksum, subjectId ); + + checksums.put( checksumFilePath.checksumAlgorithmFactory.getName(), checksum ); + } + } + catch ( IOException e ) + { + LOGGER.warn( "Could not read provided checksum for '{}' at path '{}'", + subjectId, checksumPath, e ); + } + } + } + return checksums.isEmpty() ? null : checksums; + } + + /** + * Returns the base {@link URI} of directory where checksums are laid out, may return {@code null}. + */ + private Path getBaseDir( RepositorySystemSession session ) + { + final String baseDirPath = ConfigUtils.getString( session, null, CONFIG_PROP_BASE_DIR ); + final Path baseDir; + if ( baseDirPath != null ) + { + baseDir = Paths.get( baseDirPath ); + } + else + { + baseDir = session.getLocalRepository().getBasedir().toPath().resolve( LOCAL_REPO_PREFIX ); + } + if ( !Files.isDirectory( baseDir ) ) + { + return null; + } + return baseDir; + } + + private static final class ChecksumFilePath + { + private final String path; + + private final ChecksumAlgorithmFactory checksumAlgorithmFactory; + + private ChecksumFilePath( String path, ChecksumAlgorithmFactory checksumAlgorithmFactory ) + { + this.path = path; + this.checksumAlgorithmFactory = checksumAlgorithmFactory; + } + } +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java index 37151967f..d0b3de902 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java @@ -26,7 +26,6 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; -import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Named; @@ -148,9 +147,9 @@ private URI toUri( String path ) } @Override - public List getChecksumAlgorithmNames() + public List getChecksumAlgorithmFactories() { - return checksumAlgorithms.stream().map( ChecksumAlgorithmFactory::getName ).collect( Collectors.toList() ); + return checksumAlgorithms; } @Override diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/DefaultChecksumAlgorithmFactorySelector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/DefaultChecksumAlgorithmFactorySelector.java index 3e6d657d3..d463100b2 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/DefaultChecksumAlgorithmFactorySelector.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/DefaultChecksumAlgorithmFactorySelector.java @@ -23,15 +23,16 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.Set; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector; import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; /** * Default implementation. @@ -73,15 +74,19 @@ public ChecksumAlgorithmFactory select( String algorithmName ) { throw new IllegalArgumentException( String.format( "Unsupported checksum algorithm %s, supported ones are %s", - algorithmName, getChecksumAlgorithmNames() ) + algorithmName, + getChecksumAlgorithmFactories().stream() + .map( ChecksumAlgorithmFactory::getName ) + .collect( toList() ) + ) ); } return factory; } @Override - public Set getChecksumAlgorithmNames() + public List getChecksumAlgorithmFactories() { - return new HashSet<>( factories.keySet() ); + return new ArrayList<>( factories.values() ); } } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java index 5723e7f35..65ad48807 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultArtifactResolverTest.java @@ -38,8 +38,6 @@ import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.impl.UpdateCheckManager; import org.eclipse.aether.impl.VersionResolver; -import org.eclipse.aether.internal.impl.DefaultArtifactResolver; -import org.eclipse.aether.internal.impl.DefaultUpdateCheckManager; import org.eclipse.aether.internal.test.util.TestFileProcessor; import org.eclipse.aether.internal.test.util.TestFileUtils; import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager; diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FailChecksumPolicyTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FailChecksumPolicyTest.java index 9eceb4061..512aa183b 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FailChecksumPolicyTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FailChecksumPolicyTest.java @@ -21,7 +21,7 @@ import static org.junit.Assert.*; -import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy; +import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind; import org.eclipse.aether.transfer.ChecksumFailureException; import org.eclipse.aether.transfer.TransferResource; import org.junit.Before; @@ -50,8 +50,9 @@ public void testOnTransferChecksumFailure() @Test public void testOnChecksumMatch() { - assertTrue( policy.onChecksumMatch( "SHA-1", 0 ) ); - assertTrue( policy.onChecksumMatch( "SHA-1", ChecksumPolicy.KIND_UNOFFICIAL ) ); + assertTrue( policy.onChecksumMatch( "SHA-1", ChecksumKind.REMOTE_EXTERNAL ) ); + assertTrue( policy.onChecksumMatch( "SHA-1", ChecksumKind.REMOTE_INCLUDED ) ); + assertTrue( policy.onChecksumMatch( "SHA-1", ChecksumKind.PROVIDED ) ); } @Test @@ -60,21 +61,29 @@ public void testOnChecksumMismatch() { try { - policy.onChecksumMismatch( "SHA-1", 0, exception ); + policy.onChecksumMismatch( "SHA-1", ChecksumKind.REMOTE_EXTERNAL, exception ); fail( "No exception" ); } catch ( ChecksumFailureException e ) { assertSame( exception, e ); } - policy.onChecksumMismatch( "SHA-1", ChecksumPolicy.KIND_UNOFFICIAL, exception ); + policy.onChecksumMismatch( "SHA-1", ChecksumKind.REMOTE_INCLUDED, exception ); + try + { + policy.onChecksumMismatch("SHA-1", ChecksumKind.PROVIDED, exception); + } + catch ( ChecksumFailureException e) + { + assertSame( exception, e ); + } } @Test public void testOnChecksumError() throws Exception { - policy.onChecksumError( "SHA-1", 0, exception ); + policy.onChecksumError( "SHA-1", ChecksumKind.REMOTE_EXTERNAL, exception ); } @Test diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FileProvidedChecksumsSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FileProvidedChecksumsSourceTest.java new file mode 100644 index 000000000..e062a32b4 --- /dev/null +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FileProvidedChecksumsSourceTest.java @@ -0,0 +1,107 @@ +package org.eclipse.aether.internal.impl; + +/* + * 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 java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory; +import org.eclipse.aether.internal.test.util.TestFileProcessor; +import org.eclipse.aether.internal.test.util.TestUtils; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.repository.RepositoryPolicy; +import org.eclipse.aether.spi.connector.ArtifactDownload; +import org.eclipse.aether.spi.connector.layout.RepositoryLayout; +import org.eclipse.aether.transfer.NoRepositoryLayoutException; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class FileProvidedChecksumsSourceTest +{ + private DefaultRepositorySystemSession session; + + private RepositoryLayout repositoryLayout; + + private FileProvidedChecksumsSource subject; + + @Before + public void setup() throws NoRepositoryLayoutException, IOException + { + RemoteRepository repository = new RemoteRepository.Builder("test", "default", "https://irrelevant.com").build(); + session = TestUtils.newSession(); + repositoryLayout = new Maven2RepositoryLayoutFactory().newInstance(session, repository); + subject = new FileProvidedChecksumsSource(new TestFileProcessor() ); + + // populate local repository + Path baseDir = session.getLocalRepository().getBasedir().toPath().resolve( FileProvidedChecksumsSource.LOCAL_REPO_PREFIX); + + // artifact: test:test:2.0 => "foobar" + { + Path test = baseDir.resolve("test/test/2.0/test-2.0.jar.sha1"); + Files.createDirectories(test.getParent()); + Files.write(test, "foobar".getBytes(StandardCharsets.UTF_8)); + } + } + + @Test + public void noProvidedArtifactChecksum() + { + ArtifactDownload transfer = new ArtifactDownload( + new DefaultArtifact("test:test:1.0"), + "irrelevant", + new File("irrelevant"), + RepositoryPolicy.CHECKSUM_POLICY_FAIL + ); + Map providedChecksums = subject.getProvidedArtifactChecksums( + session, + transfer, + repositoryLayout.getChecksumAlgorithmFactories() + ); + assertNull(providedChecksums); + } + + @Test + public void haveProvidedArtifactChecksum() + { + ArtifactDownload transfer = new ArtifactDownload( + new DefaultArtifact("test:test:2.0"), + "irrelevant", + new File("irrelevant"), + RepositoryPolicy.CHECKSUM_POLICY_FAIL + ); + Map providedChecksums = subject.getProvidedArtifactChecksums( + session, + transfer, + repositoryLayout.getChecksumAlgorithmFactories() + ); + assertNotNull(providedChecksums); + assertEquals(providedChecksums.get(Sha1ChecksumAlgorithmFactory.NAME), "foobar"); + } +} diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java index 311607c19..108bf9085 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java @@ -24,6 +24,7 @@ import java.net.URI; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.artifact.DefaultArtifact; @@ -121,7 +122,11 @@ public void testBadLayout() @Test public void testChecksumAlgorithmNames() { - assertEquals( Arrays.asList( "SHA-1", "MD5" ), layout.getChecksumAlgorithmNames() ); + assertEquals( Arrays.asList( "SHA-1", "MD5" ), + layout.getChecksumAlgorithmFactories().stream() + .map( ChecksumAlgorithmFactory::getName ) + .collect( Collectors.toList() ) + ); } @Test diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/WarnChecksumPolicyTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/WarnChecksumPolicyTest.java index 0f7b522c2..dd376a1aa 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/WarnChecksumPolicyTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/WarnChecksumPolicyTest.java @@ -21,7 +21,7 @@ import static org.junit.Assert.*; -import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy; +import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind; import org.eclipse.aether.transfer.ChecksumFailureException; import org.eclipse.aether.transfer.TransferResource; import org.junit.Before; @@ -50,8 +50,8 @@ public void testOnTransferChecksumFailure() @Test public void testOnChecksumMatch() { - assertTrue( policy.onChecksumMatch( "SHA-1", 0 ) ); - assertTrue( policy.onChecksumMatch( "SHA-1", ChecksumPolicy.KIND_UNOFFICIAL ) ); + assertTrue( policy.onChecksumMatch( "SHA-1", ChecksumKind.REMOTE_EXTERNAL ) ); + assertTrue( policy.onChecksumMatch( "SHA-1", ChecksumKind.REMOTE_INCLUDED ) ); } @Test @@ -60,21 +60,21 @@ public void testOnChecksumMismatch() { try { - policy.onChecksumMismatch( "SHA-1", 0, exception ); + policy.onChecksumMismatch( "SHA-1", ChecksumKind.REMOTE_EXTERNAL, exception ); fail( "No exception" ); } catch ( ChecksumFailureException e ) { assertSame( exception, e ); } - policy.onChecksumMismatch( "SHA-1", ChecksumPolicy.KIND_UNOFFICIAL, exception ); + policy.onChecksumMismatch( "SHA-1", ChecksumKind.REMOTE_INCLUDED, exception ); } @Test public void testOnChecksumError() throws Exception { - policy.onChecksumError( "SHA-1", 0, exception ); + policy.onChecksumError( "SHA-1", ChecksumKind.REMOTE_EXTERNAL, exception ); } @Test diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java index eece32e81..5bc0aa257 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java @@ -19,7 +19,7 @@ * under the License. */ -import java.util.Set; +import java.util.Collection; /** * Component performing selection of {@link ChecksumAlgorithmFactory} based on known factory names. @@ -36,9 +36,10 @@ public interface ChecksumAlgorithmFactorySelector ChecksumAlgorithmFactory select( String algorithmName ); /** - * Returns a set of supported algorithm names. This set represents ALL the algorithms supported by Resolver, and is - * NOT in any relation to given repository layout used checksums, returned by method {@link - * org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmNames()} (is super set of it). + * Returns a collection of supported algorithm names. This set represents ALL the algorithms supported by Resolver, + * and is NOT in any relation to given repository layout used checksums, returned by method {@link + * org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmFactories()} (in fact, is super set + * of it). */ - Set getChecksumAlgorithmNames(); + Collection getChecksumAlgorithmFactories(); } diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumPolicy.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumPolicy.java index f7d7a3719..2b6e04afb 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumPolicy.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumPolicy.java @@ -64,23 +64,57 @@ */ public interface ChecksumPolicy { - /** - * Bit flag indicating a checksum which is not part of the official repository layout/structure. + * Enum denoting origin of checksum. + * + * @since 1.8.0 */ - int KIND_UNOFFICIAL = 0x01; + enum ChecksumKind + { + /** + * Remote external kind of checksum are retrieved from remote doing extra transport round-trip (usually by + * getting "file.jar.sha1" for corresponding "file.jar" file). This kind of checksum is part of layout, and + * was from beginning the "official" (and one and only) checksum used by resolver. + */ + REMOTE_EXTERNAL( false ), + + /** + * Included checksums may be received from remote repository during the retrieval of the main file, for example + * from response headers in case of HTTP transport. They may be set with + * {@link org.eclipse.aether.spi.connector.transport.GetTask#setChecksum(String, String)}. Included checksums + * on mismatch are ignored, so {@link #REMOTE_EXTERNAL} will be trialed on mismatch. + */ + REMOTE_INCLUDED( true ), + + /** + * Provided checksums may be provided by {@link ProvidedChecksumsSource} components, ahead of artifact + * retrieval. + */ + PROVIDED( false ); + + private final boolean ignoreOnMismatch; + + ChecksumKind( boolean ignoreOnMismatch ) + { + this.ignoreOnMismatch = ignoreOnMismatch; + } + + public boolean isIgnoreOnMismatch() + { + return ignoreOnMismatch; + } + } /** * Signals a match between the locally computed checksum value and the checksum value declared by the remote * repository. * * @param algorithm The name of the checksum algorithm being used, must not be {@code null}. - * @param kind A bit field providing further details about the checksum. See the {@code KIND_*} constants in - * this interface for possible bit flags. + * @param kind A field providing further details about the checksum. * @return {@code true} to accept the download as valid and stop further validation, {@code false} to continue * validation with the next checksum. */ - boolean onChecksumMatch( String algorithm, int kind ); + boolean onChecksumMatch( String algorithm, ChecksumKind kind ); /** * Signals a mismatch between the locally computed checksum value and the checksum value declared by the remote @@ -88,14 +122,12 @@ public interface ChecksumPolicy * their internal state and defer a conclusion until all available checksums have been processed. * * @param algorithm The name of the checksum algorithm being used, must not be {@code null}. - * @param kind A bit field providing further details about the checksum. See the {@code KIND_*} constants in - * this - * interface for possible bit flags. + * @param kind A field providing further details about the checksum. * @param exception The exception describing the checksum mismatch, must not be {@code null}. * @throws ChecksumFailureException If the checksum validation is to be failed. If the method returns normally, * validation continues with the next checksum. */ - void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception ) + void onChecksumMismatch( String algorithm, ChecksumKind kind, ChecksumFailureException exception ) throws ChecksumFailureException; /** @@ -103,14 +135,12 @@ void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException ex * repository. * * @param algorithm The name of the checksum algorithm being used, must not be {@code null}. - * @param kind A bit field providing further details about the checksum. See the {@code KIND_*} constants in - * this - * interface for possible bit flags. + * @param kind A field providing further details about the checksum. * @param exception The exception describing the checksum error, must not be {@code null}. * @throws ChecksumFailureException If the checksum validation is to be failed. If the method returns normally, * validation continues with the next checksum. */ - void onChecksumError( String algorithm, int kind, ChecksumFailureException exception ) + void onChecksumError( String algorithm, ChecksumKind kind, ChecksumFailureException exception ) throws ChecksumFailureException; /** @@ -134,8 +164,8 @@ void onNoMoreChecksums() * issue or insist on rejecting the downloaded file as unusable. * * @param exception The exception that was thrown from a prior call to - * {@link #onChecksumMismatch(String, int, ChecksumFailureException)}, - * {@link #onChecksumError(String, int, ChecksumFailureException)} or {@link + * {@link #onChecksumMismatch(String, ChecksumKind, ChecksumFailureException)}, + * {@link #onChecksumError(String, ChecksumKind, ChecksumFailureException)} or {@link * #onNoMoreChecksums()}. * @return {@code true} to accept the download nevertheless and let artifact resolution succeed, {@code false} to * reject the transferred file as unusable. diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ProvidedChecksumsSource.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ProvidedChecksumsSource.java new file mode 100644 index 000000000..b1d70b014 --- /dev/null +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ProvidedChecksumsSource.java @@ -0,0 +1,47 @@ +package org.eclipse.aether.spi.connector.checksum; + +/* + * 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 java.util.List; +import java.util.Map; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.spi.connector.ArtifactDownload; + +/** + * Component able to provide (expected) checksums beforehand the download happens. Checksum provided by this component + * are of kind {@link org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind#PROVIDED}. + * + * @since 1.8.0 + */ +public interface ProvidedChecksumsSource +{ + /** + * May return the provided checksums (for given artifact transfer) from trusted source other than remote + * repository, or {@code null}. + * + * @param transfer The transfer that is about to be executed. + * @param checksumAlgorithmFactories The checksum algorithms that are expected. + * @return Map of expected checksums, or {@code null}. + */ + Map getProvidedArtifactChecksums( RepositorySystemSession session, + ArtifactDownload transfer, + List checksumAlgorithmFactories ); +} diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java index 8bc88a73d..4a02b0680 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java @@ -126,12 +126,12 @@ public String toString() } /** - * Returns the list of checksum names this instance of layout uses, never {@code null}. The checksum order - * represents the order how checksums are validated (hence retrieved). + * Returns immutable list of {@link ChecksumAlgorithmFactory} this instance of layout uses, never {@code null}. + * The order represents the order how checksums are validated (hence retrieved). * * @since 1.8.0 */ - List getChecksumAlgorithmNames(); + List getChecksumAlgorithmFactories(); /** * Gets the location within a remote repository where the specified artifact resides. The URI is relative to the diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/GetTask.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/GetTask.java index 3d30694d2..e6eb9a412 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/GetTask.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/GetTask.java @@ -209,9 +209,10 @@ public GetTask setListener( TransportListener listener ) /** * Gets the checksums which the remote repository advertises for the resource. The map is keyed by algorithm name - * (cf. {@link java.security.MessageDigest#getInstance(String)}) and the values are hexadecimal representations of - * the corresponding value. Note: This is optional data that a transporter may return if the underlying - * transport protocol provides metadata (e.g. HTTP headers) along with the actual resource data. + * and the values are hexadecimal representations of the corresponding value. Note: This is optional + * data that a transporter may return if the underlying transport protocol provides metadata (e.g. HTTP headers) + * along with the actual resource data. Checksums returned by this method have kind of + * {@link org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind#REMOTE_INCLUDED}. * * @return The (read-only) checksums advertised for the downloaded resource, possibly empty but never {@code null}. */ @@ -225,8 +226,7 @@ public Map getChecksums() * use this method to record checksum information which is readily available while performing the actual download, * they should not perform additional transfers to gather this data. * - * @param algorithm The name of the checksum algorithm (e.g. {@code "SHA-1"}, cf. - * {@link java.security.MessageDigest#getInstance(String)} ), may be {@code null}. + * @param algorithm The name of the checksum algorithm (e.g. {@code "SHA-1"}, may be {@code null}. * @param value The hexadecimal representation of the checksum, may be {@code null}. * @return This task for chaining, never {@code null}. */