Skip to content

Commit 26dba31

Browse files
authored
Fix #1402 Migrating AWS S3 SDK to v2 (#1403)
1 parent 5b0212f commit 26dba31

File tree

11 files changed

+236
-207
lines changed

11 files changed

+236
-207
lines changed

bolt-jakarta-servlet/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
<scope>test</scope>
6262
</dependency>
6363
<dependency>
64-
<groupId>com.amazonaws</groupId>
65-
<artifactId>aws-java-sdk-s3</artifactId>
64+
<groupId>software.amazon.awssdk</groupId>
65+
<artifactId>s3</artifactId>
6666
<version>${aws.s3.version}</version>
6767
<scope>test</scope>
6868
</dependency>

bolt-kotlin-examples/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
</dependency>
4242

4343
<dependency>
44-
<groupId>com.amazonaws</groupId>
45-
<artifactId>aws-java-sdk-s3</artifactId>
44+
<groupId>software.amazon.awssdk</groupId>
45+
<artifactId>s3</artifactId>
4646
<version>${aws.s3.version}</version>
4747
</dependency>
4848

bolt-servlet/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@
6666
<scope>test</scope>
6767
</dependency>
6868
<dependency>
69-
<groupId>com.amazonaws</groupId>
70-
<artifactId>aws-java-sdk-s3</artifactId>
69+
<groupId>software.amazon.awssdk</groupId>
70+
<artifactId>s3</artifactId>
7171
<version>${aws.s3.version}</version>
7272
<scope>test</scope>
7373
</dependency>

bolt/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
</dependency>
4242

4343
<dependency>
44-
<groupId>com.amazonaws</groupId>
45-
<artifactId>aws-java-sdk-s3</artifactId>
44+
<groupId>software.amazon.awssdk</groupId>
45+
<artifactId>s3</artifactId>
4646
<version>${aws.s3.version}</version>
4747
<scope>provided</scope>
4848
</dependency>

bolt/src/main/java/com/slack/api/bolt/service/builtin/AmazonS3InstallationService.java

Lines changed: 101 additions & 68 deletions
Large diffs are not rendered by default.

bolt/src/main/java/com/slack/api/bolt/service/builtin/AmazonS3OAuthStateService.java

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package com.slack.api.bolt.service.builtin;
22

3-
import com.amazonaws.auth.AWSCredentials;
4-
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
5-
import com.amazonaws.services.s3.AmazonS3;
6-
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
7-
import com.amazonaws.services.s3.model.AmazonS3Exception;
8-
import com.amazonaws.services.s3.model.PutObjectResult;
9-
import com.amazonaws.services.s3.model.S3Object;
10-
import com.amazonaws.util.IOUtils;
113
import com.slack.api.bolt.Initializer;
124
import com.slack.api.bolt.service.OAuthStateService;
13-
import com.slack.api.bolt.util.JsonOps;
145
import lombok.extern.slf4j.Slf4j;
6+
import software.amazon.awssdk.auth.credentials.AwsCredentials;
7+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
8+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
9+
import software.amazon.awssdk.core.ResponseBytes;
10+
import software.amazon.awssdk.core.sync.RequestBody;
11+
import software.amazon.awssdk.core.sync.ResponseTransformer;
12+
import software.amazon.awssdk.services.s3.S3Client;
13+
import software.amazon.awssdk.services.s3.model.*;
1514

16-
import java.io.IOException;
15+
import java.nio.charset.StandardCharsets;
1716

1817
/**
1918
* OAuthStateService implementation using Amazon S3.
@@ -24,6 +23,7 @@
2423
public class AmazonS3OAuthStateService implements OAuthStateService {
2524

2625
private final String bucketName;
26+
private AwsCredentialsProvider credentialsProvider;
2727

2828
public AmazonS3OAuthStateService(String bucketName) {
2929
this.bucketName = bucketName;
@@ -33,74 +33,92 @@ public AmazonS3OAuthStateService(String bucketName) {
3333
public Initializer initializer() {
3434
return (app) -> {
3535
// The first access to S3 tends to be slow on AWS Lambda.
36-
AWSCredentials credentials = getCredentials();
37-
if (credentials == null || credentials.getAWSAccessKeyId() == null) {
36+
this.credentialsProvider = DefaultCredentialsProvider.create();
37+
AwsCredentials credentials = createCredentials(credentialsProvider);
38+
if (credentials == null || credentials.accessKeyId() == null) {
3839
throw new IllegalStateException("AWS credentials not found");
3940
}
4041
if (log.isDebugEnabled()) {
41-
log.debug("AWS credentials loaded (access key id: {})", credentials.getAWSAccessKeyId());
42+
log.debug("AWS credentials loaded (access key id: {})", credentials.accessKeyId());
43+
}
44+
boolean bucketExists = false;
45+
Exception ex = null;
46+
try (S3Client s3 = createS3Client()) {
47+
bucketExists = s3.headBucket(HeadBucketRequest.builder().bucket(bucketName).build()) != null;
48+
} catch (Exception e) { // NoSuchBucketException etc.
49+
ex = e;
4250
}
43-
boolean bucketExists = createS3Client().doesBucketExistV2(bucketName);
4451
if (!bucketExists) {
45-
throw new IllegalStateException("Failed to access the Amazon S3 bucket (name: " + bucketName + ")");
52+
String error = ex != null ? ex.getClass().getName() + ":" + ex.getMessage() : "-";
53+
String message = "Failed to access the Amazon S3 bucket (name: " + bucketName + ", error: " + error + ")";
54+
throw new IllegalStateException(message);
4655
}
4756
};
4857
}
4958

5059
@Override
5160
public void addNewStateToDatastore(String state) throws Exception {
52-
AmazonS3 s3 = this.createS3Client();
53-
String value = "" + (System.currentTimeMillis() + getExpirationInSeconds() * 1000);
54-
PutObjectResult putObjectResult = s3.putObject(bucketName, getKey(state), value);
61+
PutObjectResponse putObjectResult;
62+
try (S3Client s3 = this.createS3Client()) {
63+
String value = "" + (System.currentTimeMillis() + getExpirationInSeconds() * 1000);
64+
putObjectResult = s3.putObject(
65+
PutObjectRequest.builder().bucket(bucketName).key(getKey(state)).build(),
66+
RequestBody.fromString(value)
67+
);
68+
}
5569
if (log.isDebugEnabled()) {
56-
log.debug("AWS S3 putObject result of state data - {}", JsonOps.toJsonString(putObjectResult));
70+
log.debug("AWS S3 putObject result of state data - {}", putObjectResult.toString());
5771
}
5872
}
5973

6074
@Override
6175
public boolean isAvailableInDatabase(String state) {
62-
AmazonS3 s3 = this.createS3Client();
63-
S3Object s3Object = getObject(s3, getKey(state));
76+
S3Client s3 = this.createS3Client();
77+
ResponseBytes<GetObjectResponse> s3Object = getObject(s3, getKey(state));
6478
if (s3Object == null) {
6579
return false;
6680
}
6781
String millisToExpire = null;
6882
try {
69-
millisToExpire = IOUtils.toString(s3Object.getObjectContent());
70-
return Long.valueOf(millisToExpire) > System.currentTimeMillis();
71-
} catch (IOException e) {
72-
log.error("Failed to load a state data for state: {}", state, e);
73-
return false;
83+
millisToExpire = s3Object.asString(StandardCharsets.UTF_8);
84+
return Long.parseLong(millisToExpire) > System.currentTimeMillis();
7485
} catch (NumberFormatException ne) {
7586
log.error("Invalid state value detected - state: {}, millisToExpire: {}", state, millisToExpire);
7687
return false;
88+
} catch (Exception e) {
89+
log.error("Failed to load a state data for state: {}", state, e);
90+
return false;
7791
}
7892
}
7993

8094
@Override
8195
public void deleteStateFromDatastore(String state) throws Exception {
82-
AmazonS3 s3 = this.createS3Client();
83-
s3.deleteObject(bucketName, getKey(state));
96+
try (S3Client s3 = this.createS3Client()) {
97+
s3.deleteObject(DeleteObjectRequest.builder().bucket(bucketName).key(getKey(state)).build());
98+
}
8499
}
85100

86-
protected AWSCredentials getCredentials() {
87-
return DefaultAWSCredentialsProviderChain.getInstance().getCredentials();
101+
protected AwsCredentials createCredentials(AwsCredentialsProvider provider) {
102+
return provider.resolveCredentials();
88103
}
89104

90-
protected AmazonS3 createS3Client() {
91-
return AmazonS3ClientBuilder.defaultClient();
105+
protected S3Client createS3Client() {
106+
return S3Client.builder().credentialsProvider(this.credentialsProvider).build();
92107
}
93108

94109
private String getKey(String state) {
95110
return "state/" + state;
96111
}
97112

98-
private S3Object getObject(AmazonS3 s3, String fullKey) {
113+
private ResponseBytes<GetObjectResponse> getObject(S3Client s3, String fullKey) {
99114
try {
100-
return s3.getObject(bucketName, fullKey);
101-
} catch (AmazonS3Exception e) {
115+
return s3.getObject(
116+
GetObjectRequest.builder().bucket(bucketName).key(fullKey).build(),
117+
ResponseTransformer.toBytes()
118+
);
119+
} catch (Exception e) {
102120
if (log.isDebugEnabled()) {
103-
log.debug("Amazon S3 object metadata not found (key: {}, AmazonS3Exception: {})", fullKey, e.toString());
121+
log.debug("Amazon S3 object metadata not found (key: {}, Exception: {})", fullKey, e.toString());
104122
}
105123
return null;
106124
}

bolt/src/test/java/test_locally/service/AmazonS3InstallationServiceTest.java

Lines changed: 39 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,44 @@
11
package test_locally.service;
22

3-
import com.amazonaws.SdkClientException;
4-
import com.amazonaws.auth.AWSCredentials;
5-
import com.amazonaws.services.s3.AmazonS3;
63
import com.slack.api.bolt.model.builtin.DefaultBot;
74
import com.slack.api.bolt.model.builtin.DefaultInstaller;
85
import com.slack.api.bolt.service.builtin.AmazonS3InstallationService;
96
import org.junit.Test;
7+
import software.amazon.awssdk.auth.credentials.AwsCredentials;
8+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
9+
import software.amazon.awssdk.core.ResponseBytes;
10+
import software.amazon.awssdk.core.sync.RequestBody;
11+
import software.amazon.awssdk.core.sync.ResponseTransformer;
12+
import software.amazon.awssdk.services.s3.S3Client;
13+
import software.amazon.awssdk.services.s3.model.*;
14+
15+
import java.nio.charset.StandardCharsets;
1016

1117
import static org.junit.Assert.assertNotNull;
1218
import static org.mockito.Mockito.*;
1319

1420
public class AmazonS3InstallationServiceTest {
1521

16-
@Test(expected = IllegalStateException.class)
17-
public void initializer_no_credentials() {
18-
AmazonS3 s3 = mock(AmazonS3.class);
19-
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
20-
@Override
21-
protected AWSCredentials getCredentials() {
22-
return null;
23-
}
24-
25-
@Override
26-
protected AmazonS3 createS3Client() {
27-
return s3;
28-
}
29-
};
30-
service.initializer().accept(null);
22+
static S3Client setupMocks(S3Client s3) {
23+
when(s3.headBucket((HeadBucketRequest) notNull())).thenReturn(HeadBucketResponse.builder().build());
24+
when(s3.headObject((HeadObjectRequest) notNull())).thenReturn(HeadObjectResponse.builder().build());
25+
when(s3.getObject((GetObjectRequest) notNull(), (ResponseTransformer<GetObjectResponse, ResponseBytes<GetObjectResponse>>) notNull()))
26+
.thenReturn(ResponseBytes.fromByteArray(GetObjectResponse.builder().build(), "{}".getBytes(StandardCharsets.UTF_8)));
27+
when(s3.putObject((PutObjectRequest) notNull(), (RequestBody) notNull())).thenReturn(PutObjectResponse.builder().build());
28+
return s3;
3129
}
3230

3331
@Test(expected = IllegalStateException.class)
34-
public void initializer_no_bucket() {
35-
AWSCredentials credentials = mock(AWSCredentials.class);
36-
AmazonS3 s3 = mock(AmazonS3.class);
32+
public void initializer_no_credentials() {
33+
S3Client s3 = setupMocks(mock(S3Client.class));
3734
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
3835
@Override
39-
protected AWSCredentials getCredentials() {
40-
return credentials;
36+
protected AwsCredentials createCredentials(AwsCredentialsProvider provider) {
37+
return null;
4138
}
4239

4340
@Override
44-
protected AmazonS3 createS3Client() {
41+
protected S3Client createS3Client() {
4542
return s3;
4643
}
4744
};
@@ -50,19 +47,18 @@ protected AmazonS3 createS3Client() {
5047

5148
@Test
5249
public void initializer() {
53-
AWSCredentials credentials = mock(AWSCredentials.class);
54-
when(credentials.getAWSAccessKeyId()).thenReturn("valid key");
55-
AmazonS3 s3 = mock(AmazonS3.class);
56-
when(s3.doesBucketExistV2(anyString())).thenReturn(true);
50+
AwsCredentials credentials = mock(AwsCredentials.class);
51+
when(credentials.accessKeyId()).thenReturn("valid key");
52+
S3Client s3 = setupMocks(mock(S3Client.class));
5753

5854
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
5955
@Override
60-
protected AWSCredentials getCredentials() {
56+
protected AwsCredentials createCredentials(AwsCredentialsProvider provider) {
6157
return credentials;
6258
}
6359

6460
@Override
65-
protected AmazonS3 createS3Client() {
61+
protected S3Client createS3Client() {
6662
return s3;
6763
}
6864
};
@@ -71,19 +67,18 @@ protected AmazonS3 createS3Client() {
7167

7268
@Test
7369
public void operations() throws Exception {
74-
AWSCredentials credentials = mock(AWSCredentials.class);
75-
when(credentials.getAWSAccessKeyId()).thenReturn("valid key");
76-
AmazonS3 s3 = mock(AmazonS3.class);
77-
when(s3.doesBucketExistV2(anyString())).thenReturn(true);
70+
AwsCredentials credentials = mock(AwsCredentials.class);
71+
when(credentials.accessKeyId()).thenReturn("valid key");
72+
S3Client s3 = setupMocks(mock(S3Client.class));
7873

7974
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
8075
@Override
81-
protected AWSCredentials getCredentials() {
76+
protected AwsCredentials createCredentials(AwsCredentialsProvider provider) {
8277
return credentials;
8378
}
8479

8580
@Override
86-
protected AmazonS3 createS3Client() {
81+
protected S3Client createS3Client() {
8782
return s3;
8883
}
8984
};
@@ -98,19 +93,18 @@ protected AmazonS3 createS3Client() {
9893

9994
@Test
10095
public void operations_historical_data_enabled() throws Exception {
101-
AWSCredentials credentials = mock(AWSCredentials.class);
102-
when(credentials.getAWSAccessKeyId()).thenReturn("valid key");
103-
AmazonS3 s3 = mock(AmazonS3.class);
104-
when(s3.doesBucketExistV2(anyString())).thenReturn(true);
96+
AwsCredentials credentials = mock(AwsCredentials.class);
97+
when(credentials.accessKeyId()).thenReturn("valid key");
98+
S3Client s3 = setupMocks(mock(S3Client.class));
10599

106100
AmazonS3InstallationService service = new AmazonS3InstallationService("test-bucket") {
107101
@Override
108-
protected AWSCredentials getCredentials() {
102+
protected AwsCredentials createCredentials(AwsCredentialsProvider provider) {
109103
return credentials;
110104
}
111105

112106
@Override
113-
protected AmazonS3 createS3Client() {
107+
protected S3Client createS3Client() {
114108
return s3;
115109
}
116110
};
@@ -129,32 +123,18 @@ public MyService(String bucketName) {
129123
super(bucketName);
130124
}
131125

132-
public AWSCredentials credentials() {
133-
return getCredentials();
134-
}
135-
136-
public AmazonS3 s3() {
126+
public S3Client s3() {
137127
return createS3Client();
138128
}
139129
}
140130

141-
@Test
142-
public void credentials() {
143-
MyService service = new MyService("test-bucket");
144-
try {
145-
AWSCredentials credentials = service.credentials();
146-
assertNotNull(credentials);
147-
} catch (SdkClientException ignored) {
148-
}
149-
}
150-
151131
@Test
152132
public void s3() {
153133
MyService service = new MyService("test-bucket");
154134
try {
155-
AmazonS3 s3 = service.s3();
135+
S3Client s3 = service.s3();
156136
assertNotNull(s3);
157-
} catch (SdkClientException ignored) {
137+
} catch (Exception ignored) {
158138
}
159139
}
160140

0 commit comments

Comments
 (0)