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")));
+ }
+}