Skip to content

Commit e5570f7

Browse files
authored
Making UnmodifiableMapOfLists serializable since it's a component of serializable classes such as AwsErrorDetails (#2768)
1 parent 213f898 commit e5570f7

File tree

6 files changed

+183
-1
lines changed

6 files changed

+183
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "bugfix",
5+
"description": "Allow AwsErrorDetails to be serialized using Java serialization"
6+
}

core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsErrorDetails.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package software.amazon.awssdk.awscore.exception;
1717

1818
import java.io.Serializable;
19+
import java.util.Objects;
1920
import software.amazon.awssdk.annotations.SdkPublicApi;
2021
import software.amazon.awssdk.core.SdkBytes;
2122
import software.amazon.awssdk.http.SdkHttpResponse;
@@ -102,6 +103,32 @@ public static Class<? extends Builder> serializableBuilderClass() {
102103
return BuilderImpl.class;
103104
}
104105

106+
@Override
107+
public boolean equals(Object o) {
108+
if (this == o) {
109+
return true;
110+
}
111+
if (o == null || getClass() != o.getClass()) {
112+
return false;
113+
}
114+
AwsErrorDetails that = (AwsErrorDetails) o;
115+
return Objects.equals(errorMessage, that.errorMessage) &&
116+
Objects.equals(errorCode, that.errorCode) &&
117+
Objects.equals(serviceName, that.serviceName) &&
118+
Objects.equals(sdkHttpResponse, that.sdkHttpResponse) &&
119+
Objects.equals(rawResponse, that.rawResponse);
120+
}
121+
122+
@Override
123+
public int hashCode() {
124+
int result = Objects.hashCode(errorMessage);
125+
result = 31 * result + Objects.hashCode(errorCode);
126+
result = 31 * result + Objects.hashCode(serviceName);
127+
result = 31 * result + Objects.hashCode(sdkHttpResponse);
128+
result = 31 * result + Objects.hashCode(rawResponse);
129+
return result;
130+
}
131+
105132
@Override
106133
public String toString() {
107134
return ToString.builder("AwsErrorDetails")
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.awscore.exception;
17+
18+
import java.io.IOException;
19+
import nl.jqno.equalsverifier.EqualsVerifier;
20+
import org.junit.Test;
21+
22+
public class AwsErrorDetailsTest {
23+
24+
@Test
25+
public void equals_hashcode() throws IOException, ClassNotFoundException {
26+
EqualsVerifier.forClass(AwsErrorDetails.class)
27+
.usingGetClass()
28+
.verify();
29+
}
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.awscore.exception;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.io.ByteArrayInputStream;
21+
import java.io.ByteArrayOutputStream;
22+
import java.io.IOException;
23+
import java.io.ObjectInputStream;
24+
import java.io.ObjectOutputStream;
25+
import java.time.Duration;
26+
import java.util.Collections;
27+
import org.junit.Test;
28+
import software.amazon.awssdk.http.AbortableInputStream;
29+
import software.amazon.awssdk.http.SdkHttpFullResponse;
30+
import software.amazon.awssdk.http.SdkHttpResponse;
31+
import software.amazon.awssdk.testutils.InputStreamUtils;
32+
import software.amazon.awssdk.utils.IoUtils;
33+
import software.amazon.awssdk.utils.StringInputStream;
34+
35+
public class AwsServiceExceptionSerializationTest {
36+
37+
@Test
38+
public void serializeServiceException() throws IOException, ClassNotFoundException {
39+
AwsServiceException expectedException = createException();
40+
41+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
42+
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
43+
objectOutputStream.writeObject(expectedException);
44+
objectOutputStream.flush();
45+
objectOutputStream.close();
46+
47+
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
48+
AwsServiceException resultException = (AwsServiceException) ois.readObject();
49+
50+
assertSameValues(resultException, expectedException);
51+
}
52+
53+
private void assertSameValues(AwsServiceException resultException, AwsServiceException expectedException) {
54+
assertThat(resultException.getMessage()).isEqualTo(expectedException.getMessage());
55+
assertThat(resultException.requestId()).isEqualTo(expectedException.requestId());
56+
assertThat(resultException.extendedRequestId()).isEqualTo(expectedException.extendedRequestId());
57+
assertThat(resultException.toBuilder().clockSkew()).isEqualTo(expectedException.toBuilder().clockSkew());
58+
assertThat(resultException.toBuilder().cause().getMessage()).isEqualTo(expectedException.toBuilder().cause().getMessage());
59+
assertThat(resultException.awsErrorDetails()).isEqualTo(expectedException.awsErrorDetails());
60+
}
61+
62+
63+
private AwsServiceException createException() {
64+
AbortableInputStream contentStream = AbortableInputStream.create(new StringInputStream("some content"));
65+
SdkHttpResponse httpResponse = SdkHttpFullResponse.builder()
66+
.statusCode(403)
67+
.statusText("SomeText")
68+
.putHeader("sample", "value")
69+
.content(contentStream)
70+
.build();
71+
72+
AwsErrorDetails errorDetails = AwsErrorDetails.builder()
73+
.errorCode("someCode")
74+
.errorMessage("message")
75+
.serviceName("someService")
76+
.sdkHttpResponse(httpResponse)
77+
.build();
78+
79+
return AwsServiceException.builder()
80+
.awsErrorDetails(errorDetails)
81+
.statusCode(403)
82+
.cause(new RuntimeException("someThrowable"))
83+
.clockSkew(Duration.ofSeconds(2))
84+
.requestId("requestId")
85+
.extendedRequestId("extendedRequestId")
86+
.message("message")
87+
.build();
88+
}
89+
}

http-client-spi/src/main/java/software/amazon/awssdk/http/DefaultSdkHttpFullResponse.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
import static software.amazon.awssdk.utils.CollectionUtils.deepCopyMap;
1919
import static software.amazon.awssdk.utils.CollectionUtils.deepUnmodifiableMap;
2020

21+
import java.beans.Transient;
2122
import java.io.Serializable;
2223
import java.util.ArrayList;
2324
import java.util.LinkedHashMap;
2425
import java.util.List;
2526
import java.util.Map;
27+
import java.util.Objects;
2628
import java.util.Optional;
2729
import java.util.TreeMap;
2830
import software.amazon.awssdk.annotations.Immutable;
@@ -59,6 +61,7 @@ public Map<String, List<String>> headers() {
5961
return headers;
6062
}
6163

64+
@Transient
6265
@Override
6366
public Optional<AbortableInputStream> content() {
6467
return Optional.ofNullable(content);
@@ -79,6 +82,28 @@ public SdkHttpFullResponse.Builder toBuilder() {
7982
return new Builder(this);
8083
}
8184

85+
@Override
86+
public boolean equals(Object o) {
87+
if (this == o) {
88+
return true;
89+
}
90+
if (o == null || getClass() != o.getClass()) {
91+
return false;
92+
}
93+
DefaultSdkHttpFullResponse that = (DefaultSdkHttpFullResponse) o;
94+
return Objects.equals(statusCode, that.statusCode) &&
95+
Objects.equals(statusText, that.statusText) &&
96+
Objects.equals(headers, that.headers);
97+
}
98+
99+
@Override
100+
public int hashCode() {
101+
int result = statusText != null ? statusText.hashCode() : 0;
102+
result = 31 * result + statusCode;
103+
result = 31 * result + Objects.hashCode(headers);
104+
return result;
105+
}
106+
82107
/**
83108
* Builder for a {@link DefaultSdkHttpFullResponse}.
84109
*/

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.utils;
1717

18+
import java.io.Serializable;
1819
import java.util.AbstractMap.SimpleImmutableEntry;
1920
import java.util.Collection;
2021
import java.util.Collections;
@@ -32,7 +33,10 @@
3233
* An unmodifiable view of a {@code Map<T, List<U>>}. Created using {@link CollectionUtils#unmodifiableMapOfLists(Map)}.
3334
*/
3435
@SdkInternalApi
35-
class UnmodifiableMapOfLists<T, U> implements Map<T, List<U>> {
36+
class UnmodifiableMapOfLists<T, U> implements Map<T, List<U>>, Serializable {
37+
38+
private static final long serialVersionUID = 1L;
39+
3640
private final Map<T, List<U>> delegate;
3741

3842
UnmodifiableMapOfLists(Map<T, List<U>> delegate) {

0 commit comments

Comments
 (0)