diff --git a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java
index ca599429e..77318722c 100644
--- a/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java
+++ b/powertools-common/src/main/java/software/amazon/lambda/powertools/common/internal/ClassPreLoader.java
@@ -22,12 +22,17 @@
import java.net.URLConnection;
import java.util.Enumeration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
* Used to preload classes to support automatic priming for SnapStart
*/
public final class ClassPreLoader {
public static final String CLASSES_FILE = "classesloaded.txt";
+ private static final Logger LOG = LoggerFactory.getLogger(ClassPreLoader.class);
+
private ClassPreLoader() {
// Hide default constructor
}
@@ -57,6 +62,7 @@ public static void preloadClasses() {
* @param is
*/
private static void preloadClassesFromStream(InputStream is) {
+ int loaded = 0;
try (is;
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)) {
@@ -66,26 +72,30 @@ private static void preloadClassesFromStream(InputStream is) {
if (idx != -1) {
line = line.substring(0, idx);
}
- final String className = line.stripTrailing();
- if (!className.isBlank()) {
- loadClassIfFound(className);
+ final String className = line.strip();
+ if (!className.isBlank() && loadClassIfFound(className)) {
+ loaded++;
}
}
} catch (Exception ignored) {
// No action is required if preloading fails for any reason
}
+ LOG.debug("SnapStart priming: preloaded {} class(es) from {}", loaded, CLASSES_FILE);
}
/**
* Initializes the class with given name if found, ignores otherwise
*
- * @param className
+ * @param className the binary name of the class to load
+ * @return true if the class was found and loaded, false otherwise
*/
- private static void loadClassIfFound(String className) {
+ private static boolean loadClassIfFound(String className) {
try {
Class.forName(className, true, ClassPreLoader.class.getClassLoader());
+ return true;
} catch (ClassNotFoundException e) {
// No action is required if the class with given name cannot be found
+ return false;
}
}
}
\ No newline at end of file
diff --git a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java
index 03991688e..fb1359434 100644
--- a/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java
+++ b/powertools-common/src/test/java/software/amazon/lambda/powertools/common/internal/ClassPreLoaderTest.java
@@ -8,6 +8,7 @@ class ClassPreLoaderTest {
// Making this volatile so the Thread Context doesn't need any special handling
static volatile boolean dummyClassLoaded = false;
+ static volatile boolean leadingSpaceDummyClassLoaded = false;
/**
* Dummy class to be loaded by ClassPreLoader in test.
@@ -20,15 +21,35 @@ static class DummyClass {
dummyClassLoaded = true;
}
}
+
+ /**
+ * Dummy class whose name is referenced with a leading space in
+ * powertools-common/src/test/resources/classesloaded.txt.
+ * Some modules ship a classesloaded.txt with a leading space on every line, so the loader must
+ * strip leading whitespace before calling Class.forName.
+ */
+ static class LeadingSpaceDummyClass {
+ static {
+ leadingSpaceDummyClassLoaded = true;
+ }
+ }
+
@Test
void preloadClasses_shouldIgnoreInvalidClassesAndLoadValidClasses() {
dummyClassLoaded = false;
- // powertools-common/src/test/resources/classesloaded.txt has a class that does not exist
+ leadingSpaceDummyClassLoaded = false;
+ // A class only runs its static initializer the first time it is loaded, so this test calls
+ // preloadClasses once and asserts on all classes listed in classesloaded.txt together.
+ // powertools-common/src/test/resources/classesloaded.txt has a class that does not exist.
// Verify that the missing class did not throw any exception
assertDoesNotThrow(ClassPreLoader::preloadClasses);
- // When the classloaded.txt is a mixed bag of valid and invalid classes, Valid class must load
+ // When the classloaded.txt is a mixed bag of valid and invalid classes, valid classes must load
assertTrue(dummyClassLoaded, "DummyClass should be loaded");
+ // classesloaded.txt references LeadingSpaceDummyClass with a leading space. The loader must
+ // strip it before Class.forName, otherwise the class is never loaded.
+ assertTrue(leadingSpaceDummyClassLoaded,
+ "LeadingSpaceDummyClass should be loaded despite the leading space");
}
}
\ No newline at end of file
diff --git a/powertools-common/src/test/resources/classesloaded.txt b/powertools-common/src/test/resources/classesloaded.txt
index 498ffdf69..a6cda309e 100644
--- a/powertools-common/src/test/resources/classesloaded.txt
+++ b/powertools-common/src/test/resources/classesloaded.txt
@@ -1,2 +1,3 @@
software.amazon.lambda.powertools.common.internal.NonExistingClass
-software.amazon.lambda.powertools.common.internal.ClassPreLoaderTest$DummyClass
\ No newline at end of file
+software.amazon.lambda.powertools.common.internal.ClassPreLoaderTest$DummyClass
+ software.amazon.lambda.powertools.common.internal.ClassPreLoaderTest$LeadingSpaceDummyClass
diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java
index a18b1580d..3627ab355 100644
--- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java
+++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java
@@ -28,7 +28,11 @@
import software.amazon.lambda.powertools.tracing.TracingUtils;
public class PowerTracerToolEnabledForResponseWithCustomMapper implements RequestHandler