From 3b55ec0c9b9cbf2a7a015334e3362710310d4690 Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Fri, 28 Jan 2022 14:19:25 +0100 Subject: [PATCH] [MRESOLVER-234] Introduce "provided" checksums feature This feature allows Resolver to get "provided" checksums ahead of remote transport (resolution), hence, it may operate with "good known" checksums for example (or use any other source). --- .../basic/BasicRepositoryConnector.java | 28 ++- .../BasicRepositoryConnectorFactory.java | 33 +++- .../connector/basic/ChecksumValidator.java | 49 +++-- .../basic/ChecksumValidatorTest.java | 45 +++-- .../basic/TestChecksumAlgorithmSelector.java | 2 +- .../aether/impl/guice/AetherModule.java | 16 ++ .../internal/impl/AbstractChecksumPolicy.java | 8 +- .../impl/FileProvidedChecksumsSource.java | 169 ++++++++++++++++++ .../impl/Maven2RepositoryLayoutFactory.java | 5 +- ...faultChecksumAlgorithmFactorySelector.java | 15 +- .../impl/DefaultArtifactResolverTest.java | 2 - .../internal/impl/FailChecksumPolicyTest.java | 21 ++- .../impl/FileProvidedChecksumsSourceTest.java | 107 +++++++++++ .../Maven2RepositoryLayoutFactoryTest.java | 7 +- .../internal/impl/WarnChecksumPolicyTest.java | 12 +- .../ChecksumAlgorithmFactorySelector.java | 11 +- .../connector/checksum/ChecksumPolicy.java | 62 +++++-- .../checksum/ProvidedChecksumsSource.java | 47 +++++ .../connector/layout/RepositoryLayout.java | 6 +- .../spi/connector/transport/GetTask.java | 10 +- 20 files changed, 560 insertions(+), 95 deletions(-) create mode 100644 maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FileProvidedChecksumsSource.java create mode 100644 maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/FileProvidedChecksumsSourceTest.java create mode 100644 maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ProvidedChecksumsSource.java 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}. */