Skip to content

Commit e1279f7

Browse files
authored
Merge branch 'master' into dongie/gh5755
2 parents ce9699e + c959433 commit e1279f7

File tree

5 files changed

+39
-10
lines changed

5 files changed

+39
-10
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": "Amazon S3",
4+
"contributor": "",
5+
"description": "Fix bug in S3 Multipart uploads with FileAsyncRequestBody - ensure that concurrency is limited correctly by bufferSizeInBytes"
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/customization/processors/LowercaseShapeValidatorProcessor.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
import software.amazon.awssdk.codegen.customization.CodegenCustomizationProcessor;
1919
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
2020
import software.amazon.awssdk.codegen.model.service.ServiceModel;
21+
import software.amazon.awssdk.codegen.validation.ModelInvalidException;
22+
import software.amazon.awssdk.codegen.validation.ValidationEntry;
23+
import software.amazon.awssdk.codegen.validation.ValidationErrorId;
24+
import software.amazon.awssdk.codegen.validation.ValidationErrorSeverity;
2125

2226
/**
2327
* A processor that validates shape names in service models to ensure they start with uppercase letters.
@@ -31,11 +35,12 @@ public void preprocess(ServiceModel serviceModel) {
3135

3236
serviceModel.getShapes().forEach((shapeName, shape) -> {
3337
if ("structure".equals(shape.getType()) && Character.isLowerCase(shapeName.charAt(0))) {
34-
throw new IllegalStateException(
35-
String.format("Shape name '%s' starts with a lowercase character." +
36-
"Shape names must start with an uppercase character." +
37-
"Please update the shape name in your service model",
38-
shapeName));
38+
String errorMsg = String.format("Shape name '%s' starts with a lowercase character. Shape names must start with"
39+
+ " an uppercase character. Please update the shape name in your service model",
40+
shapeName);
41+
ValidationEntry entry = ValidationEntry.create(ValidationErrorId.INVALID_IDENTIFIER_NAME,
42+
ValidationErrorSeverity.DANGER, errorMsg);
43+
throw ModelInvalidException.fromEntry(entry);
3944
}
4045
});
4146
}

codegen/src/test/java/software/amazon/awssdk/codegen/customization/processors/LowercaseShapeValidatorProcessorTest.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
import org.junit.jupiter.api.Test;
2424
import software.amazon.awssdk.codegen.model.service.ServiceModel;
2525
import software.amazon.awssdk.codegen.utils.ModelLoaderUtils;
26+
import software.amazon.awssdk.codegen.validation.ModelInvalidException;
27+
import software.amazon.awssdk.codegen.validation.ValidationEntry;
28+
import software.amazon.awssdk.codegen.validation.ValidationErrorId;
29+
import software.amazon.awssdk.codegen.validation.ValidationErrorSeverity;
2630

2731
public class LowercaseShapeValidatorProcessorTest {
2832

@@ -40,7 +44,14 @@ public static void setUp() throws IOException {
4044
@Test
4145
public void preprocess_serviceWithLowercaseShape_throwsException() {
4246
assertThatThrownBy(() -> processor.preprocess(serviceModel))
43-
.isInstanceOf(IllegalStateException.class)
44-
.hasMessageContaining("Shape name 'lowercaseshape' starts with a lowercase character");
47+
.isInstanceOf(ModelInvalidException.class)
48+
.hasMessageContaining("Shape name 'lowercaseshape' starts with a lowercase character")
49+
.matches(e -> {
50+
ModelInvalidException modelInvalid = (ModelInvalidException) e;
51+
ValidationEntry entry = modelInvalid.validationEntries().get(0);
52+
53+
return entry.getErrorId() == ValidationErrorId.INVALID_IDENTIFIER_NAME &&
54+
entry.getSeverity() == ValidationErrorSeverity.DANGER;
55+
}, "Validation entry details are correct");
4556
}
4657
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/FileAsyncRequestBodySplitHelper.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ private final class FileAsyncRequestBodyWrapper implements AsyncRequestBody {
166166

167167
private final FileAsyncRequestBody fileAsyncRequestBody;
168168
private final SimplePublisher<AsyncRequestBody> simplePublisher;
169+
private final AtomicBoolean hasCompleted = new AtomicBoolean(false);
169170

170171
FileAsyncRequestBodyWrapper(FileAsyncRequestBody fileAsyncRequestBody,
171172
SimplePublisher<AsyncRequestBody> simplePublisher) {
@@ -175,16 +176,22 @@ private final class FileAsyncRequestBodyWrapper implements AsyncRequestBody {
175176

176177
@Override
177178
public void subscribe(Subscriber<? super ByteBuffer> s) {
178-
fileAsyncRequestBody.doAfterOnComplete(() -> startNextRequestBody(simplePublisher))
179+
fileAsyncRequestBody.doAfterOnComplete(this::startNextIfNeeded)
179180
// The reason we still need to call startNextRequestBody when the subscription is
180181
// cancelled is that upstream could cancel the subscription even though the stream has
181182
// finished successfully before onComplete. If this happens, doAfterOnComplete callback
182183
// will never be invoked, and if the current buffer is full, the publisher will stop
183184
// sending new FileAsyncRequestBody, leading to uncompleted future.
184-
.doAfterOnCancel(() -> startNextRequestBody(simplePublisher))
185+
.doAfterOnCancel(this::startNextIfNeeded)
185186
.subscribe(s);
186187
}
187188

189+
private void startNextIfNeeded() {
190+
if (hasCompleted.compareAndSet(false, true)) {
191+
startNextRequestBody(simplePublisher);
192+
}
193+
}
194+
188195
@Override
189196
public Optional<Long> contentLength() {
190197
return fileAsyncRequestBody.contentLength();

core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/async/FileAsyncRequestBodySplitHelperTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private static Runnable verifyConcurrentRequests(FileAsyncRequestBodySplitHelper
9292
if (concurrency > maxConcurrency.get()) {
9393
maxConcurrency.set(concurrency);
9494
}
95-
assertThat(helper.numAsyncRequestBodiesInFlight()).hasValueLessThan(10);
95+
assertThat(helper.numAsyncRequestBodiesInFlight()).hasValueBetween(0,10);
9696
};
9797
}
9898
}

0 commit comments

Comments
 (0)