This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/master by this push:
     new 70696c0b5 Better checksum control (#1784)
70696c0b5 is described below

commit 70696c0b5f2b4e74a1a910459dd94e1a6715303b
Author: Tamas Cservenak <[email protected]>
AuthorDate: Wed Feb 4 13:03:44 2026 +0100

    Better checksum control (#1784)
    
    Ability to explicitly configure checksums for download (used to validate 
during donwloads) and for uploads (generated during uploads).
    
    Fixes #1782
---
 .../connector/basic/BasicRepositoryConnector.java  |   2 +-
 .../impl/Maven2RepositoryLayoutFactory.java        | 113 +++++++++++++++++----
 .../impl/Maven2RepositoryLayoutFactoryTest.java    |  48 ++++++++-
 .../checksum/ChecksumAlgorithmFactorySelector.java |   2 +-
 .../spi/connector/layout/RepositoryLayout.java     |  22 +++-
 5 files changed, 161 insertions(+), 26 deletions(-)

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 fd2d51e93..3d4d748fa 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
@@ -215,7 +215,7 @@ final class BasicRepositoryConnector implements 
RepositoryConnector {
 
         SmartExecutor executor = getExecutor(true, 
safeArtifactDownloads.size() + safeMetadataDownloads.size());
         RunnableErrorForwarder errorForwarder = new RunnableErrorForwarder();
-        List<ChecksumAlgorithmFactory> checksumAlgorithmFactories = 
layout.getChecksumAlgorithmFactories();
+        List<ChecksumAlgorithmFactory> checksumAlgorithmFactories = 
layout.getChecksumAlgorithmFactories(false);
 
         boolean first = true;
 
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 c89ca5ffe..fdfc0c5ba 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,6 +26,7 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
 
 import org.eclipse.aether.ConfigurationProperties;
@@ -57,7 +58,9 @@ public final class Maven2RepositoryLayoutFactory implements 
RepositoryLayoutFact
     /**
      * Comma-separated list of checksum algorithms with which checksums are 
validated (downloaded) and generated
      * (uploaded) with this layout. Resolver by default supports following 
algorithms: MD5, SHA-1, SHA-256 and
-     * SHA-512. New algorithms can be added by implementing 
ChecksumAlgorithmFactory component.
+     * SHA-512. New algorithms can be added by implementing 
ChecksumAlgorithmFactory component. To configure separately
+     * checksums for download or upload, use {@link 
#CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS} and
+     * {@link #CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS} respectively.
      *
      * @since 1.8.0
      * @configurationSource {@link 
RepositorySystemSession#getConfigProperties()}
@@ -69,6 +72,38 @@ public final class Maven2RepositoryLayoutFactory implements 
RepositoryLayoutFact
 
     public static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
 
+    /**
+     * Comma-separated list of checksum algorithms with which checksums are 
generated and uploaded
+     * with this layout. Resolver by default supports following algorithms: 
MD5, SHA-1, SHA-256 and
+     * SHA-512. New algorithms can be added by implementing 
ChecksumAlgorithmFactory component.
+     * If this property is set, it <em>overrides</em> the value set in {@link 
#CONFIG_PROP_CHECKSUMS_ALGORITHMS} for
+     * uploads.
+     *
+     * @since 2.0.15
+     * @configurationSource {@link 
RepositorySystemSession#getConfigProperties()}
+     * @configurationType {@link java.lang.String}
+     * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS}
+     * @configurationRepoIdSuffix Yes
+     */
+    public static final String CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS =
+            CONFIG_PROPS_PREFIX + "uploadChecksumAlgorithms";
+
+    /**
+     * Comma-separated list of checksum algorithms with which checksums are 
validated (downloaded) with this layout.
+     * Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 
and SHA-512.
+     * New algorithms can be added by implementing ChecksumAlgorithmFactory 
component.
+     * If this property is set, it <em>overrides</em> the value set in {@link 
#CONFIG_PROP_CHECKSUMS_ALGORITHMS} for
+     * downloads.
+     *
+     * @since 2.0.15
+     * @configurationSource {@link 
RepositorySystemSession#getConfigProperties()}
+     * @configurationType {@link java.lang.String}
+     * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS}
+     * @configurationRepoIdSuffix Yes
+     */
+    public static final String CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS =
+            CONFIG_PROPS_PREFIX + "downloadChecksumAlgorithms";
+
     private float priority;
 
     private final ChecksumAlgorithmFactorySelector 
checksumAlgorithmFactorySelector;
@@ -106,26 +141,48 @@ public final class Maven2RepositoryLayoutFactory 
implements RepositoryLayoutFact
             throw new NoRepositoryLayoutException(repository);
         }
 
-        List<ChecksumAlgorithmFactory> checksumsAlgorithms = 
checksumAlgorithmFactorySelector.selectList(
-                
ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString(
-                        session,
-                        DEFAULT_CHECKSUMS_ALGORITHMS,
-                        CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + 
repository.getId(),
-                        CONFIG_PROP_CHECKSUMS_ALGORITHMS,
-                        // MRESOLVER-701: support legacy properties for 
simpler transitioning
-                        "aether.checksums.algorithms",
-                        "aether.checksums.algorithms." + repository.getId())));
-
-        return new Maven2RepositoryLayout(checksumsAlgorithms, 
artifactPredicateFactory.newInstance(session));
+        // explicit property for download (will be empty if not configured)
+        List<String> downloadChecksumsAlgorithmNames = 
ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString(
+                session,
+                DEFAULT_CHECKSUMS_ALGORITHMS,
+                CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS + "." + 
repository.getId(),
+                CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS,
+                CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
+                CONFIG_PROP_CHECKSUMS_ALGORITHMS,
+                // MRESOLVER-701: support legacy properties for simpler 
transitioning
+                "aether.checksums.algorithms." + repository.getId(),
+                "aether.checksums.algorithms"));
+
+        // explicit property for upload (will be empty if not configured)
+        List<String> uploadChecksumsAlgorithmNames = 
ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString(
+                session,
+                DEFAULT_CHECKSUMS_ALGORITHMS,
+                CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS + "." + 
repository.getId(),
+                CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS,
+                CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
+                CONFIG_PROP_CHECKSUMS_ALGORITHMS,
+                // MRESOLVER-701: support legacy properties for simpler 
transitioning
+                "aether.checksums.algorithms." + repository.getId(),
+                "aether.checksums.algorithms"));
+
+        return new Maven2RepositoryLayout(
+                
checksumAlgorithmFactorySelector.selectList(downloadChecksumsAlgorithmNames),
+                
checksumAlgorithmFactorySelector.selectList(uploadChecksumsAlgorithmNames),
+                artifactPredicateFactory.newInstance(session));
     }
 
     private static class Maven2RepositoryLayout implements RepositoryLayout {
-        private final List<ChecksumAlgorithmFactory> 
configuredChecksumAlgorithms;
+        private final List<ChecksumAlgorithmFactory> 
configuredDownloadChecksumAlgorithms;
+        private final List<ChecksumAlgorithmFactory> 
configuredUploadChecksumAlgorithms;
         private final ArtifactPredicate artifactPredicate;
 
         private Maven2RepositoryLayout(
-                List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms, 
ArtifactPredicate artifactPredicate) {
-            this.configuredChecksumAlgorithms = 
Collections.unmodifiableList(configuredChecksumAlgorithms);
+                List<ChecksumAlgorithmFactory> 
configuredDownloadChecksumAlgorithms,
+                List<ChecksumAlgorithmFactory> 
configuredUploadChecksumAlgorithms,
+                ArtifactPredicate artifactPredicate) {
+            this.configuredDownloadChecksumAlgorithms =
+                    
Collections.unmodifiableList(configuredDownloadChecksumAlgorithms);
+            this.configuredUploadChecksumAlgorithms = 
Collections.unmodifiableList(configuredUploadChecksumAlgorithms);
             this.artifactPredicate = requireNonNull(artifactPredicate);
         }
 
@@ -139,7 +196,19 @@ public final class Maven2RepositoryLayoutFactory 
implements RepositoryLayoutFact
 
         @Override
         public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() {
-            return configuredChecksumAlgorithms;
+            LinkedHashMap<String, ChecksumAlgorithmFactory> factories = new 
LinkedHashMap<>();
+            configuredDownloadChecksumAlgorithms.forEach(f -> 
factories.putIfAbsent(f.getName(), f));
+            configuredUploadChecksumAlgorithms.forEach(f -> 
factories.putIfAbsent(f.getName(), f));
+            return Collections.unmodifiableList(new 
ArrayList<>(factories.values()));
+        }
+
+        @Override
+        public List<ChecksumAlgorithmFactory> 
getChecksumAlgorithmFactories(boolean upload) {
+            if (upload) {
+                return configuredUploadChecksumAlgorithms;
+            } else {
+                return configuredDownloadChecksumAlgorithms;
+            }
         }
 
         @Override
@@ -196,17 +265,19 @@ public final class Maven2RepositoryLayoutFactory 
implements RepositoryLayoutFact
             if (artifactPredicate.isWithoutChecksum(artifact) || 
artifactPredicate.isChecksum(artifact)) {
                 return Collections.emptyList();
             }
-            return getChecksumLocations(location);
+            return getChecksumLocations(location, upload);
         }
 
         @Override
         public List<ChecksumLocation> getChecksumLocations(Metadata metadata, 
boolean upload, URI location) {
-            return getChecksumLocations(location);
+            return getChecksumLocations(location, upload);
         }
 
-        private List<ChecksumLocation> getChecksumLocations(URI location) {
-            List<ChecksumLocation> checksumLocations = new 
ArrayList<>(configuredChecksumAlgorithms.size());
-            for (ChecksumAlgorithmFactory checksumAlgorithmFactory : 
configuredChecksumAlgorithms) {
+        private List<ChecksumLocation> getChecksumLocations(URI location, 
boolean upload) {
+            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories =
+                    upload ? configuredUploadChecksumAlgorithms : 
configuredDownloadChecksumAlgorithms;
+            List<ChecksumLocation> checksumLocations = new 
ArrayList<>(checksumAlgorithmFactories.size());
+            for (ChecksumAlgorithmFactory checksumAlgorithmFactory : 
checksumAlgorithmFactories) {
                 checksumLocations.add(ChecksumLocation.forLocation(location, 
checksumAlgorithmFactory));
             }
             return checksumLocations;
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 d6bfed7e8..74f20426c 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
@@ -319,7 +319,7 @@ public class Maven2RepositoryLayoutFactoryTest {
     }
 
     @Test
-    void testCustomChecksumsIgnored_IllegalInout() throws Exception {
+    void testCustomChecksumsIgnored_IllegalInput() throws Exception {
         session.setConfigProperty(
                 
DefaultArtifactPredicateFactory.CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS, 
".asc,foo");
         try {
@@ -332,4 +332,50 @@ public class Maven2RepositoryLayoutFactoryTest {
                     message);
         }
     }
+
+    @Test
+    void testUploadDownloadChecksumsConfigured() throws Exception {
+        session.setConfigProperty(
+                
Maven2RepositoryLayoutFactory.CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS, 
"SHA-256,SHA-1");
+        
session.setConfigProperty(Maven2RepositoryLayoutFactory.CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS,
 "SHA-512,MD5");
+        layout = factory.newInstance(session, newRepo("default"));
+        // as configured
+        assertEquals(Arrays.asList("SHA-256", "SHA-1"), 
toNames(layout.getChecksumAlgorithmFactories(false)));
+        // as configured
+        assertEquals(Arrays.asList("SHA-512", "MD5"), 
toNames(layout.getChecksumAlgorithmFactories(true)));
+        // in order with download ones added first
+        assertEquals(
+                Arrays.asList("SHA-256", "SHA-1", "SHA-512", "MD5"), 
toNames(layout.getChecksumAlgorithmFactories()));
+    }
+
+    @Test
+    void testDownloadChecksumsConfigured() throws Exception {
+        session.setConfigProperty(
+                
Maven2RepositoryLayoutFactory.CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS, 
"SHA-256,SHA-1");
+        layout = factory.newInstance(session, newRepo("default"));
+        // as configured
+        assertEquals(Arrays.asList("SHA-256", "SHA-1"), 
toNames(layout.getChecksumAlgorithmFactories(false)));
+        // defaults
+        assertEquals(Arrays.asList("SHA-1", "MD5"), 
toNames(layout.getChecksumAlgorithmFactories(true)));
+        // in order with download ones added first
+        assertEquals(Arrays.asList("SHA-256", "SHA-1", "MD5"), 
toNames(layout.getChecksumAlgorithmFactories()));
+    }
+
+    @Test
+    void testUploadChecksumsConfigured() throws Exception {
+        session.setConfigProperty(
+                
Maven2RepositoryLayoutFactory.CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS, 
"SHA-512,SHA-256,SHA-1");
+        layout = factory.newInstance(session, newRepo("default"));
+        // defaults
+        assertEquals(Arrays.asList("SHA-1", "MD5"), 
toNames(layout.getChecksumAlgorithmFactories(false)));
+        // as configured
+        assertEquals(Arrays.asList("SHA-512", "SHA-256", "SHA-1"), 
toNames(layout.getChecksumAlgorithmFactories(true)));
+        // in order with download ones added first
+        assertEquals(
+                Arrays.asList("SHA-1", "MD5", "SHA-512", "SHA-256"), 
toNames(layout.getChecksumAlgorithmFactories()));
+    }
+
+    private static List<String> toNames(List<ChecksumAlgorithmFactory> 
factories) {
+        return 
factories.stream().map(ChecksumAlgorithmFactory::getName).collect(Collectors.toList());
+    }
 }
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 cc96ddabb..88f4b922a 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
@@ -52,7 +52,7 @@ public interface ChecksumAlgorithmFactorySelector {
     /**
      * Returns immutable collection of all supported algorithms. 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
+     * 
org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmFactories(boolean)}
 (in fact, is super set
      * of it).
      */
     Collection<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories();
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 d0110e679..c503e832f 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
@@ -115,13 +115,31 @@ public interface RepositoryLayout {
 
     /**
      * Returns immutable list of {@link ChecksumAlgorithmFactory} this 
instance of layout uses, never {@code null}.
-     * The order also represents the order how remote external checksums are 
retrieved and validated.
+     * This (legacy, but not deprecated) method will return <em>all checksums 
this layout uses</em>, but
+     * these may be different in case upload or download checksums are 
explicitly configured. This method will
+     * reflect the checksum order used for download, but may have more 
elements than actually used in download
+     * validation, if the generated checksums for upload has extra elements.
      *
      * @see 
org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind
+     * @see #getChecksumAlgorithmFactories(boolean)
      * @since 1.8.0
      */
     List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories();
 
+    /**
+     * Returns immutable list of {@link ChecksumAlgorithmFactory} this 
instance of layout uses for download or upload,
+     * never {@code null}. The order also represents the order how remote 
external checksums are retrieved and
+     * validated (if for download).
+     *
+     * @param upload {@code false} if the caller needs checksums used for 
download validation, or {@code true} if the
+     *                            caller needs checksums generated for upload.
+     * @see 
org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind
+     * @since 2.0.15
+     */
+    default List<ChecksumAlgorithmFactory> 
getChecksumAlgorithmFactories(boolean upload) {
+        return getChecksumAlgorithmFactories();
+    }
+
     /**
      * Tells whether given artifact have remote external checksums according 
to current layout or not. If it returns
      * {@code true}, then layout configured checksums will be expected: on 
upload they will be calculated and deployed
@@ -132,7 +150,7 @@ public interface RepositoryLayout {
      *
      * The result affects only layout provided checksums. See
      * {@link 
org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind#REMOTE_EXTERNAL}.
-     * On download, the {@link 
org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmFactories()}
+     * On download, the {@link 
org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmFactories(boolean)}
      * layout required checksums are calculated, and non layout-provided 
checksums are still utilized.
      *
      * Typical case to return {@code false} (to omit checksums) is for 
artifact signatures, that are already a

Reply via email to