diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-641f6c9.json b/.changes/next-release/bugfix-AWSSDKforJavav2-641f6c9.json new file mode 100644 index 000000000000..c7c0d934c47e --- /dev/null +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-641f6c9.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Fix an issue where the CRC64NVME class cannot be loaded from CRT in some situations, even if `aws-crt` is correctly included in the application classpath." +} diff --git a/core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/ConstructorCache.java b/core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/ConstructorCache.java index af539cde94ae..ef10bf8a29af 100644 --- a/core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/ConstructorCache.java +++ b/core/checksums/src/main/java/software/amazon/awssdk/checksums/internal/ConstructorCache.java @@ -17,23 +17,25 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; -import java.util.Collections; import java.util.Map; import java.util.Optional; -import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.utils.ClassLoaderHelper; import software.amazon.awssdk.utils.Logger; /** - * A cache that stores classes and their constructors by class name and class loader. + * A cache that stores classes and exposes a method to retrieve its zero-argument constructor. *

- * This cache uses weak references to both class loaders and classes, allowing them to be garbage collected - * when no longer needed. It provides methods to retrieve the zero-argument constructor for a class, - * based on the current thread's context class loader or the system class loader. + * This cache stores weak references to loaded classes, allowing them to be garbage collected at any point. + *

+ * Classes are loaded by first attempting to load it via the thread context {@code ClassLoader} (or system {@code ClassLoader} if + * the calling thread does not have one). If that fails, it will attempt using the {@code ClassLoader} that loaded + * {@link ClassLoaderHelper}. *

* If a class or its zero-argument constructor cannot be found, an empty result is returned. + * + * @see ClassLoaderHelper#loadClass(String, boolean, Class[]) */ @SdkInternalApi public final class ConstructorCache { @@ -43,7 +45,7 @@ public final class ConstructorCache { * Cache storing classes by class name and class loader. * Uses weak references to allow garbage collection when not needed. */ - private final Map>>>> classesByClassName = + private final Map>>> classesByClassName = new ConcurrentHashMap<>(); /** @@ -51,28 +53,28 @@ public final class ConstructorCache { * Returns an empty result if the class is not found. */ private Optional> getClass(String className) { - Map>>> classesByClassLoader = - classesByClassName.computeIfAbsent(className, k -> Collections.synchronizedMap(new WeakHashMap<>())); - - ClassLoader classLoader = ClassLoaderHelper.contextClassLoader(); - Optional>> classRef = classesByClassLoader.computeIfAbsent(classLoader, k -> { + Optional>> classRef = classesByClassName.computeIfAbsent(className, k -> { try { - Class clazz = classLoader.loadClass(className); + Class clazz = ClassLoaderHelper.loadClass(k, false); return Optional.of(new WeakReference<>(clazz)); } catch (ClassNotFoundException e) { return Optional.empty(); } }); - // if the WeakReference to the class has been garbage collected, remove it from the cache and try again + // Were we able to find this class? if (classRef.isPresent()) { Class clazz = classRef.get().get(); + // Class hasn't been GC'd if (clazz != null) { return Optional.of(clazz); } - classesByClassLoader.remove(classLoader); + // if the WeakReference to the class has been garbage collected, it has been unloaded. + // Remove it from the cache and try a fresh load + classesByClassName.remove(className); return getClass(className); } + return Optional.empty(); }