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.git


The following commit(s) were added to refs/heads/master by this push:
     new 437f34f8dc [MNG-7957] Checksum API (backed by Resolver) (#1359)
437f34f8dc is described below

commit 437f34f8dcc2cd46e64076d4c1c03f5339d2a331
Author: Tamas Cservenak <[email protected]>
AuthorDate: Thu Dec 21 09:05:10 2023 +0100

    [MNG-7957] Checksum API (backed by Resolver) (#1359)
    
    Expose checksumming API (backed by Resolver).
    
    ---
    
    https://issues.apache.org/jira/browse/MNG-7957
---
 .../api/services/ChecksumAlgorithmService.java     | 161 +++++++++++++++++
 .../ChecksumAlgorithmServiceException.java         |  31 ++++
 .../impl/DefaultChecksumAlgorithmService.java      | 194 +++++++++++++++++++++
 .../impl/DefaultChecksumAlgorithmServiceTest.java  |  95 ++++++++++
 4 files changed, 481 insertions(+)

diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmService.java
 
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmService.java
new file mode 100644
index 0000000000..51f72d591e
--- /dev/null
+++ 
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmService.java
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+package org.apache.maven.api.services;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.maven.api.Service;
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Checksum algorithms service.
+ *
+ * @since 4.0.0
+ */
+@Experimental
+public interface ChecksumAlgorithmService extends Service {
+
+    /**
+     * Returns immutable collection of all supported algorithm names.
+     */
+    @Nonnull
+    Collection<String> getChecksumAlgorithmNames();
+
+    /**
+     * Returns {@link ChecksumAlgorithm} for given algorithm name, or throws 
if algorithm not supported.
+     *
+     * @throws ChecksumAlgorithmServiceException if asked algorithm name is 
not supported.
+     * @throws NullPointerException if passed in name is {@code null}.
+     */
+    @Nonnull
+    ChecksumAlgorithm select(@Nonnull String algorithmName);
+
+    /**
+     * Returns a collection of {@link ChecksumAlgorithm} in same order as 
algorithm names are ordered, or throws if
+     * any of the algorithm name is not supported. The returned collection has 
equal count of elements as passed in
+     * collection of names, and if names contains duplicated elements, the 
returned list of algorithms will have
+     * duplicates as well.
+     *
+     * @throws ChecksumAlgorithmServiceException if any asked algorithm name 
is not supported.
+     * @throws NullPointerException if passed in list of names is {@code null}.
+     */
+    @Nonnull
+    Collection<ChecksumAlgorithm> select(@Nonnull Collection<String> 
algorithmNames);
+
+    /**
+     * Calculates checksums for specified data.
+     *
+     * @param data        The content for which to calculate checksums, must 
not be {@code null}.
+     * @param algorithms  The checksum algorithms to use, must not be {@code 
null}.
+     * @return The calculated checksums, indexed by algorithms, never {@code 
null}.
+     * @throws NullPointerException if passed in any parameter is {@code null}.
+     */
+    @Nonnull
+    Map<ChecksumAlgorithm, String> calculate(@Nonnull byte[] data, @Nonnull 
Collection<ChecksumAlgorithm> algorithms);
+
+    /**
+     * Calculates checksums for specified data.
+     *
+     * @param data        The content for which to calculate checksums, must 
not be {@code null}.
+     * @param algorithms  The checksum algorithms to use, must not be {@code 
null}.
+     * @return The calculated checksums, indexed by algorithms, never {@code 
null}.
+     * @throws NullPointerException if passed in any parameter is {@code null}.
+     */
+    @Nonnull
+    Map<ChecksumAlgorithm, String> calculate(
+            @Nonnull ByteBuffer data, @Nonnull Collection<ChecksumAlgorithm> 
algorithms);
+
+    /**
+     * Calculates checksums for specified file.
+     *
+     * @param file        The file for which to calculate checksums, must not 
be {@code null}.
+     * @param algorithms  The checksum algorithms to use, must not be {@code 
null}.
+     * @return The calculated checksums, indexed by algorithms, never {@code 
null}.
+     * @throws NullPointerException if passed in any parameter is {@code null}.
+     * @throws IOException In case of any IO problem.
+     */
+    @Nonnull
+    Map<ChecksumAlgorithm, String> calculate(@Nonnull Path file, @Nonnull 
Collection<ChecksumAlgorithm> algorithms)
+            throws IOException;
+
+    /**
+     * Calculates checksums for specified stream. Upon this method returns, 
the stream will be depleted (fully read)
+     * but not closed.
+     *
+     * @param stream      The stream for which to calculate checksums, must 
not be {@code null}.
+     * @param algorithms  The checksum algorithms to use, must not be {@code 
null}.
+     * @return The calculated checksums, indexed by algorithms, never {@code 
null}.
+     * @throws NullPointerException if passed in any parameter is {@code null}.
+     * @throws IOException In case of any IO problem.
+     */
+    @Nonnull
+    Map<ChecksumAlgorithm, String> calculate(
+            @Nonnull InputStream stream, @Nonnull 
Collection<ChecksumAlgorithm> algorithms) throws IOException;
+
+    /**
+     * The checksum algorithm.
+     */
+    interface ChecksumAlgorithm {
+        /**
+         * Returns the algorithm name, usually used as key, never {@code null} 
value. The name is a standard name of
+         * algorithm (if applicable) or any other designator that is algorithm 
commonly referred with. Example: "SHA-1".
+         */
+        @Nonnull
+        String getName();
+
+        /**
+         * Returns the file extension to be used for given checksum file 
(without leading dot), never {@code null}. The
+         * extension should be file and URL path friendly, and may differ from 
algorithm name.
+         * The checksum extension SHOULD NOT contain dot (".") character.
+         * Example: "sha1".
+         */
+        @Nonnull
+        String getFileExtension();
+
+        /**
+         * Each invocation of this method returns a new instance of 
calculator, never {@code null} value.
+         */
+        @Nonnull
+        ChecksumCalculator getCalculator();
+    }
+
+    /**
+     * The checksum calculator.
+     */
+    interface ChecksumCalculator {
+        /**
+         * Updates the checksum algorithm inner state with input.
+         *
+         * @throws NullPointerException if passed in buffer is {@code null}.
+         */
+        void update(@Nonnull ByteBuffer input);
+
+        /**
+         * Returns the algorithm end result as string, never {@code null}. 
After invoking this method, this instance should
+         * be discarded and not reused. For new checksum calculation you have 
to get new instance.
+         */
+        @Nonnull
+        String checksum();
+    }
+}
diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java
 
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java
new file mode 100644
index 0000000000..a22c1ee159
--- /dev/null
+++ 
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ChecksumAlgorithmServiceException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+package org.apache.maven.api.services;
+
+import org.apache.maven.api.annotations.Experimental;
+
+@Experimental
+public class ChecksumAlgorithmServiceException extends MavenException {
+
+    private static final long serialVersionUID = 1201171469179367694L;
+
+    public ChecksumAlgorithmServiceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultChecksumAlgorithmService.java
 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultChecksumAlgorithmService.java
new file mode 100644
index 0000000000..10baef3ccc
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultChecksumAlgorithmService.java
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+package org.apache.maven.internal.impl;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import org.apache.maven.api.services.*;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import 
org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
+
+import static org.apache.maven.internal.impl.Utils.nonNull;
+
+@Named
+@Singleton
+public class DefaultChecksumAlgorithmService implements 
ChecksumAlgorithmService {
+    private final ChecksumAlgorithmFactorySelector 
checksumAlgorithmFactorySelector;
+
+    @Inject
+    public DefaultChecksumAlgorithmService(ChecksumAlgorithmFactorySelector 
checksumAlgorithmFactorySelector) {
+        this.checksumAlgorithmFactorySelector =
+                nonNull(checksumAlgorithmFactorySelector, 
"checksumAlgorithmFactorySelector");
+    }
+
+    @Override
+    public Collection<String> getChecksumAlgorithmNames() {
+        return 
checksumAlgorithmFactorySelector.getChecksumAlgorithmFactories().stream()
+                .map(ChecksumAlgorithmFactory::getName)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public ChecksumAlgorithm select(String algorithmName) {
+        nonNull(algorithmName, "algorithmName");
+        try {
+            return new 
DefaultChecksumAlgorithm(checksumAlgorithmFactorySelector.select(algorithmName));
+        } catch (IllegalArgumentException e) {
+            throw new ChecksumAlgorithmServiceException("unsupported 
algorithm", e);
+        }
+    }
+
+    @Override
+    public Collection<ChecksumAlgorithm> select(Collection<String> 
algorithmNames) {
+        nonNull(algorithmNames, "algorithmNames");
+        try {
+            return checksumAlgorithmFactorySelector.selectList(new 
ArrayList<>(algorithmNames)).stream()
+                    .map(DefaultChecksumAlgorithm::new)
+                    .collect(Collectors.toList());
+        } catch (IllegalArgumentException e) {
+            throw new ChecksumAlgorithmServiceException("unsupported 
algorithm", e);
+        }
+    }
+
+    @Override
+    public Map<ChecksumAlgorithm, String> calculate(byte[] data, 
Collection<ChecksumAlgorithm> algorithms) {
+        nonNull(data, "data");
+        nonNull(algorithms, "algorithms");
+        try {
+            return calculate(new ByteArrayInputStream(data), algorithms);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e); // really unexpected
+        }
+    }
+
+    @Override
+    public Map<ChecksumAlgorithm, String> calculate(ByteBuffer data, 
Collection<ChecksumAlgorithm> algorithms) {
+        nonNull(data, "data");
+        nonNull(algorithms, "algorithms");
+        LinkedHashMap<ChecksumAlgorithm, ChecksumCalculator> algMap = new 
LinkedHashMap<>();
+        algorithms.forEach(f -> algMap.put(f, f.getCalculator()));
+        data.mark();
+        for (ChecksumCalculator checksumCalculator : algMap.values()) {
+            checksumCalculator.update(data);
+            data.reset();
+        }
+        LinkedHashMap<ChecksumAlgorithm, String> result = new 
LinkedHashMap<>();
+        algMap.forEach((k, v) -> result.put(k, v.checksum()));
+        return result;
+    }
+
+    @Override
+    public Map<ChecksumAlgorithm, String> calculate(Path file, 
Collection<ChecksumAlgorithm> algorithms)
+            throws IOException {
+        nonNull(file, "file");
+        nonNull(algorithms, "algorithms");
+        try (InputStream inputStream = new 
BufferedInputStream(Files.newInputStream(file))) {
+            return calculate(inputStream, algorithms);
+        }
+    }
+
+    @Override
+    public Map<ChecksumAlgorithm, String> calculate(InputStream stream, 
Collection<ChecksumAlgorithm> algorithms)
+            throws IOException {
+        nonNull(stream, "stream");
+        nonNull(algorithms, "algorithms");
+        LinkedHashMap<ChecksumAlgorithm, ChecksumCalculator> algMap = new 
LinkedHashMap<>();
+        algorithms.forEach(f -> algMap.put(f, f.getCalculator()));
+        final byte[] buffer = new byte[1024 * 32];
+        for (; ; ) {
+            int read = stream.read(buffer);
+            if (read < 0) {
+                break;
+            }
+            for (ChecksumCalculator checksumCalculator : algMap.values()) {
+                checksumCalculator.update(ByteBuffer.wrap(buffer, 0, read));
+            }
+        }
+        LinkedHashMap<ChecksumAlgorithm, String> result = new 
LinkedHashMap<>();
+        algMap.forEach((k, v) -> result.put(k, v.checksum()));
+        return result;
+    }
+
+    private static class DefaultChecksumAlgorithm implements ChecksumAlgorithm 
{
+        private final ChecksumAlgorithmFactory factory;
+
+        DefaultChecksumAlgorithm(ChecksumAlgorithmFactory factory) {
+            this.factory = factory;
+        }
+
+        @Override
+        public String getName() {
+            return factory.getName();
+        }
+
+        @Override
+        public String getFileExtension() {
+            return factory.getFileExtension();
+        }
+
+        @Override
+        public ChecksumCalculator getCalculator() {
+            return new DefaultChecksumCalculator(factory.getAlgorithm());
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            DefaultChecksumAlgorithm that = (DefaultChecksumAlgorithm) o;
+            return Objects.equals(factory.getName(), that.factory.getName());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(factory.getName());
+        }
+    }
+
+    private static class DefaultChecksumCalculator implements 
ChecksumCalculator {
+        private final 
org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm algorithm;
+
+        
DefaultChecksumCalculator(org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm
 algorithm) {
+            this.algorithm = algorithm;
+        }
+
+        @Override
+        public void update(ByteBuffer input) {
+            algorithm.update(input);
+        }
+
+        @Override
+        public String checksum() {
+            return algorithm.checksum();
+        }
+    }
+}
diff --git 
a/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultChecksumAlgorithmServiceTest.java
 
b/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultChecksumAlgorithmServiceTest.java
new file mode 100644
index 0000000000..8c4f4c2315
--- /dev/null
+++ 
b/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultChecksumAlgorithmServiceTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+package org.apache.maven.internal.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.maven.api.services.ChecksumAlgorithmService;
+import org.eclipse.aether.internal.impl.checksum.*;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class DefaultChecksumAlgorithmServiceTest {
+    private static Map<String, ChecksumAlgorithmFactory> 
getChecksumAlgorithmFactories() {
+        HashMap<String, ChecksumAlgorithmFactory> result = new HashMap<>();
+        result.put(Sha512ChecksumAlgorithmFactory.NAME, new 
Sha512ChecksumAlgorithmFactory());
+        result.put(Sha256ChecksumAlgorithmFactory.NAME, new 
Sha256ChecksumAlgorithmFactory());
+        result.put(Sha1ChecksumAlgorithmFactory.NAME, new 
Sha1ChecksumAlgorithmFactory());
+        result.put(Md5ChecksumAlgorithmFactory.NAME, new 
Md5ChecksumAlgorithmFactory());
+        return result;
+    }
+
+    private final DefaultChecksumAlgorithmService service = new 
DefaultChecksumAlgorithmService(
+            new 
DefaultChecksumAlgorithmFactorySelector(getChecksumAlgorithmFactories()));
+
+    @Test
+    void smokeTest() {
+        Collection<String> algNames = service.getChecksumAlgorithmNames();
+        assertEquals(4, algNames.size());
+    }
+
+    @Test
+    void emptySha1Calculator() {
+        ChecksumAlgorithmService.ChecksumCalculator calculator =
+                service.select("SHA-1").getCalculator();
+        calculator.update(ByteBuffer.allocate(0));
+        assertEquals(calculator.checksum(), 
"da39a3ee5e6b4b0d3255bfef95601890afd80709");
+    }
+
+    @Test
+    void calculateByte() throws IOException {
+        Map<ChecksumAlgorithmService.ChecksumAlgorithm, String> checksums = 
service.calculate(
+                "test".getBytes(StandardCharsets.UTF_8), 
service.select(Arrays.asList("SHA-1", "MD5")));
+        assertNotNull(checksums);
+        assertEquals(2, checksums.size());
+        assertEquals("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 
checksums.get(service.select("SHA-1")));
+        assertEquals("098f6bcd4621d373cade4e832627b4f6", 
checksums.get(service.select("MD5")));
+    }
+
+    @Test
+    void calculateByteBuffer() throws IOException {
+        Map<ChecksumAlgorithmService.ChecksumAlgorithm, String> checksums = 
service.calculate(
+                ByteBuffer.wrap("test".getBytes(StandardCharsets.UTF_8)),
+                service.select(Arrays.asList("SHA-1", "MD5")));
+        assertNotNull(checksums);
+        assertEquals(2, checksums.size());
+        assertEquals("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 
checksums.get(service.select("SHA-1")));
+        assertEquals("098f6bcd4621d373cade4e832627b4f6", 
checksums.get(service.select("MD5")));
+    }
+
+    @Test
+    void calculateStream() throws IOException {
+        Map<ChecksumAlgorithmService.ChecksumAlgorithm, String> checksums = 
service.calculate(
+                new 
ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8)),
+                service.select(Arrays.asList("SHA-1", "MD5")));
+        assertNotNull(checksums);
+        assertEquals(2, checksums.size());
+        assertEquals("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 
checksums.get(service.select("SHA-1")));
+        assertEquals("098f6bcd4621d373cade4e832627b4f6", 
checksums.get(service.select("MD5")));
+    }
+}

Reply via email to