Skip to content

Commit 88abec2

Browse files
authored
Load the CRC checksum from CRT using Class path loaders inst… (#5692)
* bugfix : Load the CRC checksum from CRT using Class path loaders instead of direct reference of crt related classes * Handled PR comments * Handled comment to make it private
1 parent 83ae1a7 commit 88abec2

File tree

15 files changed

+260
-72
lines changed

15 files changed

+260
-72
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Load Checksum classes of CRT from Classloader instead of direct references of CRT classes."
6+
}

core/checksums/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
<groupId>software.amazon.awssdk.crt</groupId>
5656
<artifactId>aws-crt</artifactId>
5757
<version>${awscrt.version}</version>
58-
<optional>true</optional>
58+
<scope>test</scope>
5959
</dependency>
6060

6161
<dependency>

core/checksums/src/main/java/software/amazon/awssdk/checksums/SdkChecksum.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import java.util.zip.Checksum;
2020
import software.amazon.awssdk.annotations.SdkProtectedApi;
2121
import software.amazon.awssdk.checksums.internal.Crc32Checksum;
22-
import software.amazon.awssdk.checksums.internal.Crc32cProvider;
2322
import software.amazon.awssdk.checksums.internal.Crc64NvmeChecksum;
23+
import software.amazon.awssdk.checksums.internal.CrcChecksumProvider;
2424
import software.amazon.awssdk.checksums.internal.Md5Checksum;
2525
import software.amazon.awssdk.checksums.internal.Sha1Checksum;
2626
import software.amazon.awssdk.checksums.internal.Sha256Checksum;
@@ -39,7 +39,7 @@ public interface SdkChecksum extends Checksum {
3939
static SdkChecksum forAlgorithm(ChecksumAlgorithm algorithm) {
4040
switch (algorithm.algorithmId()) {
4141
case "CRC32C":
42-
return Crc32cProvider.create();
42+
return CrcChecksumProvider.crc32cImplementation();
4343
case "CRC32":
4444
return new Crc32Checksum();
4545
case "SHA1":

core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/Crc64NvmeChecksum.java

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,42 +15,50 @@
1515

1616
package software.amazon.awssdk.checksums.internal;
1717

18-
import static software.amazon.awssdk.utils.DependencyValidate.requireClass;
1918
import static software.amazon.awssdk.utils.NumericUtils.longToByte;
2019

21-
import java.util.zip.Checksum;
2220
import software.amazon.awssdk.annotations.SdkInternalApi;
2321
import software.amazon.awssdk.checksums.SdkChecksum;
24-
import software.amazon.awssdk.crt.checksums.CRC64NVME;
2522

2623
/**
2724
* Implementation of {@link SdkChecksum} to calculate an CRC64NVME checksum.
2825
*/
2926
@SdkInternalApi
30-
public final class Crc64NvmeChecksum extends BaseCrcChecksum {
31-
private static final String CRT_CRC64NVME_PATH = "software.amazon.awssdk.crt.checksums.CRC64NVME";
32-
private static final String CRT_MODULE = "software.amazon.awssdk.crt:aws-crt";
27+
public final class Crc64NvmeChecksum implements SdkChecksum {
28+
29+
private final SdkChecksum sdkChecksum;
3330

3431
public Crc64NvmeChecksum() {
35-
super(getCrc64Nvme());
32+
this.sdkChecksum = CrcChecksumProvider.crc64NvmeCrtImplementation();
3633
}
3734

38-
private static CRC64NVME getCrc64Nvme() {
39-
requireClass(CRT_CRC64NVME_PATH, CRT_MODULE, "CRC64NVME");
40-
return new CRC64NVME();
35+
@Override
36+
public byte[] getChecksumBytes() {
37+
return longToByte(sdkChecksum.getValue());
4138
}
4239

4340
@Override
44-
public Checksum cloneChecksum(Checksum checksum) {
45-
if (checksum instanceof CRC64NVME) {
46-
return (Checksum) ((CRC64NVME) checksum).clone();
47-
}
41+
public void mark(int readLimit) {
42+
this.sdkChecksum.mark(readLimit);
43+
}
4844

49-
throw new IllegalStateException("Unsupported checksum");
45+
@Override
46+
public void update(int b) {
47+
this.sdkChecksum.update(b);
5048
}
5149

5250
@Override
53-
public byte[] getChecksumBytes() {
54-
return longToByte(getChecksum().getValue());
51+
public void update(byte[] b, int off, int len) {
52+
this.sdkChecksum.update(b, off, len);
53+
}
54+
55+
@Override
56+
public long getValue() {
57+
return sdkChecksum.getValue();
58+
}
59+
60+
@Override
61+
public void reset() {
62+
sdkChecksum.reset();
5563
}
5664
}

core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/Crc32cProvider.java renamed to core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/CrcChecksumProvider.java

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,34 @@
1818
import java.util.zip.Checksum;
1919
import software.amazon.awssdk.annotations.SdkInternalApi;
2020
import software.amazon.awssdk.checksums.SdkChecksum;
21-
import software.amazon.awssdk.crt.checksums.CRC32C;
2221

2322
/**
24-
* Utility class to provide different implementations of CRC32C checksum. This class supports the use of: 1. Java-based CRC32C
25-
* (Java 9+ when available) 2. CRT-based CRC32C (when available) 3. SDK-based CRC32C (as fallback)
23+
* Utility class providing implementations of CRC checksums, specifically CRC32C and CRC64NVME.
24+
*
25+
* <p>Supports the following implementations for CRC32C:</p>
26+
* <ul>
27+
* <li>Java-based CRC32C (Java 9+)</li>
28+
* <li>CRT-based CRC32C (using AWS CRT library)</li>
29+
* <li>SDK-based CRC32C (fallback)</li>
30+
* </ul>
31+
*
32+
* <p>Only supports CRT-based implementation for CRC64NVME (using AWS CRT library).</p>
33+
*
34+
* <p>For internal use only ({@link SdkInternalApi}).</p>
2635
*/
2736
@SdkInternalApi
28-
public final class Crc32cProvider {
37+
public final class CrcChecksumProvider {
2938

3039

3140
// Class paths for different CRC32C implementations
3241
private static final String CRT_CRC32C_CLASS_PATH = "software.amazon.awssdk.crt.checksums.CRC32C";
3342
private static final String JAVA_CRC32C_CLASS_PATH = "java.util.zip.CRC32C";
3443
private static final ConstructorCache CONSTRUCTOR_CACHE = new ConstructorCache();
44+
private static final String CRT_CRC64NVME_PATH = "software.amazon.awssdk.crt.checksums.CRC64NVME";
45+
private static final String CRT_MODULE = "software.amazon.awssdk.crt:aws-crt";
3546

3647
// Private constructor to prevent instantiation
37-
private Crc32cProvider() {
48+
private CrcChecksumProvider() {
3849
}
3950

4051
/**
@@ -44,7 +55,7 @@ private Crc32cProvider() {
4455
*/
4556
static SdkChecksum createSdkBasedCrc32C() {
4657
SdkCrc32CChecksum sdkChecksum = SdkCrc32CChecksum.create();
47-
return new CrcCloneOnMarkChecksum(sdkChecksum, checksumToClone -> ((SdkCrc32CChecksum) checksumToClone).clone());
58+
return new CrcCloneOnMarkChecksum(sdkChecksum);
4859
}
4960

5061
/**
@@ -54,7 +65,7 @@ static SdkChecksum createSdkBasedCrc32C() {
5465
*
5566
* @return An instance of {@link SdkChecksum}, based on the first available option.
5667
*/
57-
public static SdkChecksum create() {
68+
public static SdkChecksum crc32cImplementation() {
5869
SdkChecksum checksum = createJavaCrc32C();
5970
if (checksum == null) {
6071
checksum = createCrtCrc32C();
@@ -65,14 +76,38 @@ public static SdkChecksum create() {
6576
static SdkChecksum createCrtCrc32C() {
6677
return CONSTRUCTOR_CACHE.getConstructor(CRT_CRC32C_CLASS_PATH).map(constructor -> {
6778
try {
68-
return new CrcCloneOnMarkChecksum((Checksum) constructor.newInstance(), checksumToClone ->
69-
(Checksum) ((CRC32C) checksumToClone).clone());
70-
} catch (ClassCastException | ReflectiveOperationException e) {
71-
throw new IllegalStateException("Failed to instantiate " + JAVA_CRC32C_CLASS_PATH, e);
79+
Checksum checksumInstance = (Checksum) constructor.newInstance();
80+
return new CrcCloneOnMarkChecksum(checksumInstance);
81+
} catch (ReflectiveOperationException e) {
82+
throw new IllegalStateException("Failed to instantiate " + CRT_CRC32C_CLASS_PATH, e);
7283
}
7384
}).orElse(null);
7485
}
7586

87+
/**
88+
* Creates an instance of the CRT-based CRC64NVME checksum using AWS's CRT library.
89+
* <p>
90+
* Attempts to load the `CRC64NVME` implementation specified by `CRT_CRC64NVME_PATH` and, if successful,
91+
* wraps it in {@link CrcCloneOnMarkChecksum}. Throws a {@link RuntimeException} if `CRC64NVME` is unavailable.
92+
* </p>
93+
*
94+
* @return An {@link SdkChecksum} instance for CRC64NVME.
95+
* @throws IllegalStateException if instantiation fails.
96+
* @throws RuntimeException if the `CRC64NVME` implementation is not available.
97+
*/
98+
static SdkChecksum crc64NvmeCrtImplementation() {
99+
return CONSTRUCTOR_CACHE.getConstructor(CRT_CRC64NVME_PATH).map(constructor -> {
100+
try {
101+
Checksum checksumInstance = (Checksum) constructor.newInstance();
102+
return new CrcCloneOnMarkChecksum(checksumInstance);
103+
} catch (ReflectiveOperationException e) {
104+
throw new IllegalStateException("Failed to instantiate " + CRT_CRC32C_CLASS_PATH, e);
105+
}
106+
}).orElseThrow(() -> new RuntimeException(
107+
"Could not load " + CRT_CRC64NVME_PATH + ". Add dependency on '" + CRT_MODULE
108+
+ "' module to enable CRC64NVME feature."));
109+
}
110+
76111
static SdkChecksum createJavaCrc32C() {
77112
return CONSTRUCTOR_CACHE.getConstructor(JAVA_CRC32C_CLASS_PATH).map(constructor -> {
78113
try {

core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/CrcCloneOnMarkChecksum.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import static software.amazon.awssdk.utils.NumericUtils.longToByte;
1919

20-
import java.util.function.Function;
20+
import java.lang.reflect.Method;
2121
import java.util.zip.Checksum;
2222
import software.amazon.awssdk.annotations.SdkInternalApi;
2323

@@ -32,22 +32,31 @@
3232
@SdkInternalApi
3333
public final class CrcCloneOnMarkChecksum extends BaseCrcChecksum {
3434

35-
private final Function<Checksum, Checksum> cloneFunction;
36-
37-
public CrcCloneOnMarkChecksum(Checksum checksum, Function<Checksum, Checksum> cloneFunction) {
35+
public CrcCloneOnMarkChecksum(Checksum checksum) {
3836
super(checksum);
39-
this.cloneFunction = cloneFunction;
4037
}
4138

4239
@Override
4340
Checksum cloneChecksum(Checksum checksum) {
44-
// Use the function to clone the checksum
45-
return cloneFunction.apply(checksum);
41+
return cloneIfCloneable(checksum);
4642
}
4743

4844
@Override
4945
public byte[] getChecksumBytes() {
5046
byte[] valueBytes = longToByte(getValue());
5147
return new byte[] { valueBytes[4], valueBytes[5], valueBytes[6], valueBytes[7] };
5248
}
53-
}
49+
50+
// Function to clone any Checksum that implements Cloneable
51+
private Checksum cloneIfCloneable(Checksum checksum) {
52+
if (checksum instanceof Cloneable) {
53+
try {
54+
Method cloneMethod = checksum.getClass().getMethod("clone");
55+
return (Checksum) cloneMethod.invoke(checksum);
56+
} catch (Exception e) {
57+
throw new IllegalStateException("Failed to clone the checksum", e);
58+
}
59+
}
60+
throw new UnsupportedOperationException("Checksum does not support cloning");
61+
}
62+
}

core/checksums/src/test/java/software/amazon/awssdk/checksums/internal/CRTBasedCRC32CChecksumTest.java

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.checksums.internal;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
20+
import java.math.BigInteger;
21+
import java.nio.charset.StandardCharsets;
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
import software.amazon.awssdk.checksums.SdkChecksum;
25+
import software.amazon.awssdk.utils.BinaryUtils;
26+
27+
class Crc64NvmeChecksumTest {
28+
29+
private SdkChecksum sdkChecksum;
30+
private static final String TEST_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
31+
32+
@BeforeEach
33+
public void setUp() {
34+
sdkChecksum = new Crc64NvmeChecksum();
35+
}
36+
37+
@Test
38+
void validateCrcChecksumValues() {
39+
byte[] bytes = TEST_STRING.getBytes(StandardCharsets.UTF_8);
40+
sdkChecksum.update(bytes, 0, bytes.length);
41+
assertEquals("0000000000000000000000008b8f30cfc6f16409", getAsString(sdkChecksum.getChecksumBytes()));
42+
}
43+
44+
@Test
45+
void validateEncodedBase64ForCrc() {
46+
sdkChecksum.update("abc".getBytes(StandardCharsets.UTF_8));
47+
String toBase64 = BinaryUtils.toBase64(sdkChecksum.getChecksumBytes());
48+
assertEquals("BeXKuz/B+us=", toBase64);
49+
}
50+
51+
@Test
52+
void validateMarkAndResetForCrc() {
53+
sdkChecksum.update("ab".getBytes(StandardCharsets.UTF_8));
54+
sdkChecksum.mark(3);
55+
sdkChecksum.update("xyz".getBytes(StandardCharsets.UTF_8));
56+
sdkChecksum.reset();
57+
sdkChecksum.update("c".getBytes(StandardCharsets.UTF_8));
58+
String toBase64 = BinaryUtils.toBase64(sdkChecksum.getChecksumBytes());
59+
assertEquals("BeXKuz/B+us=", toBase64);
60+
}
61+
62+
@Test
63+
void validateMarkForCrc() {
64+
sdkChecksum.update("Hello ".getBytes(StandardCharsets.UTF_8));
65+
sdkChecksum.mark(4);
66+
sdkChecksum.update("world".getBytes(StandardCharsets.UTF_8));
67+
String toBase64 = BinaryUtils.toBase64(sdkChecksum.getChecksumBytes());
68+
assertEquals("OOJZ0D8xKts=", toBase64);
69+
}
70+
71+
@Test
72+
void validateSingleMarksForCrc() {
73+
sdkChecksum.update("alpha".getBytes(StandardCharsets.UTF_8));
74+
sdkChecksum.mark(3);
75+
sdkChecksum.update("beta".getBytes(StandardCharsets.UTF_8));
76+
sdkChecksum.reset();
77+
String toBase64 = BinaryUtils.toBase64(sdkChecksum.getChecksumBytes());
78+
assertEquals("Ehnh98TMQlQ=", toBase64);
79+
}
80+
81+
@Test
82+
void validateMultipleMarksForCrc() {
83+
sdkChecksum.update("alpha".getBytes(StandardCharsets.UTF_8));
84+
sdkChecksum.mark(3);
85+
sdkChecksum.update("beta".getBytes(StandardCharsets.UTF_8));
86+
sdkChecksum.mark(5);
87+
sdkChecksum.update("gamma".getBytes(StandardCharsets.UTF_8));
88+
sdkChecksum.reset();
89+
sdkChecksum.update("delta".getBytes(StandardCharsets.UTF_8));
90+
String toBase64 = BinaryUtils.toBase64(sdkChecksum.getChecksumBytes());
91+
// Final checksum of "alphabetadelta"
92+
assertEquals("ugWp+3k2NgA=", toBase64);
93+
}
94+
95+
@Test
96+
void validateResetWithoutMarkForCrc() {
97+
sdkChecksum.update("beta".getBytes(StandardCharsets.UTF_8));
98+
sdkChecksum.reset();
99+
sdkChecksum.update("alpha".getBytes(StandardCharsets.UTF_8));
100+
String toBase64 = BinaryUtils.toBase64(sdkChecksum.getChecksumBytes());
101+
// Checksum of "alpha"
102+
assertEquals("Ehnh98TMQlQ=", toBase64);
103+
}
104+
105+
private String getAsString(byte[] checksumBytes) {
106+
return String.format("%040x", new BigInteger(1, checksumBytes));
107+
}
108+
}

0 commit comments

Comments
 (0)