[SCB-2189]uploading multiple files using RestTemplate randomly fail (#2233)
diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java
new file mode 100644
index 0000000..cebd61c
--- /dev/null
+++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.demo.springmvc.client;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.servicecomb.demo.CategorizedTestCase;
+import org.apache.servicecomb.demo.TestMgr;
+import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Component;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+@Component
+public class TestUploadSchema implements CategorizedTestCase {
+ @Override
+ public void testRestTransport() throws Exception {
+ testServerStartupSuccess();
+ testUploadMultiBigFiles();
+ }
+
+ private void testServerStartupSuccess() {
+ RestTemplate template = RestTemplateBuilder.create();
+ boolean result = template.getForObject("servicecomb://springmvc/upload/isServerStartUpSuccess", Boolean.class);
+ TestMgr.check(result, true);
+ }
+
+ private void testUploadMultiBigFiles() throws Exception {
+ final int fileNum = 5;
+ List<File> files = new ArrayList<>(fileNum);
+
+ String fileName = UUID.randomUUID().toString();
+ for (int i = 0; i < fileNum; i++) {
+ File tempFile = new File("random-client-" + fileName + i);
+ files.add(tempFile);
+ FileOutputStream fo = new FileOutputStream(tempFile);
+ byte[] data = new byte[1024 * 1024 * 10];
+ Arrays.fill(data, (byte) 33);
+ IOUtils.write(data, fo);
+ fo.close();
+ }
+
+ RestTemplate template = RestTemplateBuilder.create();
+
+ MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
+ for (int i = 0; i < fileNum; i++) {
+ map.add("files", new FileSystemResource(files.get(i)));
+ }
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA);
+ HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(map, headers);
+
+ String result = template.postForObject("servicecomb://springmvc/upload/fileUpload", entity, String.class);
+ TestMgr.check(result, "success");
+ }
+}
diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java
index d8d1bdd..9ca48e7 100644
--- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java
+++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java
@@ -89,7 +89,7 @@
}
public void testRegisteredBasePath() {
- TestMgr.check(15, RegistrationManager.INSTANCE.getMicroservice().getPaths().size());
+ TestMgr.check(16, RegistrationManager.INSTANCE.getMicroservice().getPaths().size());
}
private String getSwaggerContent(Swagger swagger) {
diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java
new file mode 100644
index 0000000..24ec3f2
--- /dev/null
+++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.demo.springmvc.server;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.servicecomb.demo.TestMgr;
+import org.apache.servicecomb.provider.rest.common.RestSchema;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.multipart.MultipartFile;
+
+@RestSchema(schemaId = "UploadSchema")
+@RequestMapping(path = "/upload", produces = MediaType.APPLICATION_JSON_VALUE)
+public class UploadSchema {
+ @PostMapping(path = "/fileUpload", produces = MediaType.TEXT_PLAIN_VALUE,
+ consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+ public String fileUpload(@RequestPart(name = "files") List<MultipartFile> files) {
+ try {
+ String fileName = UUID.randomUUID().toString();
+ int index = 0;
+ for (MultipartFile file : files) {
+ File tempFile = new File("random-server-" + fileName + index);
+ file.transferTo(tempFile);
+ index++;
+ }
+ return "success";
+ } catch (IOException e) {
+ return "failed";
+ }
+ }
+
+ @GetMapping(path = "/isServerStartUpSuccess")
+ public boolean isServerStartUpSuccess() {
+ return TestMgr.isSuccess();
+ }
+}
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java
index d76e7f2..cc147c1 100644
--- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java
@@ -23,12 +23,12 @@
import javax.servlet.http.Part;
-import io.vertx.core.Handler;
import org.apache.commons.io.IOUtils;
import org.apache.servicecomb.foundation.vertx.http.DownloadUtils;
import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart;
import io.vertx.core.Context;
+import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.streams.ReadStream;
import io.vertx.core.streams.WriteStream;
@@ -66,7 +66,13 @@
public CompletableFuture<Void> toWriteStream(WriteStream<Buffer> writeStream, Handler<Throwable> throwableHandler) {
return prepareReadStream()
.thenCompose(readStream -> new PumpCommon().pump(context, readStream, writeStream, throwableHandler))
- .whenComplete((v, e) -> DownloadUtils.clearPartResource(part));
+ .whenComplete((v, e) -> {
+ DownloadUtils.clearPartResource(part);
+ // PumpImpl will add drainHandler to writeStream,
+ // in order to support write multiple files to same writeStream,
+ // need reset after one stream is successful.
+ writeStream.drainHandler(null);
+ });
}
public CompletableFuture<Void> toOutputStream(OutputStream outputStream, boolean autoCloseOutputStream) {