Skip to content

Commit 957b88f

Browse files
committed
Make Lazy implement Closeable so that it can be closed to close its initializer and value.
Fixes #2149.
1 parent 8f06c8a commit 957b88f

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Fixed a resource leak that could occur when closing the default credentials provider (or a client using the default credentials provider), when `closeable` credentials like STS or SSO were in use. Fixes [#2149](https://github.com/aws/aws-sdk-java-v2/issues/2149)."
6+
}

core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProviderTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.junit.Test;
2121
import org.mockito.Mockito;
2222
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
23+
import software.amazon.awssdk.utils.SdkAutoCloseable;
2324

2425
public class LazyAwsCredentialsProviderTest {
2526
@SuppressWarnings("unchecked")
@@ -48,4 +49,30 @@ public void resolveCredentialsInvokesSupplierExactlyOnce() {
4849
Mockito.verify(credentialsConstructor, Mockito.times(1)).get();
4950
Mockito.verify(credentials, Mockito.times(2)).resolveCredentials();
5051
}
52+
53+
@Test
54+
public void delegatesClosesInitializerAndValue() {
55+
CloseableSupplier initializer = Mockito.mock(CloseableSupplier.class);
56+
CloseableCredentialsProvider value = Mockito.mock(CloseableCredentialsProvider.class);
57+
58+
Mockito.when(initializer.get()).thenReturn(value);
59+
60+
LazyAwsCredentialsProvider.create(initializer).close();
61+
62+
Mockito.verify(initializer).close();
63+
Mockito.verify(value).close();
64+
}
65+
66+
@Test
67+
public void delegatesClosesInitializerEvenIfGetFails() {
68+
CloseableSupplier initializer = Mockito.mock(CloseableSupplier.class);
69+
Mockito.when(initializer.get()).thenThrow(new RuntimeException());
70+
71+
LazyAwsCredentialsProvider.create(initializer).close();
72+
73+
Mockito.verify(initializer).close();
74+
}
75+
76+
private interface CloseableSupplier extends Supplier<AwsCredentialsProvider>, SdkAutoCloseable {}
77+
private interface CloseableCredentialsProvider extends SdkAutoCloseable, AwsCredentialsProvider {}
5178
}

utils/src/main/java/software/amazon/awssdk/utils/Lazy.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020

2121
/**
2222
* A class that lazily constructs a value the first time {@link #getValue()} is invoked.
23+
*
24+
* This should be {@link #close()}d if the initializer returns value that needs to be {@link AutoCloseable#close()}d.
2325
*/
2426
@SdkPublicApi
25-
public class Lazy<T> {
27+
public class Lazy<T> implements SdkAutoCloseable {
2628
private final Supplier<T> initializer;
2729

2830
private volatile T value;
@@ -53,4 +55,17 @@ public String toString() {
5355
.add("value", value == null ? "Uninitialized" : value)
5456
.build();
5557
}
58+
59+
@Override
60+
public void close() {
61+
try {
62+
// Make sure the value has been initialized before we attempt to close it
63+
getValue();
64+
} catch (RuntimeException e) {
65+
// Failed to initialize the value.
66+
}
67+
68+
IoUtils.closeIfCloseable(initializer, null);
69+
IoUtils.closeIfCloseable(value, null);
70+
}
5671
}

0 commit comments

Comments
 (0)