From bf40ad637a4ad4fe53cf81762530b4db53370f31 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Wed, 12 Nov 2025 15:44:36 +0100 Subject: [PATCH 1/9] improv(parameters): Make parameters top-level provider thread-safe for cache and transformer management. --- .../parameters/cache/CacheManagerTest.java | 94 ++++++++++++++- .../transform/TransformationManagerTest.java | 111 +++++++++++++++++- .../powertools/parameters/BaseProvider.java | 31 +++-- .../parameters/cache/CacheManager.java | 23 ++-- .../parameters/cache/DataStore.java | 6 +- .../transform/TransformationManager.java | 43 +++++-- 6 files changed, 274 insertions(+), 34 deletions(-) diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java index 2bcfcc566..a305c7945 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java @@ -21,6 +21,7 @@ import java.time.Clock; import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -69,7 +70,6 @@ public void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldRet manager.setDefaultExpirationTime(of(42, SECONDS)); manager.putInCache("key", "value"); - Optional value = manager.getIfNotExpired("key", offset(clock, of(40, SECONDS)).instant()); assertThat(value).isPresent().contains("value"); @@ -101,4 +101,96 @@ public void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() assertThat(value2).isPresent().contains("value2"); } + @Test + public void putInCache_sharedCache_shouldBeAccessibleAcrossThreads() throws InterruptedException { + // GIVEN + Thread thread1 = new Thread(() -> { + manager.setExpirationTime(of(60, SECONDS)); + manager.putInCache("sharedKey", "valueFromThread1"); + manager.resetExpirationTime(); + }); + + Thread thread2 = new Thread(() -> { + manager.setExpirationTime(of(10, SECONDS)); + // Thread 2 should be able to read the value cached by Thread 1 + Optional value = manager.getIfNotExpired("sharedKey", clock.instant()); + assertThat(value).isPresent().contains("valueFromThread1"); + manager.resetExpirationTime(); + }); + + // WHEN + thread1.start(); + thread1.join(); + thread2.start(); + thread2.join(); + + // THEN - Both threads should be able to access the same cached value + Optional value = manager.getIfNotExpired("sharedKey", clock.instant()); + assertThat(value).isPresent().contains("valueFromThread1"); + } + + @Test + public void putInCache_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + int threadCount = 10; + Thread[] threads = new Thread[threadCount]; + boolean[] success = new boolean[threadCount]; + Clock testClock = Clock.systemDefaultZone(); + + // WHEN - Multiple threads set different expiration times and cache values concurrently + for (int i = 0; i < threadCount; i++) { + final int threadIndex = i; + final int expirationSeconds = (i % 2 == 0) ? 60 : 10; // Alternate between 60s and 10s + + threads[i] = new Thread(() -> { + try { + manager.setExpirationTime(of(expirationSeconds, SECONDS)); + manager.putInCache("key" + threadIndex, "value" + threadIndex); + manager.resetExpirationTime(); + success[threadIndex] = true; + } catch (Exception e) { + success[threadIndex] = false; + } + }); + } + + // Start all threads + for (Thread thread : threads) { + thread.start(); + } + + // Wait for all threads to complete + for (Thread thread : threads) { + thread.join(); + } + + // THEN - All threads should complete successfully + for (boolean result : success) { + assertThat(result).isTrue(); + } + + // THEN - Each cached value should have the correct expiration time + // Values with 60s TTL should still be present after 9s, values with 10s should expire after 11s + for (int i = 0; i < threadCount; i++) { + final int expirationSeconds = (i % 2 == 0) ? 60 : 10; + + // Check that value is still present just before expiration + Optional valueBeforeExpiry = manager.getIfNotExpired("key" + i, + offset(testClock, of(expirationSeconds - 1, SECONDS)).instant()); + assertThat(valueBeforeExpiry) + .as("Thread %d with %ds expiration should still have value after %ds", i, expirationSeconds, + expirationSeconds - 1) + .isPresent() + .contains("value" + i); + + // Check that value expires after the TTL + Optional valueAfterExpiry = manager.getIfNotExpired("key" + i, + offset(testClock, of(expirationSeconds + 1, SECONDS)).instant()); + assertThat(valueAfterExpiry) + .as("Thread %d with %ds expiration should not have value after %ds", i, expirationSeconds, + expirationSeconds + 1) + .isNotPresent(); + } + } + } diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java index 39e69f9e0..e92fc5546 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java @@ -21,8 +21,10 @@ import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import java.util.Base64; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import software.amazon.lambda.powertools.parameters.exception.TransformationException; public class TransformationManagerTest { @@ -90,9 +92,9 @@ public void performComplexTransformation_noTransformer_shouldThrowException() { public void performComplexTransformation_shouldPerformTransformation() { manager.setTransformer(json); - ObjectToDeserialize object = - manager.performComplexTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", - ObjectToDeserialize.class); + ObjectToDeserialize object = manager.performComplexTransformation( + "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class); assertThat(object).isNotNull(); } @@ -104,4 +106,107 @@ public void performComplexTransformation_throwsTransformationException() { assertThatExceptionOfType(TransformationException.class) .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); } + + @Test + public void unsetTransformer_shouldCleanUpThreadLocal() { + // GIVEN + manager.setTransformer(json); + assertThat(manager.shouldTransform()).isTrue(); + + // WHEN + manager.unsetTransformer(); + + // THEN + assertThat(manager.shouldTransform()).isFalse(); + } + + @Test + public void setTransformer_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + boolean[] success = new boolean[2]; + + Thread thread1 = new Thread(() -> { + try { + manager.setTransformer(json); + Thread.sleep(10); // Small delay to increase chance of thread interleaving + // Thread 1 expects json transformer + String result = manager.performComplexTransformation( + "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class).getFoo(); + success[0] = result.equals("Foo"); + } catch (Exception e) { + e.printStackTrace(); + success[0] = false; + } + }); + + Thread thread2 = new Thread(() -> { + try { + Thread.sleep(5); // Start slightly after thread1 + manager.setTransformer(base64); + // Thread 2 expects base64 transformer + String result = manager.performBasicTransformation( + Base64.getEncoder().encodeToString("bar".getBytes())); + success[1] = result.equals("bar"); + } catch (Exception e) { + e.printStackTrace(); + success[1] = false; + } + }); + + // WHEN - Start both threads concurrently + thread1.start(); + thread2.start(); + + // THEN - Both threads should complete without errors + thread1.join(); + thread2.join(); + + assertThat(success[0]).as("Thread 1 with JSON transformer should succeed").isTrue(); + assertThat(success[1]).as("Thread 2 with Base64 transformer should succeed").isTrue(); + } + + @Test + public void unsetTransformer_concurrentCalls_shouldNotAffectOtherThreads() throws InterruptedException { + // GIVEN + boolean[] success = new boolean[2]; + + Thread thread1 = new Thread(() -> { + try { + manager.setTransformer(json); + Thread.sleep(10); + // Thread 1 should still have json transformer even if thread 2 unsets + assertThat(manager.shouldTransform()).isTrue(); + success[0] = true; + } catch (Exception e) { + e.printStackTrace(); + success[0] = false; + } + }); + + Thread thread2 = new Thread(() -> { + try { + manager.setTransformer(base64); + Thread.sleep(5); + manager.unsetTransformer(); + // Thread 2 should have no transformer after unset + assertThat(manager.shouldTransform()).isFalse(); + success[1] = true; + } catch (Exception e) { + e.printStackTrace(); + success[1] = false; + } + }); + + // WHEN + thread1.start(); + thread2.start(); + + // THEN + thread1.join(); + thread2.join(); + + assertThat(success[0]).as("Thread 1 should still have transformer").isTrue(); + assertThat(success[1]).as("Thread 2 should have unset transformer").isTrue(); + } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java index bedace28c..d83ff0298 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java @@ -19,7 +19,8 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; -import software.amazon.awssdk.annotations.NotThreadSafe; + +import software.amazon.awssdk.annotations.ThreadSafe; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.TransformationException; import software.amazon.lambda.powertools.parameters.transform.BasicTransformer; @@ -28,8 +29,20 @@ /** * Base class for all parameter providers. + *

+ * This class is thread-safe when used as a singleton in multi-threaded environments. + * Configuration methods ({@link #withMaxAge(int, ChronoUnit)}, {@link #withTransformation(Class)}) + * use thread-local storage to support concurrent requests with different requirements. + *

+ * The cache and transformation managers are thread-safe with zero synchronization overhead, + * using lock-free data structures (ThreadLocal, AtomicReference, ConcurrentHashMap) for optimal performance. + * The cache storage is shared across all threads, allowing cached values to be reused across requests. + *

+ * Implementation Requirements: Subclasses must ensure that implementations of + * {@link #getValue(String)} and {@link #getMultipleValues(String)} are thread-safe to + * guarantee overall thread-safety of the provider. */ -@NotThreadSafe +@ThreadSafe public abstract class BaseProvider implements ParamProvider { public static final String PARAMETERS = "parameters"; @@ -91,6 +104,7 @@ public BaseProvider withMaxAge(int maxAge, ChronoUnit unit) { * @param transformerClass Class of the transformer to apply. For convenience, you can use {@link Transformer#json} or {@link Transformer#base64} shortcuts. * @return the provider itself in order to chain calls (eg.

provider.withTransformation(json).get("key", MyObject.class)
). */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public BaseProvider withTransformation(Class transformerClass) { if (transformationManager == null) { throw new IllegalStateException( @@ -110,12 +124,12 @@ public BaseProvider withTransformation(Class transformerC * eg. getMultiple("/foo/bar") will retrieve [key="baz", value="valuebaz"] for parameter "/foo/bar/baz" */ @Override + @SuppressWarnings("unchecked") // Cache stores Object, safe cast as we control what's stored public Map getMultiple(String path) { // remove trailing whitespace String pathWithoutTrailingSlash = path.replaceAll("\\/+$", ""); try { - return (Map) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> - { + return (Map) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> { Map params = getMultipleValues(pathWithoutTrailingSlash); cacheManager.putInCache(pathWithoutTrailingSlash, params); @@ -143,8 +157,7 @@ public Map getMultiple(String path) { @Override public String get(final String key) { try { - return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> - { + return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { String value = getValue(key); String transformedValue = value; @@ -175,10 +188,10 @@ public String get(final String key) { * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. */ @Override + @SuppressWarnings("unchecked") // Cache stores Object, safe cast as we control what's stored public T get(final String key, final Class targetClass) { try { - return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> - { + return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { String value = getValue(key); if (transformationManager == null) { @@ -207,7 +220,7 @@ protected Instant now() { protected void resetToDefaults() { cacheManager.resetExpirationTime(); if (transformationManager != null) { - transformationManager.setTransformer(null); + transformationManager.unsetTransformer(); } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java index b868cb642..99c281b3d 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java @@ -20,18 +20,27 @@ import java.time.Duration; import java.time.Instant; import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +/** + * Manages caching of parameter values with configurable expiration times. + *

+ * This class is thread-safe. The cache storage is shared across all threads, + * while expiration time configuration is thread-local to support concurrent + * requests with different cache TTL requirements. + */ public class CacheManager { static final Duration DEFAULT_MAX_AGE_SECS = Duration.of(5, SECONDS); private final DataStore store; - private Duration defaultMaxAge = DEFAULT_MAX_AGE_SECS; - private Duration maxAge = defaultMaxAge; + private final AtomicReference defaultMaxAge = new AtomicReference<>(DEFAULT_MAX_AGE_SECS); + private final ThreadLocal maxAge = ThreadLocal.withInitial(() -> null); public CacheManager() { store = new DataStore(); } + @SuppressWarnings("unchecked") // DataStore stores Object, safe cast as we control what's stored public Optional getIfNotExpired(String key, Instant now) { if (store.hasExpired(key, now)) { return Optional.empty(); @@ -40,19 +49,19 @@ public Optional getIfNotExpired(String key, Instant now) { } public void setExpirationTime(Duration duration) { - this.maxAge = duration; + this.maxAge.set(duration); } public void setDefaultExpirationTime(Duration duration) { - this.defaultMaxAge = duration; - this.maxAge = duration; + this.defaultMaxAge.set(duration); } public void putInCache(String key, T value) { - store.put(key, value, Clock.systemDefaultZone().instant().plus(maxAge)); + Duration effectiveMaxAge = maxAge.get() != null ? maxAge.get() : defaultMaxAge.get(); + store.put(key, value, Clock.systemDefaultZone().instant().plus(effectiveMaxAge)); } public void resetExpirationTime() { - maxAge = defaultMaxAge; + maxAge.remove(); } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java index 737faa353..6ce8a3e82 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java @@ -42,7 +42,11 @@ public Object get(String key) { } public boolean hasExpired(String key, Instant now) { - boolean hasExpired = !store.containsKey(key) || now.isAfter(store.get(key).time); + ValueNode node = store.get(key); + if (node == null) { + return true; + } + boolean hasExpired = now.isAfter(node.time); // Auto-clean if the parameter has expired if (hasExpired) { remove(key); diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java index d3fbce14f..bddbf81d9 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java @@ -15,31 +15,44 @@ package software.amazon.lambda.powertools.parameters.transform; import java.lang.reflect.InvocationTargetException; + import software.amazon.lambda.powertools.parameters.exception.TransformationException; /** * Manager in charge of transforming parameter values in another format.
* Leverages a {@link Transformer} in order to perform the transformation.
* The transformer must be passed with {@link #setTransformer(Class)} before performing any transform operation. + *

+ * This class is thread-safe. Transformer configuration is thread-local to support concurrent + * requests with different transformation requirements. */ public class TransformationManager { - private Class transformer = null; + private final ThreadLocal> transformer = ThreadLocal.withInitial(() -> null); /** * Set the {@link Transformer} to use for transformation. Must be called before any transformation. * * @param transformerClass class of the {@link Transformer} */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public void setTransformer(Class transformerClass) { - this.transformer = transformerClass; + this.transformer.set(transformerClass); + } + + /** + * Unset the {@link Transformer} and clean up thread-local storage. + * Should be called after transformation is complete to prevent memory leaks. + */ + public void unsetTransformer() { + this.transformer.remove(); } /** * @return true if a {@link Transformer} has been passed to the Manager */ public boolean shouldTransform() { - return transformer != null; + return transformer.get() != null; } /** @@ -48,20 +61,22 @@ public boolean shouldTransform() { * @param value the value to transform * @return the value transformed */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public String performBasicTransformation(String value) { - if (transformer == null) { + Class transformerClass = transformer.get(); + if (transformerClass == null) { throw new IllegalStateException( "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } - if (!BasicTransformer.class.isAssignableFrom(transformer)) { + if (!BasicTransformer.class.isAssignableFrom(transformerClass)) { throw new IllegalStateException("Wrong Transformer for a String, choose a BasicTransformer."); } try { - BasicTransformer basicTransformer = - (BasicTransformer) transformer.getDeclaredConstructor().newInstance(null); + BasicTransformer basicTransformer = (BasicTransformer) transformerClass.getDeclaredConstructor() + .newInstance(null); return basicTransformer.applyTransformation(value); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | - InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException e) { throw new TransformationException(e); } } @@ -73,17 +88,19 @@ public String performBasicTransformation(String value) { * @param targetClass the type of the target object. * @return the value transformed in an object ot type T. */ + @SuppressWarnings("rawtypes") // Transformer type parameter determined at runtime public T performComplexTransformation(String value, Class targetClass) { - if (transformer == null) { + Class transformerClass = transformer.get(); + if (transformerClass == null) { throw new IllegalStateException( "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } try { - Transformer complexTransformer = transformer.getDeclaredConstructor().newInstance(null); + Transformer complexTransformer = transformerClass.getDeclaredConstructor().newInstance(null); return complexTransformer.applyTransformation(value, targetClass); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | - InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException e) { throw new TransformationException(e); } } From 5fc86a385a8a2bc3316a677a8f9e132c67799e89 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Wed, 12 Nov 2025 17:07:44 +0100 Subject: [PATCH 2/9] Use countdown latch instead of Thread.sleep for transformation manager tests. --- .../transform/TransformationManagerTest.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java index e92fc5546..14bdfccdf 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java @@ -21,6 +21,7 @@ import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import java.util.Base64; +import java.util.concurrent.CountDownLatch; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -124,11 +125,13 @@ public void unsetTransformer_shouldCleanUpThreadLocal() { public void setTransformer_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { // GIVEN boolean[] success = new boolean[2]; + CountDownLatch latch = new CountDownLatch(2); Thread thread1 = new Thread(() -> { try { + latch.countDown(); + latch.await(); manager.setTransformer(json); - Thread.sleep(10); // Small delay to increase chance of thread interleaving // Thread 1 expects json transformer String result = manager.performComplexTransformation( "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", @@ -142,7 +145,8 @@ public void setTransformer_concurrentCalls_shouldBeThreadSafe() throws Interrupt Thread thread2 = new Thread(() -> { try { - Thread.sleep(5); // Start slightly after thread1 + latch.countDown(); + latch.await(); manager.setTransformer(base64); // Thread 2 expects base64 transformer String result = manager.performBasicTransformation( @@ -170,11 +174,13 @@ public void setTransformer_concurrentCalls_shouldBeThreadSafe() throws Interrupt public void unsetTransformer_concurrentCalls_shouldNotAffectOtherThreads() throws InterruptedException { // GIVEN boolean[] success = new boolean[2]; + CountDownLatch latch = new CountDownLatch(2); Thread thread1 = new Thread(() -> { try { + latch.countDown(); + latch.await(); manager.setTransformer(json); - Thread.sleep(10); // Thread 1 should still have json transformer even if thread 2 unsets assertThat(manager.shouldTransform()).isTrue(); success[0] = true; @@ -186,8 +192,9 @@ public void unsetTransformer_concurrentCalls_shouldNotAffectOtherThreads() throw Thread thread2 = new Thread(() -> { try { + latch.countDown(); + latch.await(); manager.setTransformer(base64); - Thread.sleep(5); manager.unsetTransformer(); // Thread 2 should have no transformer after unset assertThat(manager.shouldTransform()).isFalse(); From aa4ce4d927ef583191aeea3711a6f807730ca21c Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Wed, 12 Nov 2025 17:11:30 +0100 Subject: [PATCH 3/9] Make SSMProvider and AppConfigProvider thread-safe. --- .../appconfig/AppConfigProvider.java | 4 +- .../parameters/ssm/SSMProvider.java | 30 ++--- .../parameters/ssm/SSMProviderTest.java | 106 +++++++++++++++++- 3 files changed, 118 insertions(+), 22 deletions(-) diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java index 37f07ae7a..06d00ffbe 100644 --- a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProvider.java @@ -14,8 +14,8 @@ package software.amazon.lambda.powertools.parameters.appconfig; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; @@ -46,7 +46,7 @@ public class AppConfigProvider extends BaseProvider { private final AppConfigDataClient client; private final String application; private final String environment; - private final Map establishedSessions = new HashMap<>(); + private final Map establishedSessions = new ConcurrentHashMap<>(); AppConfigProvider(CacheManager cacheManager, TransformationManager transformationManager, AppConfigDataClient client, String environment, String application) { diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java index c24b08b84..3cf728219 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProvider.java @@ -66,8 +66,8 @@ public class SSMProvider extends BaseProvider { private final SsmClient client; - private boolean decrypt = false; - private boolean recursive = false; + private final ThreadLocal decrypt = ThreadLocal.withInitial(() -> false); + private final ThreadLocal recursive = ThreadLocal.withInitial(() -> false); /** * Constructor with custom {@link SsmClient}.
@@ -109,7 +109,7 @@ public static SSMProvider create() { public String getValue(String key) { GetParameterRequest request = GetParameterRequest.builder() .name(key) - .withDecryption(decrypt) + .withDecryption(decrypt.get()) .build(); return client.getParameter(request).parameter().value(); } @@ -122,7 +122,7 @@ public String getValue(String key) { * @return the provider itself in order to chain calls (eg.

provider.withDecryption().get("key")
). */ public SSMProvider withDecryption() { - this.decrypt = true; + this.decrypt.set(true); return this; } @@ -133,7 +133,7 @@ public SSMProvider withDecryption() { * @return the provider itself in order to chain calls (eg.
provider.recursive().getMultiple("key")
). */ public SSMProvider recursive() { - this.recursive = true; + this.recursive.set(true); return this; } @@ -160,8 +160,8 @@ protected Map getMultipleValues(String path) { private Map getMultipleBis(String path, String nextToken) { GetParametersByPathRequest request = GetParametersByPathRequest.builder() .path(path) - .withDecryption(decrypt) - .recursive(recursive) + .withDecryption(decrypt.get()) + .recursive(recursive.get()) .nextToken(nextToken) .build(); @@ -170,12 +170,12 @@ private Map getMultipleBis(String path, String nextToken) { // not using the client.getParametersByPathPaginator() as hardly testable GetParametersByPathResponse res = client.getParametersByPath(request); if (res.hasParameters()) { - res.parameters().forEach(parameter -> - { - /* Standardize the parameter name - The parameter name returned by SSM will contain the full path. - However, for readability, we should return only the part after - the path. + res.parameters().forEach(parameter -> { + /* + * Standardize the parameter name + * The parameter name returned by SSM will contain the full path. + * However, for readability, we should return only the part after + * the path. */ String name = parameter.name(); if (name.startsWith(path)) { @@ -196,8 +196,8 @@ private Map getMultipleBis(String path, String nextToken) { @Override protected void resetToDefaults() { super.resetToDefaults(); - recursive = false; - decrypt = false; + decrypt.remove(); + recursive.remove(); } // For tests purpose only diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index db45dc21c..5777275f7 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; + import org.assertj.core.data.MapEntry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,6 +34,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; + import software.amazon.awssdk.services.ssm.SsmClient; import software.amazon.awssdk.services.ssm.model.GetParameterRequest; import software.amazon.awssdk.services.ssm.model.GetParameterResponse; @@ -165,8 +168,8 @@ public void getMultipleWithNextToken() { List parameters1 = new ArrayList<>(); parameters1.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters1.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); - GetParametersByPathResponse response1 = - GetParametersByPathResponse.builder().parameters(parameters1).nextToken("123abc").build(); + GetParametersByPathResponse response1 = GetParametersByPathResponse.builder().parameters(parameters1) + .nextToken("123abc").build(); List parameters2 = new ArrayList<>(); parameters2.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); @@ -185,8 +188,7 @@ public void getMultipleWithNextToken() { GetParametersByPathRequest request1 = requestParams.get(0); GetParametersByPathRequest request2 = requestParams.get(1); - assertThat(asList(request1, request2)).allSatisfy(req -> - { + assertThat(asList(request1, request2)).allSatisfy(req -> { assertThat(req.path()).isEqualTo("/prod/app1"); assertThat(req.withDecryption()).isFalse(); assertThat(req.recursive()).isFalse(); @@ -203,7 +205,101 @@ public void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationMana SSMProvider ssmProvider = SSMProvider.builder() .build(); // Assert - assertDoesNotThrow(()->ssmProvider.withTransformation(json)); + assertDoesNotThrow(() -> ssmProvider.withTransformation(json)); + } + + @Test + public void withDecryption_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + Parameter param1 = Parameter.builder().value("value1").build(); + Parameter param2 = Parameter.builder().value("value2").build(); + GetParameterResponse response1 = GetParameterResponse.builder().parameter(param1).build(); + GetParameterResponse response2 = GetParameterResponse.builder().parameter(param2).build(); + CountDownLatch latch = new CountDownLatch(2); + Mockito.when(client.getParameter(paramCaptor.capture())) + .thenReturn(response1, response2); + + // WHEN + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.withDecryption().getValue("key1"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.getValue("key2"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + // THEN + List requests = paramCaptor.getAllValues(); + assertThat(requests).hasSize(2); + boolean hasDecryptedRequest = requests.stream().anyMatch(GetParameterRequest::withDecryption); + boolean hasNonDecryptedRequest = requests.stream().anyMatch(r -> !r.withDecryption()); + assertThat(hasDecryptedRequest).isTrue(); + assertThat(hasNonDecryptedRequest).isTrue(); + } + + @Test + public void recursive_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + // GIVEN + List params1 = new ArrayList<>(); + params1.add(Parameter.builder().name("/path1/key1").value("value1").build()); + List params2 = new ArrayList<>(); + params2.add(Parameter.builder().name("/path2/key2").value("value2").build()); + GetParametersByPathResponse response1 = GetParametersByPathResponse.builder().parameters(params1).build(); + GetParametersByPathResponse response2 = GetParametersByPathResponse.builder().parameters(params2).build(); + CountDownLatch latch = new CountDownLatch(2); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())) + .thenReturn(response1, response2); + + // WHEN + Thread thread1 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.recursive().getMultiple("/path1"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + Thread thread2 = new Thread(() -> { + try { + latch.countDown(); + latch.await(); + provider.getMultiple("/path2"); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }); + + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + + // THEN + List requests = paramByPathCaptor.getAllValues(); + assertThat(requests).hasSize(2); + boolean hasRecursiveRequest = requests.stream().anyMatch(GetParametersByPathRequest::recursive); + boolean hasNonRecursiveRequest = requests.stream().anyMatch(r -> !r.recursive()); + assertThat(hasRecursiveRequest).isTrue(); + assertThat(hasNonRecursiveRequest).isTrue(); } private void initMock(String expectedValue) { From ebbc90344ce5eb603cb8f0e5bf27c6e467291b04 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Wed, 12 Nov 2025 17:20:33 +0100 Subject: [PATCH 4/9] Make all tests package-private. --- .../appconfig/AppConfigParamAspectTest.java | 4 +- .../dynamodb/DynamoDbParamAspectTest.java | 4 +- .../dynamodb/DynamoDbProviderE2ETest.java | 6 +- .../dynamodb/DynamoDbProviderTest.java | 24 ++++---- .../secrets/SecretsParamAspectTest.java | 4 +- .../secrets/SecretsProviderTest.java | 14 ++--- .../parameters/ssm/SSMParamAspectTest.java | 4 +- .../parameters/ssm/SSMProviderTest.java | 22 +++---- .../parameters/BaseProviderTest.java | 60 +++++++++---------- .../ParamProvidersIntegrationTest.java | 8 +-- .../parameters/cache/CacheManagerTest.java | 20 +++---- .../parameters/cache/DataStoreTest.java | 14 ++--- .../transform/Base64TransformerTest.java | 6 +- .../transform/JsonTransformerTest.java | 8 +-- .../transform/TransformationManagerTest.java | 28 ++++----- 15 files changed, 113 insertions(+), 113 deletions(-) diff --git a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java index df3191632..a32cc20a5 100644 --- a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java @@ -22,10 +22,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class AppConfigParamAspectTest { +class AppConfigParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked AppConfigProvider String environment = "myEnvironment"; String appName = "myApp"; diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java index 07e93a7c1..4294eca48 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class DynamoDbParamAspectTest { +class DynamoDbParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked DynamoDbProvider String tableName = "my-test-tablename"; String key = "myKey"; diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java index 2695938d8..a2e4a60fe 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java @@ -36,7 +36,7 @@ * will move this across. */ @Disabled -public class DynamoDbProviderE2ETest { +class DynamoDbProviderE2ETest { final String ParamsTestTable = "ddb-params-test"; final String MultiparamsTestTable = "ddb-multiparams-test"; @@ -52,7 +52,7 @@ public DynamoDbProviderE2ETest() { } @Test - public void TestGetValue() { + void TestGetValue() { // Arrange HashMap testItem = new HashMap(); @@ -72,7 +72,7 @@ public void TestGetValue() { } @Test - public void TestGetValues() { + void TestGetValues() { // Arrange HashMap testItem = new HashMap(); diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java index 68d48b01c..569d08efd 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java @@ -45,7 +45,7 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @ExtendWith(MockitoExtension.class) -public class DynamoDbProviderTest { +class DynamoDbProviderTest { private final String tableName = "ddb-test-table"; @@ -64,14 +64,14 @@ public class DynamoDbProviderTest { private DynamoDbProvider provider; @BeforeEach - public void init() { + void init() { openMocks(this); CacheManager cacheManager = new CacheManager(); provider = new DynamoDbProvider(cacheManager, transformationManager, client, tableName); } @Test - public void getValue() { + void getValue() { // Arrange String key = "Key1"; @@ -94,7 +94,7 @@ public void getValue() { } @Test - public void getValueWithNullResultsReturnsNull() { + void getValueWithNullResultsReturnsNull() { // Arrange Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() .item(null) @@ -108,7 +108,7 @@ public void getValueWithNullResultsReturnsNull() { } @Test - public void getValueWithoutResultsReturnsNull() { + void getValueWithoutResultsReturnsNull() { // Arrange Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() .item(new HashMap<>()) @@ -122,7 +122,7 @@ public void getValueWithoutResultsReturnsNull() { } @Test - public void getValueWithMalformedRowThrows() { + void getValueWithMalformedRowThrows() { // Arrange String key = "Key1"; HashMap responseData = new HashMap(); @@ -138,7 +138,7 @@ public void getValueWithMalformedRowThrows() { } @Test - public void getValues() { + void getValues() { // Arrange String key = "Key1"; @@ -172,7 +172,7 @@ public void getValues() { } @Test - public void getValuesWithoutResultsReturnsNull() { + void getValuesWithoutResultsReturnsNull() { // Arrange Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn( QueryResponse.builder().items().build()); @@ -185,7 +185,7 @@ public void getValuesWithoutResultsReturnsNull() { } @Test - public void getMultipleValuesMissingSortKey_throwsException() { + void getMultipleValuesMissingSortKey_throwsException() { // Arrange String key = "Key1"; HashMap item = new HashMap(); @@ -204,7 +204,7 @@ public void getMultipleValuesMissingSortKey_throwsException() { } @Test - public void getValuesWithMalformedRowThrows() { + void getValuesWithMalformedRowThrows() { // Arrange String key = "Key1"; HashMap item1 = new HashMap(); @@ -224,7 +224,7 @@ public void getValuesWithMalformedRowThrows() { } @Test - public void testDynamoDBBuilderMissingTable_throwsException() { + void testDynamoDBBuilderMissingTable_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> DynamoDbProvider.builder() @@ -233,7 +233,7 @@ public void testDynamoDBBuilderMissingTable_throwsException() { } @Test - public void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { + void testDynamoDBBuilder_withoutParameter_shouldHaveDefaultTransformationManager() { // Act DynamoDbProvider dynamoDbProvider = DynamoDbProvider.builder().withTable("test-table") diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java index 7aa0f0872..5523cbb0e 100644 --- a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class SecretsParamAspectTest { +class SecretsParamAspectTest { @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { // Setup our aspect to return a mocked SecretsProvider String key = "myKey"; String value = "mySecretValue"; diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java index d0b32874a..a6fbe1c8e 100644 --- a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsProviderTest.java @@ -40,7 +40,7 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; @ExtendWith(MockitoExtension.class) -public class SecretsProviderTest { +class SecretsProviderTest { @Mock SecretsManagerClient client; @@ -56,13 +56,13 @@ public class SecretsProviderTest { SecretsProvider provider; @BeforeEach - public void init() { + void init() { cacheManager = new CacheManager(); provider = new SecretsProvider(cacheManager, transformationManager, client); } @Test - public void getValue() { + void getValue() { String key = "Key1"; String expectedValue = "Value1"; GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(expectedValue).build(); @@ -76,7 +76,7 @@ public void getValue() { } @Test - public void getValueBase64() { + void getValueBase64() { String key = "Key2"; String expectedValue = "Value2"; byte[] valueb64 = Base64.getEncoder().encode(expectedValue.getBytes()); @@ -91,14 +91,14 @@ public void getValueBase64() { } @Test - public void getMultipleValuesThrowsException() { + void getMultipleValuesThrowsException() { // Act & Assert assertThatRuntimeException().isThrownBy(() -> provider.getMultipleValues("path")) .withMessage("Impossible to get multiple values from AWS Secrets Manager"); } @Test - public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { + void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { // Act SecretsProvider secretsProvider = SecretsProvider.builder() .build(); @@ -109,7 +109,7 @@ public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() } @Test - public void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + void testGetSecretsProvider_withoutParameter_shouldHaveDefaultTransformationManager() { // Act SecretsProvider secretsProvider = SecretsProvider.builder() .build(); diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java index e56d20ffa..abfc1d7fa 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java @@ -21,13 +21,13 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -public class SSMParamAspectTest { +class SSMParamAspectTest { // This class tests the SSM Param aspect in the same fashion // as the tests for the aspects for the other providers. @Test - public void parameterInjectedByProvider() throws Exception { + void parameterInjectedByProvider() throws Exception { String key = "myKey"; String value = "mySecretValue"; diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index 5777275f7..d2270b7ec 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -44,7 +44,7 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -public class SSMProviderTest { +class SSMProviderTest { @Mock SsmClient client; @@ -63,14 +63,14 @@ public class SSMProviderTest { SSMProvider provider; @BeforeEach - public void init() { + void init() { MockitoAnnotations.openMocks(this); cacheManager = new CacheManager(); provider = new SSMProvider(cacheManager, null, client); } @Test - public void getValue() { + void getValue() { String key = "Key1"; String expectedValue = "Value1"; initMock(expectedValue); @@ -83,7 +83,7 @@ public void getValue() { } @Test - public void getValueDecrypted() { + void getValueDecrypted() { String key = "Key2"; String expectedValue = "Value2"; initMock(expectedValue); @@ -96,7 +96,7 @@ public void getValueDecrypted() { } @Test - public void getMultiple() { + void getMultiple() { List parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); @@ -119,7 +119,7 @@ public void getMultiple() { } @Test - public void getMultipleWithTrailingSlash() { + void getMultipleWithTrailingSlash() { List parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); @@ -142,7 +142,7 @@ public void getMultipleWithTrailingSlash() { } @Test - public void getMultiple_cached_shouldNotCallSSM() { + void getMultiple_cached_shouldNotCallSSM() { List parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); @@ -164,7 +164,7 @@ public void getMultiple_cached_shouldNotCallSSM() { } @Test - public void getMultipleWithNextToken() { + void getMultipleWithNextToken() { List parameters1 = new ArrayList<>(); parameters1.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters1.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); @@ -199,7 +199,7 @@ public void getMultipleWithNextToken() { } @Test - public void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationManager() { + void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationManager() { // Act SSMProvider ssmProvider = SSMProvider.builder() @@ -209,7 +209,7 @@ public void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationMana } @Test - public void withDecryption_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + void withDecryption_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { // GIVEN Parameter param1 = Parameter.builder().value("value1").build(); Parameter param2 = Parameter.builder().value("value2").build(); @@ -255,7 +255,7 @@ public void withDecryption_concurrentCalls_shouldBeThreadSafe() throws Interrupt } @Test - public void recursive_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + void recursive_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { // GIVEN List params1 = new ArrayList<>(); params1.add(Parameter.builder().name("/path1/key1").value("value1").build()); diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java index cbc8f5b30..35b77f21f 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java @@ -38,7 +38,7 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -public class BaseProviderTest { +class BaseProviderTest { Clock clock; CacheManager cacheManager; @@ -47,7 +47,7 @@ public class BaseProviderTest { boolean getFromStore = false; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); cacheManager = new CacheManager(); transformationManager = new TransformationManager(); @@ -55,7 +55,7 @@ public void setup() { } @Test - public void get_notCached_shouldGetValue() { + void get_notCached_shouldGetValue() { String foo = provider.get("toto"); assertThat(foo).isEqualTo("valueFromStore"); @@ -63,7 +63,7 @@ public void get_notCached_shouldGetValue() { } @Test - public void get_cached_shouldGetFromCache() { + void get_cached_shouldGetFromCache() { provider.get("foo"); getFromStore = false; @@ -73,7 +73,7 @@ public void get_cached_shouldGetFromCache() { } @Test - public void get_expired_shouldGetValue() { + void get_expired_shouldGetValue() { provider.get("bar"); getFromStore = false; @@ -84,7 +84,7 @@ public void get_expired_shouldGetValue() { } @Test - public void getMultiple_notCached_shouldGetValue() { + void getMultiple_notCached_shouldGetValue() { Map foo = provider.getMultiple("toto"); assertThat(foo.get("toto")).isEqualTo("valueFromStore"); @@ -92,7 +92,7 @@ public void getMultiple_notCached_shouldGetValue() { } @Test - public void getMultiple_cached_shouldGetFromCache() { + void getMultiple_cached_shouldGetFromCache() { provider.getMultiple("foo"); getFromStore = false; @@ -102,7 +102,7 @@ public void getMultiple_cached_shouldGetFromCache() { } @Test - public void getMultiple_expired_shouldGetValue() { + void getMultiple_expired_shouldGetValue() { provider.getMultiple("bar"); getFromStore = false; @@ -113,7 +113,7 @@ public void getMultiple_expired_shouldGetValue() { } @Test - public void get_customTTL_cached_shouldGetFromCache() { + void get_customTTL_cached_shouldGetFromCache() { provider.withMaxAge(12, ChronoUnit.MINUTES).get("key"); getFromStore = false; @@ -124,7 +124,7 @@ public void get_customTTL_cached_shouldGetFromCache() { } @Test - public void get_customTTL_expired_shouldGetValue() { + void get_customTTL_expired_shouldGetValue() { provider.withMaxAge(2, ChronoUnit.MINUTES).get("mykey"); getFromStore = false; @@ -135,7 +135,7 @@ public void get_customTTL_expired_shouldGetValue() { } @Test - public void get_customDefaultTTL_cached_shouldGetFromCache() { + void get_customDefaultTTL_cached_shouldGetFromCache() { provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); provider.get("foobar"); getFromStore = false; @@ -147,7 +147,7 @@ public void get_customDefaultTTL_cached_shouldGetFromCache() { } @Test - public void get_customDefaultTTL_expired_shouldGetValue() { + void get_customDefaultTTL_expired_shouldGetValue() { provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); getFromStore = false; @@ -158,7 +158,7 @@ public void get_customDefaultTTL_expired_shouldGetValue() { } @Test - public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { + void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { provider.get("foobaz"); getFromStore = false; @@ -169,7 +169,7 @@ public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { } @Test - public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { + void get_customDefaultTTLAndTTL_expired_shouldGetValue() { provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); @@ -183,7 +183,7 @@ public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { } @Test - public void get_basicTransformation_shouldTransformInString() { + void get_basicTransformation_shouldTransformInString() { provider.setValue(Base64.getEncoder().encodeToString("bar".getBytes())); String value = provider.withTransformation(Transformer.base64).get("base64"); @@ -192,7 +192,7 @@ public void get_basicTransformation_shouldTransformInString() { } @Test - public void get_complexTransformation_shouldTransformInObject() { + void get_complexTransformation_shouldTransformInObject() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); ObjectToDeserialize objectToDeserialize = provider.withTransformation(json).get("foo", @@ -205,7 +205,7 @@ public void get_complexTransformation_shouldTransformInObject() { } @Test - public void getObject_notCached_shouldGetValue() { + void getObject_notCached_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); ObjectToDeserialize foo = provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -215,7 +215,7 @@ public void getObject_notCached_shouldGetValue() { } @Test - public void getObject_cached_shouldGetFromCache() { + void getObject_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -227,7 +227,7 @@ public void getObject_cached_shouldGetFromCache() { } @Test - public void getObject_expired_shouldGetValue() { + void getObject_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withTransformation(json).get("foo", ObjectToDeserialize.class); @@ -240,7 +240,7 @@ public void getObject_expired_shouldGetValue() { } @Test - public void getObject_customTTL_cached_shouldGetFromCache() { + void getObject_customTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withMaxAge(12, ChronoUnit.MINUTES) @@ -255,7 +255,7 @@ public void getObject_customTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customTTL_expired_shouldGetValue() { + void getObject_customTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.withMaxAge(2, ChronoUnit.MINUTES) @@ -270,7 +270,7 @@ public void getObject_customTTL_expired_shouldGetValue() { } @Test - public void getObject_customDefaultTTL_cached_shouldGetFromCache() { + void getObject_customDefaultTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); @@ -286,7 +286,7 @@ public void getObject_customDefaultTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customDefaultTTL_expired_shouldGetValue() { + void getObject_customDefaultTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); @@ -302,7 +302,7 @@ public void getObject_customDefaultTTL_expired_shouldGetValue() { } @Test - public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { + void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.cacheManager.setDefaultExpirationTime(Duration.ofSeconds(5)); @@ -319,7 +319,7 @@ public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { } @Test - public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { + void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); @@ -336,7 +336,7 @@ public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { } @Test - public void get_noTransformationManager_shouldThrowException() { + void get_noTransformationManager_shouldThrowException() { provider = new BasicProvider(new CacheManager(), null); assertThatIllegalStateException() @@ -344,14 +344,14 @@ public void get_noTransformationManager_shouldThrowException() { } @Test - public void getObject_noTransformationManager_shouldThrowException() { + void getObject_noTransformationManager_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> provider.get("foo", ObjectToDeserialize.class)); } @Test - public void getTwoParams_shouldResetTTLOptionsInBetween() { + void getTwoParams_shouldResetTTLOptionsInBetween() { provider.withMaxAge(50, SECONDS).get("foo50"); provider.get("foo5"); @@ -365,7 +365,7 @@ public void getTwoParams_shouldResetTTLOptionsInBetween() { } @Test - public void getTwoParams_shouldResetTransformationOptionsInBetween() { + void getTwoParams_shouldResetTransformationOptionsInBetween() { provider.setValue(Base64.getEncoder().encodeToString("base64encoded".getBytes())); String foob64 = provider.withTransformation(base64).get("foob64"); @@ -384,7 +384,7 @@ public BasicProvider(CacheManager cacheManager, TransformationManager transforma super(cacheManager, transformationManager); } - public void setValue(String value) { + void setValue(String value) { this.value = value; } diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java index 6b3cf7641..5bc609777 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java @@ -46,7 +46,7 @@ import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; @ExtendWith(MockitoExtension.class) -public class ParamProvidersIntegrationTest { +class ParamProvidersIntegrationTest { @Mock SsmClient ssmClient; @@ -66,7 +66,7 @@ public class ParamProvidersIntegrationTest { ArgumentCaptor secretsCaptor; @Test - public void ssmProvider_get() { + void ssmProvider_get() { SSMProvider ssmProvider = SSMProvider.builder() .withClient(ssmClient) .build(); @@ -84,7 +84,7 @@ public void ssmProvider_get() { } @Test - public void ssmProvider_getMultiple() { + void ssmProvider_getMultiple() { SSMProvider ssmProvider = SSMProvider.builder() .withClient(ssmClient) .build(); @@ -111,7 +111,7 @@ public void ssmProvider_getMultiple() { } @Test - public void secretsProvider_get() { + void secretsProvider_get() { SecretsProvider secretsProvider = SecretsProvider.builder() .withClient(secretsManagerClient) .build(); diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java index a305c7945..d22572fad 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java @@ -25,20 +25,20 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class CacheManagerTest { +class CacheManagerTest { CacheManager manager; Clock clock; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); manager = new CacheManager(); } @Test - public void getIfNotExpired_notExpired_shouldReturnValue() { + void getIfNotExpired_notExpired_shouldReturnValue() { manager.putInCache("key", "value"); Optional value = manager.getIfNotExpired("key", clock.instant()); @@ -47,7 +47,7 @@ public void getIfNotExpired_notExpired_shouldReturnValue() { } @Test - public void getIfNotExpired_expired_shouldReturnNothing() { + void getIfNotExpired_expired_shouldReturnNothing() { manager.putInCache("key", "value"); Optional value = manager.getIfNotExpired("key", offset(clock, of(6, SECONDS)).instant()); @@ -56,7 +56,7 @@ public void getIfNotExpired_expired_shouldReturnNothing() { } @Test - public void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValue() { + void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValue() { manager.setExpirationTime(of(42, SECONDS)); manager.putInCache("key", "value"); @@ -66,7 +66,7 @@ public void getIfNotExpired_withCustomExpirationTime_notExpired_shouldReturnValu } @Test - public void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldReturnValue() { + void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldReturnValue() { manager.setDefaultExpirationTime(of(42, SECONDS)); manager.putInCache("key", "value"); @@ -76,7 +76,7 @@ public void getIfNotExpired_withCustomDefaultExpirationTime_notExpired_shouldRet } @Test - public void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_shouldUseExpirationTime() { + void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_shouldUseExpirationTime() { manager.setDefaultExpirationTime(of(42, SECONDS)); manager.setExpirationTime(of(2, SECONDS)); manager.putInCache("key", "value"); @@ -87,7 +87,7 @@ public void getIfNotExpired_customDefaultExpirationTime_customExpirationTime_sho } @Test - public void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() { + void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() { manager.setDefaultExpirationTime(of(42, SECONDS)); manager.setExpirationTime(of(2, SECONDS)); manager.putInCache("key", "value"); @@ -102,7 +102,7 @@ public void getIfNotExpired_resetExpirationTime_shouldUseDefaultExpirationTime() } @Test - public void putInCache_sharedCache_shouldBeAccessibleAcrossThreads() throws InterruptedException { + void putInCache_sharedCache_shouldBeAccessibleAcrossThreads() throws InterruptedException { // GIVEN Thread thread1 = new Thread(() -> { manager.setExpirationTime(of(60, SECONDS)); @@ -130,7 +130,7 @@ public void putInCache_sharedCache_shouldBeAccessibleAcrossThreads() throws Inte } @Test - public void putInCache_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + void putInCache_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { // GIVEN int threadCount = 10; Thread[] threads = new Thread[threadCount]; diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java index e86ded9be..0de31e63d 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java @@ -24,35 +24,35 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class DataStoreTest { +class DataStoreTest { Clock clock; DataStore store; @BeforeEach - public void setup() { + void setup() { clock = Clock.systemDefaultZone(); store = new DataStore(); } @Test - public void put_shouldInsertInStore() { + void put_shouldInsertInStore() { store.put("key", "value", Instant.now()); assertThat(store.get("key")).isEqualTo("value"); } @Test - public void get_invalidKey_shouldReturnNull() { + void get_invalidKey_shouldReturnNull() { assertThat(store.get("key")).isNull(); } @Test - public void hasExpired_invalidKey_shouldReturnTrue() { + void hasExpired_invalidKey_shouldReturnTrue() { assertThat(store.hasExpired("key", clock.instant())).isTrue(); } @Test - public void hasExpired_notExpired_shouldReturnFalse() { + void hasExpired_notExpired_shouldReturnFalse() { Instant now = Instant.now(); store.put("key", "value", now.plus(10, SECONDS)); @@ -61,7 +61,7 @@ public void hasExpired_notExpired_shouldReturnFalse() { } @Test - public void hasExpired_expired_shouldReturnTrueAndRemoveElement() { + void hasExpired_expired_shouldReturnTrueAndRemoveElement() { Instant now = Instant.now(); store.put("key", "value", now.plus(10, SECONDS)); diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java index ea713b552..8dbddacf6 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.parameters.exception.TransformationException; -public class Base64TransformerTest { +class Base64TransformerTest { @Test - public void transform_base64_shouldTransformInString() { + void transform_base64_shouldTransformInString() { Base64Transformer transformer = new Base64Transformer(); String s = transformer.applyTransformation(Base64.getEncoder().encodeToString("foobar".getBytes())); @@ -33,7 +33,7 @@ public void transform_base64_shouldTransformInString() { } @Test - public void transform_base64WrongFormat_shouldThrowException() { + void transform_base64WrongFormat_shouldThrowException() { Base64Transformer transformer = new Base64Transformer(); assertThatExceptionOfType(TransformationException.class) diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java index 5cb980cc7..b8389cb92 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java @@ -22,10 +22,10 @@ import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.parameters.exception.TransformationException; -public class JsonTransformerTest { +class JsonTransformerTest { @Test - public void transform_json_shouldTransformInObject() throws TransformationException { + void transform_json_shouldTransformInObject() throws TransformationException { JsonTransformer transformation = new JsonTransformer<>(); ObjectToDeserialize objectToDeserialize = @@ -38,7 +38,7 @@ public void transform_json_shouldTransformInObject() throws TransformationExcept } @Test - public void transform_json_shouldTransformInHashMap() throws TransformationException { + void transform_json_shouldTransformInHashMap() throws TransformationException { JsonTransformer transformation = new JsonTransformer<>(); Map map = @@ -50,7 +50,7 @@ public void transform_json_shouldTransformInHashMap() throws TransformationExcep } @Test - public void transform_badJson_shouldThrowException() { + void transform_badJson_shouldThrowException() { JsonTransformer transformation = new JsonTransformer<>(); assertThatExceptionOfType(TransformationException.class) diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java index 14bdfccdf..5d7edec60 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java @@ -28,37 +28,37 @@ import software.amazon.lambda.powertools.parameters.exception.TransformationException; -public class TransformationManagerTest { +class TransformationManagerTest { TransformationManager manager; Class basicTransformer = BasicTransformer.class; @BeforeEach - public void setup() { + void setup() { manager = new TransformationManager(); } @Test - public void setTransformer_shouldTransform() { + void setTransformer_shouldTransform() { manager.setTransformer(json); assertThat(manager.shouldTransform()).isTrue(); } @Test - public void notSetTransformer_shouldNotTransform() { + void notSetTransformer_shouldNotTransform() { assertThat(manager.shouldTransform()).isFalse(); } @Test - public void performBasicTransformation_noTransformer_shouldThrowException() { + void performBasicTransformation_noTransformer_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> manager.performBasicTransformation("value")); } @Test - public void performBasicTransformation_notBasicTransformer_shouldThrowException() { + void performBasicTransformation_notBasicTransformer_shouldThrowException() { manager.setTransformer(json); assertThatIllegalStateException() @@ -66,7 +66,7 @@ public void performBasicTransformation_notBasicTransformer_shouldThrowException( } @Test - public void performBasicTransformation_abstractTransformer_throwsTransformationException() { + void performBasicTransformation_abstractTransformer_throwsTransformationException() { manager.setTransformer(basicTransformer); assertThatExceptionOfType(TransformationException.class) @@ -74,7 +74,7 @@ public void performBasicTransformation_abstractTransformer_throwsTransformationE } @Test - public void performBasicTransformation_shouldPerformTransformation() { + void performBasicTransformation_shouldPerformTransformation() { manager.setTransformer(base64); String expectedValue = "bar"; @@ -84,13 +84,13 @@ public void performBasicTransformation_shouldPerformTransformation() { } @Test - public void performComplexTransformation_noTransformer_shouldThrowException() { + void performComplexTransformation_noTransformer_shouldThrowException() { assertThatIllegalStateException() .isThrownBy(() -> manager.performComplexTransformation("value", ObjectToDeserialize.class)); } @Test - public void performComplexTransformation_shouldPerformTransformation() { + void performComplexTransformation_shouldPerformTransformation() { manager.setTransformer(json); ObjectToDeserialize object = manager.performComplexTransformation( @@ -101,7 +101,7 @@ public void performComplexTransformation_shouldPerformTransformation() { } @Test - public void performComplexTransformation_throwsTransformationException() { + void performComplexTransformation_throwsTransformationException() { manager.setTransformer(basicTransformer); assertThatExceptionOfType(TransformationException.class) @@ -109,7 +109,7 @@ public void performComplexTransformation_throwsTransformationException() { } @Test - public void unsetTransformer_shouldCleanUpThreadLocal() { + void unsetTransformer_shouldCleanUpThreadLocal() { // GIVEN manager.setTransformer(json); assertThat(manager.shouldTransform()).isTrue(); @@ -122,7 +122,7 @@ public void unsetTransformer_shouldCleanUpThreadLocal() { } @Test - public void setTransformer_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { + void setTransformer_concurrentCalls_shouldBeThreadSafe() throws InterruptedException { // GIVEN boolean[] success = new boolean[2]; CountDownLatch latch = new CountDownLatch(2); @@ -171,7 +171,7 @@ public void setTransformer_concurrentCalls_shouldBeThreadSafe() throws Interrupt } @Test - public void unsetTransformer_concurrentCalls_shouldNotAffectOtherThreads() throws InterruptedException { + void unsetTransformer_concurrentCalls_shouldNotAffectOtherThreads() throws InterruptedException { // GIVEN boolean[] success = new boolean[2]; CountDownLatch latch = new CountDownLatch(2); From e6397d4ddda7301b269d4c3be28d7516c4d74492 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Thu, 13 Nov 2025 15:31:38 +0100 Subject: [PATCH 5/9] Fix sonar finding AZp41z3q7Kei0U644EOS. --- .../powertools/parameters/ssm/SSMProviderTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index d2270b7ec..b9010f314 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -247,11 +247,10 @@ void withDecryption_concurrentCalls_shouldBeThreadSafe() throws InterruptedExcep // THEN List requests = paramCaptor.getAllValues(); - assertThat(requests).hasSize(2); - boolean hasDecryptedRequest = requests.stream().anyMatch(GetParameterRequest::withDecryption); - boolean hasNonDecryptedRequest = requests.stream().anyMatch(r -> !r.withDecryption()); - assertThat(hasDecryptedRequest).isTrue(); - assertThat(hasNonDecryptedRequest).isTrue(); + assertThat(requests) + .hasSize(2) + .anyMatch(GetParameterRequest::withDecryption) + .anyMatch(r -> !r.withDecryption()); } @Test From 394c1ef62921f2901545d99a8804f70ece12c737 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Thu, 13 Nov 2025 15:48:33 +0100 Subject: [PATCH 6/9] Fix sonar finding AZp41z3q7Kei0U644EOS for real now. --- .../parameters/ssm/SSMProviderTest.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index b9010f314..fb475a737 100644 --- a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -188,11 +188,13 @@ void getMultipleWithNextToken() { GetParametersByPathRequest request1 = requestParams.get(0); GetParametersByPathRequest request2 = requestParams.get(1); - assertThat(asList(request1, request2)).allSatisfy(req -> { - assertThat(req.path()).isEqualTo("/prod/app1"); - assertThat(req.withDecryption()).isFalse(); - assertThat(req.recursive()).isFalse(); - }); + assertThat(asList(request1, request2)) + .isNotEmpty() + .allSatisfy(req -> { + assertThat(req.path()).isEqualTo("/prod/app1"); + assertThat(req.withDecryption()).isFalse(); + assertThat(req.recursive()).isFalse(); + }); assertThat(request1.nextToken()).isNull(); assertThat(request2.nextToken()).isEqualTo("123abc"); @@ -294,11 +296,10 @@ void recursive_concurrentCalls_shouldBeThreadSafe() throws InterruptedException // THEN List requests = paramByPathCaptor.getAllValues(); - assertThat(requests).hasSize(2); - boolean hasRecursiveRequest = requests.stream().anyMatch(GetParametersByPathRequest::recursive); - boolean hasNonRecursiveRequest = requests.stream().anyMatch(r -> !r.recursive()); - assertThat(hasRecursiveRequest).isTrue(); - assertThat(hasNonRecursiveRequest).isTrue(); + assertThat(requests) + .hasSize(2) + .anyMatch(GetParametersByPathRequest::recursive) + .anyMatch(r -> !r.recursive()); } private void initMock(String expectedValue) { From 58f39ed6cab64d615d84078b254815a5d34eb8ec Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Thu, 13 Nov 2025 15:58:54 +0100 Subject: [PATCH 7/9] Fix PMD findings. --- .../dynamodb/DynamoDbProviderE2ETest.java | 20 +++++++++---------- .../dynamodb/DynamoDbProviderTest.java | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java index a2e4a60fe..af2617edf 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderE2ETest.java @@ -38,8 +38,8 @@ @Disabled class DynamoDbProviderE2ETest { - final String ParamsTestTable = "ddb-params-test"; - final String MultiparamsTestTable = "ddb-multiparams-test"; + private static final String PARAMS_TEST_TABLE = "ddb-params-test"; + private static final String MULTI_PARAMS_TEST_TABLE = "ddb-multiparams-test"; private final DynamoDbClient ddbClient; public DynamoDbProviderE2ETest() { @@ -55,16 +55,16 @@ public DynamoDbProviderE2ETest() { void TestGetValue() { // Arrange - HashMap testItem = new HashMap(); + Map testItem = new HashMap<>(); testItem.put("id", AttributeValue.fromS("test_param")); testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(ParamsTestTable) + .tableName(PARAMS_TEST_TABLE) .item(testItem) .build()); // Act - DynamoDbProvider provider = makeProvider(ParamsTestTable); + DynamoDbProvider provider = makeProvider(PARAMS_TEST_TABLE); String value = provider.getValue("test_param"); // Assert @@ -75,26 +75,26 @@ void TestGetValue() { void TestGetValues() { // Arrange - HashMap testItem = new HashMap(); + Map testItem = new HashMap<>(); testItem.put("id", AttributeValue.fromS("test_param")); testItem.put("sk", AttributeValue.fromS("test_param_part_1")); testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(MultiparamsTestTable) + .tableName(MULTI_PARAMS_TEST_TABLE) .item(testItem) .build()); - HashMap testItem2 = new HashMap(); + Map testItem2 = new HashMap<>(); testItem2.put("id", AttributeValue.fromS("test_param")); testItem2.put("sk", AttributeValue.fromS("test_param_part_2")); testItem2.put("value", AttributeValue.fromS("the_value_is_still_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(MultiparamsTestTable) + .tableName(MULTI_PARAMS_TEST_TABLE) .item(testItem2) .build()); // Act - DynamoDbProvider provider = makeProvider(MultiparamsTestTable); + DynamoDbProvider provider = makeProvider(MULTI_PARAMS_TEST_TABLE); Map values = provider.getMultipleValues("test_param"); // Assert diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java index 569d08efd..794f09412 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java @@ -47,7 +47,7 @@ @ExtendWith(MockitoExtension.class) class DynamoDbProviderTest { - private final String tableName = "ddb-test-table"; + private static final String TABLE_NAME = "ddb-test-table"; @Mock DynamoDbClient client; @@ -67,7 +67,7 @@ class DynamoDbProviderTest { void init() { openMocks(this); CacheManager cacheManager = new CacheManager(); - provider = new DynamoDbProvider(cacheManager, transformationManager, client, tableName); + provider = new DynamoDbProvider(cacheManager, transformationManager, client, TABLE_NAME); } @Test @@ -76,7 +76,7 @@ void getValue() { // Arrange String key = "Key1"; String expectedValue = "Value1"; - HashMap responseData = new HashMap(); + Map responseData = new HashMap<>(); responseData.put("id", AttributeValue.fromS(key)); responseData.put("value", AttributeValue.fromS(expectedValue)); GetItemResponse response = GetItemResponse.builder() @@ -89,7 +89,7 @@ void getValue() { // Assert assertThat(value).isEqualTo(expectedValue); - assertThat(getItemValueCaptor.getValue().tableName()).isEqualTo(tableName); + assertThat(getItemValueCaptor.getValue().tableName()).isEqualTo(TABLE_NAME); assertThat(getItemValueCaptor.getValue().key().get("id").s()).isEqualTo(key); } @@ -166,7 +166,7 @@ void getValues() { assertThat(values.size()).isEqualTo(2); assertThat(values.get(subkey1)).isEqualTo(val1); assertThat(values.get(subkey2)).isEqualTo(val2); - assertThat(queryRequestCaptor.getValue().tableName()).isEqualTo(tableName); + assertThat(queryRequestCaptor.getValue().tableName()).isEqualTo(TABLE_NAME); assertThat(queryRequestCaptor.getValue().keyConditionExpression()).isEqualTo("id = :v_id"); assertThat(queryRequestCaptor.getValue().expressionAttributeValues().get(":v_id").s()).isEqualTo(key); } From 9b2c7b12d0aaa92e72a3a67507e42d9ed24ada69 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Thu, 13 Nov 2025 16:06:43 +0100 Subject: [PATCH 8/9] Fix PMD findings. --- .../parameters/dynamodb/DynamoDbProviderTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java index 794f09412..64f29db79 100644 --- a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbProviderTest.java @@ -125,7 +125,7 @@ void getValueWithoutResultsReturnsNull() { void getValueWithMalformedRowThrows() { // Arrange String key = "Key1"; - HashMap responseData = new HashMap(); + Map responseData = new HashMap<>(); responseData.put("id", AttributeValue.fromS(key)); responseData.put("not-value", AttributeValue.fromS("something")); Mockito.when(client.getItem(getItemValueCaptor.capture())).thenReturn(GetItemResponse.builder() @@ -146,11 +146,11 @@ void getValues() { String val1 = "Val1"; String subkey2 = "Subkey2"; String val2 = "Val2"; - HashMap item1 = new HashMap(); + Map item1 = new HashMap<>(); item1.put("id", AttributeValue.fromS(key)); item1.put("sk", AttributeValue.fromS(subkey1)); item1.put("value", AttributeValue.fromS(val1)); - HashMap item2 = new HashMap(); + Map item2 = new HashMap<>(); item2.put("id", AttributeValue.fromS(key)); item2.put("sk", AttributeValue.fromS(subkey2)); item2.put("value", AttributeValue.fromS(val2)); @@ -188,7 +188,7 @@ void getValuesWithoutResultsReturnsNull() { void getMultipleValuesMissingSortKey_throwsException() { // Arrange String key = "Key1"; - HashMap item = new HashMap(); + Map item = new HashMap<>(); item.put("id", AttributeValue.fromS(key)); item.put("value", AttributeValue.fromS("somevalue")); QueryResponse response = QueryResponse.builder() @@ -207,7 +207,7 @@ void getMultipleValuesMissingSortKey_throwsException() { void getValuesWithMalformedRowThrows() { // Arrange String key = "Key1"; - HashMap item1 = new HashMap(); + Map item1 = new HashMap<>(); item1.put("id", AttributeValue.fromS(key)); item1.put("sk", AttributeValue.fromS("some-subkey")); item1.put("not-value", AttributeValue.fromS("somevalue")); From b35c6ef7f4b1938b82d169469805f9fc1bc915b8 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Thu, 13 Nov 2025 16:12:25 +0100 Subject: [PATCH 9/9] Fix PMD findings. --- .../lambda/powertools/parameters/BaseProviderTest.java | 2 +- .../parameters/transform/JsonTransformerTest.java | 2 +- .../parameters/transform/TransformationManagerTest.java | 4 ++-- .../lambda/powertools/parameters/cache/DataStore.java | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java index 35b77f21f..dd31ce016 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java @@ -199,7 +199,7 @@ void get_complexTransformation_shouldTransformInObject() { ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( - o -> o.getFoo().equals("Foo") + o -> "Foo".equals(o.getFoo()) && o.getBar() == 42 && o.getBaz() == 123456789); } diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java index b8389cb92..48cebb6b0 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java @@ -32,7 +32,7 @@ void transform_json_shouldTransformInObject() throws TransformationException { transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( - o -> o.getFoo().equals("Foo") + o -> "Foo".equals(o.getFoo()) && o.getBar() == 42 && o.getBaz() == 123456789); } diff --git a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java index 5d7edec60..fad6f5391 100644 --- a/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java @@ -136,7 +136,7 @@ void setTransformer_concurrentCalls_shouldBeThreadSafe() throws InterruptedExcep String result = manager.performComplexTransformation( "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", ObjectToDeserialize.class).getFoo(); - success[0] = result.equals("Foo"); + success[0] = "Foo".equals(result); } catch (Exception e) { e.printStackTrace(); success[0] = false; @@ -151,7 +151,7 @@ void setTransformer_concurrentCalls_shouldBeThreadSafe() throws InterruptedExcep // Thread 2 expects base64 transformer String result = manager.performBasicTransformation( Base64.getEncoder().encodeToString("bar".getBytes())); - success[1] = result.equals("bar"); + success[1] = "bar".equals(result); } catch (Exception e) { e.printStackTrace(); success[1] = false; diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java index 6ce8a3e82..4b6350cb5 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java @@ -15,6 +15,7 @@ package software.amazon.lambda.powertools.parameters.cache; import java.time.Instant; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** @@ -22,7 +23,7 @@ */ public class DataStore { - private final ConcurrentHashMap store; + private final Map store; public DataStore() { this.store = new ConcurrentHashMap<>(); @@ -32,8 +33,8 @@ public void put(String key, Object value, Instant time) { store.put(key, new ValueNode(value, time)); } - public void remove(String Key) { - store.remove(Key); + public void remove(String key) { + store.remove(key); } public Object get(String key) {