Skip to content

Commit a979885

Browse files
committed
SettableListenableFuture properly rethrows Error
Issue: SPR-14298
1 parent dacc31e commit a979885

File tree

2 files changed

+36
-27
lines changed

2 files changed

+36
-27
lines changed

spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323
import java.util.concurrent.atomic.AtomicReference;
2424

2525
import org.springframework.util.Assert;
26+
import org.springframework.util.ReflectionUtils;
2627

2728
/**
2829
* A {@link org.springframework.util.concurrent.ListenableFuture ListenableFuture}
@@ -49,11 +50,11 @@ public SettableListenableFuture() {
4950

5051

5152
/**
52-
* Set the value of this future. This method will return {@code true} if
53-
* the value was set successfully, or {@code false} if the future has already
54-
* been set or cancelled.
55-
* @param value the value that will be set.
56-
* @return {@code true} if the value was successfully set, else {@code false}.
53+
* Set the value of this future. This method will return {@code true} if the
54+
* value was set successfully, or {@code false} if the future has already been
55+
* set or cancelled.
56+
* @param value the value that will be set
57+
* @return {@code true} if the value was successfully set, else {@code false}
5758
*/
5859
public boolean set(T value) {
5960
boolean success = this.settableTask.setValue(value);
@@ -64,14 +65,14 @@ public boolean set(T value) {
6465
}
6566

6667
/**
67-
* Set the exception of this future. This method will return {@code true} if
68-
* the exception was set successfully, or {@code false} if the future has already
69-
* been set or cancelled.
70-
* @param exception the value that will be set.
71-
* @return {@code true} if the exception was successfully set, else {@code false}.
68+
* Set the exception of this future. This method will return {@code true} if the
69+
* exception was set successfully, or {@code false} if the future has already been
70+
* set or cancelled.
71+
* @param exception the value that will be set
72+
* @return {@code true} if the exception was successfully set, else {@code false}
7273
*/
7374
public boolean setException(Throwable exception) {
74-
Assert.notNull(exception, "'exception' must not be null");
75+
Assert.notNull(exception, "Exception must not be null");
7576
boolean success = this.settableTask.setException(exception);
7677
if (success) {
7778
this.listenableFuture.run();
@@ -149,7 +150,7 @@ protected void interruptTask() {
149150

150151
private static class SettableTask<T> implements Callable<T> {
151152

152-
private static final String NO_VALUE = SettableListenableFuture.class.getName() + ".NO_VALUE";
153+
private static final Object NO_VALUE = new Object();
153154

154155
private final AtomicReference<Object> value = new AtomicReference<Object>(NO_VALUE);
155156

@@ -176,10 +177,11 @@ public void setCancelled() {
176177
@SuppressWarnings("unchecked")
177178
@Override
178179
public T call() throws Exception {
179-
if (value.get() instanceof Exception) {
180-
throw (Exception) value.get();
180+
Object val = this.value.get();
181+
if (val instanceof Throwable) {
182+
ReflectionUtils.rethrowException((Throwable) val);
181183
}
182-
return (T) value.get();
184+
return (T) val;
183185
}
184186
}
185187

spring-core/src/test/java/org/springframework/util/concurrent/SettableListenableFutureTests.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,7 +21,6 @@
2121
import java.util.concurrent.TimeUnit;
2222
import java.util.concurrent.TimeoutException;
2323

24-
import org.junit.Before;
2524
import org.junit.Test;
2625

2726
import static org.hamcrest.Matchers.*;
@@ -35,12 +34,8 @@
3534
@SuppressWarnings({ "rawtypes", "unchecked" })
3635
public class SettableListenableFutureTests {
3736

38-
private SettableListenableFuture<String> settableListenableFuture;
37+
private final SettableListenableFuture<String> settableListenableFuture = new SettableListenableFuture<String>();
3938

40-
@Before
41-
public void setUp() {
42-
settableListenableFuture = new SettableListenableFuture<String>();
43-
}
4439

4540
@Test
4641
public void validateInitialValues() {
@@ -76,6 +71,20 @@ public void throwsSetExceptionWrappedInExecutionException() throws ExecutionExce
7671
}
7772
}
7873

74+
@Test
75+
public void throwsSetErrorWrappedInExecutionException() throws ExecutionException, InterruptedException {
76+
Throwable exception = new OutOfMemoryError();
77+
boolean wasSet = settableListenableFuture.setException(exception);
78+
assertTrue(wasSet);
79+
try {
80+
settableListenableFuture.get();
81+
fail("Expected ExecutionException");
82+
}
83+
catch (ExecutionException ex) {
84+
assertThat(ex.getCause(), equalTo(exception));
85+
}
86+
}
87+
7988
@Test
8089
public void setValueTriggersCallback() {
8190
String string = "hello";
@@ -85,7 +94,6 @@ public void setValueTriggersCallback() {
8594
public void onSuccess(String result) {
8695
callbackHolder[0] = result;
8796
}
88-
8997
@Override
9098
public void onFailure(Throwable ex) {
9199
fail("Expected onSuccess() to be called");
@@ -104,7 +112,6 @@ public void setValueTriggersCallbackOnlyOnce() {
104112
public void onSuccess(String result) {
105113
callbackHolder[0] = result;
106114
}
107-
108115
@Override
109116
public void onFailure(Throwable ex) {
110117
fail("Expected onSuccess() to be called");
@@ -124,7 +131,6 @@ public void setExceptionTriggersCallback() {
124131
public void onSuccess(String result) {
125132
fail("Expected onFailure() to be called");
126133
}
127-
128134
@Override
129135
public void onFailure(Throwable ex) {
130136
callbackHolder[0] = ex;
@@ -143,7 +149,6 @@ public void setExceptionTriggersCallbackOnlyOnce() {
143149
public void onSuccess(String result) {
144150
fail("Expected onFailure() to be called");
145151
}
146-
147152
@Override
148153
public void onFailure(Throwable ex) {
149154
callbackHolder[0] = ex;
@@ -322,6 +327,7 @@ public void cancelDoesNotNotifyCallbacksOnSetException() {
322327
verifyNoMoreInteractions(callback);
323328
}
324329

330+
325331
private static class InterruptableSettableListenableFuture extends SettableListenableFuture<String> {
326332

327333
private boolean interrupted = false;
@@ -335,4 +341,5 @@ boolean calledInterruptTask() {
335341
return interrupted;
336342
}
337343
}
344+
338345
}

0 commit comments

Comments
 (0)