Skip to content

Commit bfe9e50

Browse files
fix: interrupt all uploads (#7616)
* fix: interrupt all uploads * Update vaadin-upload-flow-parent/vaadin-upload-flow-integration-tests/src/main/java/com/vaadin/flow/component/upload/tests/UploadInterruptView.java Co-authored-by: Marco Collovati <[email protected]> --------- Co-authored-by: Marco Collovati <[email protected]>
1 parent c6aa2b2 commit bfe9e50

File tree

3 files changed

+161
-3
lines changed

3 files changed

+161
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.component.upload.tests;
17+
18+
import java.io.FilterOutputStream;
19+
import java.io.IOException;
20+
import java.io.OutputStream;
21+
22+
import com.vaadin.flow.component.html.Div;
23+
import com.vaadin.flow.component.upload.Upload;
24+
import com.vaadin.flow.component.upload.receivers.MultiFileMemoryBuffer;
25+
import com.vaadin.flow.router.Route;
26+
27+
@Route("vaadin-upload/interrupt")
28+
public class UploadInterruptView extends Div {
29+
30+
public UploadInterruptView() {
31+
Div output = new Div();
32+
output.setId("test-output");
33+
Div eventsOutput = new Div();
34+
eventsOutput.setId("test-events-output");
35+
36+
MultiFileMemoryBuffer buffer = new SlowMultiFileMemoryBuffer();
37+
Upload upload = new Upload(buffer);
38+
upload.setAcceptedFileTypes(".txt");
39+
upload.addStartedListener(event -> {
40+
if (isInterruptableFile(event.getFileName())) {
41+
event.getUpload().interruptUpload();
42+
}
43+
});
44+
upload.addFailedListener(event -> {
45+
eventsOutput.add("-failed");
46+
output.add("FAILED:" + event.getFileName() + ","
47+
+ event.getReason().getMessage());
48+
});
49+
upload.addSucceededListener(event -> eventsOutput.add("-succeeded"));
50+
upload.addAllFinishedListener(event -> eventsOutput.add("-finished"));
51+
52+
add(upload, output, eventsOutput);
53+
}
54+
55+
private static boolean isInterruptableFile(String fileName) {
56+
return fileName != null && fileName.endsWith(".interrupt.txt");
57+
}
58+
59+
// Returns an OutputStream that delays write operations for uploads. The
60+
// delay ensures that the interruption flag is set before uploads completion
61+
// so that the test can verify all uploads failed.
62+
private static class SlowMultiFileMemoryBuffer
63+
extends MultiFileMemoryBuffer {
64+
@Override
65+
public OutputStream receiveUpload(String fileName, String MIMEType) {
66+
OutputStream outputStream = super.receiveUpload(fileName, MIMEType);
67+
// Also delay the interrupted file to allow other uploads to start
68+
int delay = isInterruptableFile(fileName) ? 500 : 1000;
69+
return new SlowOutputStream(outputStream, delay);
70+
}
71+
}
72+
73+
private static class SlowOutputStream extends FilterOutputStream {
74+
75+
private final int delay;
76+
77+
SlowOutputStream(OutputStream delegate, int delay) {
78+
super(delegate);
79+
this.delay = delay;
80+
}
81+
82+
@Override
83+
public void write(byte[] b, int off, int len) throws IOException {
84+
try {
85+
Thread.sleep(delay);
86+
} catch (InterruptedException e) {
87+
Thread.currentThread().interrupt();
88+
throw new IOException("Interrupted", e);
89+
}
90+
super.write(b, off, len);
91+
}
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.component.upload.tests;
17+
18+
import java.io.File;
19+
import java.util.List;
20+
21+
import org.junit.Assert;
22+
import org.junit.Before;
23+
import org.junit.Test;
24+
import org.openqa.selenium.WebElement;
25+
26+
import com.vaadin.flow.component.upload.testbench.UploadElement;
27+
import com.vaadin.flow.testutil.TestPath;
28+
29+
@TestPath("vaadin-upload/interrupt")
30+
public class UploadInterruptIT extends AbstractUploadIT {
31+
32+
private UploadElement upload;
33+
private WebElement uploadOutput;
34+
private WebElement eventsOutput;
35+
36+
@Before
37+
public void init() {
38+
open();
39+
upload = $(UploadElement.class).waitForFirst();
40+
uploadOutput = $("div").id("test-output");
41+
eventsOutput = $("div").id("test-events-output");
42+
}
43+
44+
@Test
45+
public void uploadMultipleFiles_interruptUpload_allOngoingUploadsInterrupted()
46+
throws Exception {
47+
48+
List<File> files = List.of(createTempFile("txt"), createTempFile("txt"),
49+
createTempFile("interrupt.txt"), createTempFile("txt"),
50+
createTempFile("txt"));
51+
52+
upload.uploadMultiple(files, 10);
53+
54+
Assert.assertEquals("Expected all uploads to be interrupted",
55+
"-failed-failed-failed-failed-failed-finished",
56+
eventsOutput.getText());
57+
58+
files.forEach(file -> Assert.assertTrue(
59+
"Expected upload of " + file.getName() + " to be failed",
60+
uploadOutput.getText().contains(
61+
"FAILED:" + file.getName() + ",Upload interrupted")));
62+
}
63+
}

vaadin-upload-flow-parent/vaadin-upload-flow/src/main/java/com/vaadin/flow/component/upload/Upload.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public UploadIcon() {
8080
}
8181

8282
private StreamVariable streamVariable;
83-
private boolean interrupted = false;
83+
private volatile boolean interrupted = false;
8484

8585
private int activeUploads = 0;
8686
private boolean uploading;
@@ -458,7 +458,7 @@ private void startUpload() {
458458
* The interruption will be done by the receiving thread so this method will
459459
* return immediately and the actual interrupt will happen a bit later.
460460
* <p>
461-
* Note! this will interrupt all uploads in multi-upload mode.
461+
* Note! this will interrupt all ongoing uploads in multi-upload mode.
462462
*/
463463
public void interruptUpload() {
464464
if (isUploading()) {
@@ -468,7 +468,9 @@ public void interruptUpload() {
468468

469469
private void endUpload() {
470470
activeUploads--;
471-
interrupted = false;
471+
if (activeUploads == 0) {
472+
interrupted = false;
473+
}
472474
}
473475

474476
/**

0 commit comments

Comments
 (0)