Skip to content

Commit b61997d

Browse files
committed
This closes #883
2 parents 97c5cf6 + 2e4fc3e commit b61997d

4 files changed

Lines changed: 83 additions & 14 deletions

File tree

api/src/main/java/org/apache/brooklyn/api/mgmt/ExecutionContext.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,20 +85,26 @@ public interface ExecutionContext extends Executor {
8585
* Implementations will typically act like {@link #get(TaskAdaptable)} with additional
8686
* tricks to attempt to be non-blocking, such as recognizing some "immediate" markers.
8787
* <p>
88-
* Supports {@link Callable}, {@link Runnable}, and {@link Supplier} argument types as well as {@link Task}.
88+
* Supports {@link Callable}, {@link Runnable}, {@link Supplier}, and {@link TaskFactory} argument types.
8989
* <p>
90-
* This executes the given code, and in the case of {@link Task} it may cancel it,
91-
* so the caller should not use this if the argument is going to be used later and
92-
* is expected to be pristine. Supply a {@link TaskFactory} if this method's {@link Task#cancel(boolean)}
93-
* is problematic, or consider other utilities (such as ValueResolver with immediate(true)
94-
* in a downstream project).
90+
* Passing in {@link Task} is deprecated and discouraged - see {@link #getImmediately(Task)}.
9591
*/
9692
// TODO reference ImmediateSupplier when that class is moved to utils project
9793
@Beta
98-
<T> Maybe<T> getImmediately(Object callableOrSupplierOrTask);
99-
/** As {@link #getImmediately(Object)} but strongly typed for a task. */
94+
<T> Maybe<T> getImmediately(Object callableOrSupplierOrTaskFactory);
95+
96+
/**
97+
* As {@link #getImmediately(Object)} but strongly typed for a task.
98+
*
99+
* @deprecated since 1.0.0; this can cause the task to be interrupted/cancelled, such that subsequent
100+
* use of {@code task.get()} will fail (if the value was not resolved immediately previously).
101+
* It is only safe to call this if the the given task is for a one-off usage (not expected
102+
* to be used again later). Consider supplying a {@link TaskFactory} if this tasks's
103+
* {@link Task#cancel(boolean)} is problematic, or consider other utilities
104+
* (such as ValueResolver with immediate(true) in the brooklyn-core project).
105+
*/
100106
@Beta
101-
<T> Maybe<T> getImmediately(Task<T> callableOrSupplierOrTask);
107+
<T> Maybe<T> getImmediately(Task<T> task);
102108

103109
/**
104110
* Efficient implementation of common case when {@link #submit(TaskAdaptable)} is followed by an immediate {@link Task#get()}.

api/src/main/java/org/apache/brooklyn/api/objs/Configurable.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public interface ConfigurationSupport {
6363
<T> T get(ConfigKey<T> key);
6464

6565
/**
66-
* @see {@link #getConfig(ConfigKey)}
66+
* @see {@link #get(ConfigKey)}
6767
*/
6868
<T> T get(HasConfigKey<T> key);
6969

@@ -73,7 +73,7 @@ public interface ConfigurationSupport {
7373
<T> T set(ConfigKey<T> key, T val);
7474

7575
/**
76-
* @see {@link #setConfig(HasConfigKey, Object)}
76+
* @see {@link #set(ConfigKey, Object)}
7777
*/
7878
<T> T set(HasConfigKey<T> key, T val);
7979

@@ -83,12 +83,16 @@ public interface ConfigurationSupport {
8383
* Returns immediately without blocking; subsequent calls to {@link #getConfig(ConfigKey)}
8484
* will execute the task, and block until the task completes.
8585
*
86-
* @see {@link #setConfig(ConfigKey, Object)}
86+
* @deprecated since 1.0.0; do not use task because can be evaluated only once, and if
87+
* cancelled will affect all subsequent lookups of the config value.
88+
* Consider using a {@link org.apache.brooklyn.api.mgmt.TaskFactory}.
8789
*/
8890
<T> T set(ConfigKey<T> key, Task<T> val);
8991

9092
/**
91-
* @see {@link #setConfig(ConfigKey, Task)}
93+
* @see {@link #set(ConfigKey, Task)}
94+
*
95+
* @deprecated since 1.0.0 (see {@link #set(ConfigKey, Task)}
9296
*/
9397
<T> T set(HasConfigKey<T> key, Task<T> val);
9498

core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,21 @@ public <T> Maybe<T> getImmediately(Object callableOrSupplier) {
270270
}
271271
callableOrSupplier = fakeTaskForContext.getJob();
272272
} else if (callableOrSupplier instanceof TaskAdaptable) {
273-
return getImmediately( ((TaskAdaptable<T>)callableOrSupplier).asTask() );
273+
Task<T> task = ((TaskAdaptable<T>)callableOrSupplier).asTask();
274+
if (task == callableOrSupplier) {
275+
// Our TaskAdaptable was a task, but not a BasicTask.
276+
// Avoid infinite recursion (don't just call ourselves again!).
277+
if (task.isDone()) {
278+
return Maybe.of(task.getUnchecked());
279+
} else if (task.isSubmitted() || task.isBegun()) {
280+
throw new ImmediateUnsupportedException("Task is in progress and incomplete: "+task);
281+
} else {
282+
throw new ImmediateUnsupportedException("Task not a 'BasicTask', so cannot extract job to get immediately: "+task);
283+
}
284+
} else {
285+
// recurse - try again with the task we've just generated
286+
return getImmediately(task);
287+
}
274288
} else {
275289
fakeTaskForContext = new BasicTask<T>(MutableMap.of("displayName", "Immediate evaluation"));
276290
}

core/src/test/java/org/apache/brooklyn/util/core/task/ValueResolverTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
3636
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
3737
import org.apache.brooklyn.test.Asserts;
38+
import org.apache.brooklyn.util.core.task.ImmediateSupplier.ImmediateUnsupportedException;
3839
import org.apache.brooklyn.util.core.task.ImmediateSupplier.ImmediateValueNotAvailableException;
3940
import org.apache.brooklyn.util.guava.Maybe;
4041
import org.apache.brooklyn.util.time.Duration;
@@ -188,6 +189,50 @@ public void testUnsubmittedTaskWithExecutionContextExecutesAndTimesOutImmediate(
188189
Asserts.assertThat(t, (tt) -> !tt.isDone());
189190
}
190191

192+
public void testExecutionContextGetImmediatelyBasicTaskTooSlow() throws Exception {
193+
final Task<String> t = newSleepTask(Duration.ONE_MINUTE, "foo");
194+
195+
// Extracts job from task, tries to run it, and sees it tries to block so aborts.
196+
// It will also render the task unusable (cancelled); not bothering to assert that!
197+
Maybe<String> result = app.getExecutionContext().getImmediately(t);
198+
Assert.assertFalse(result.isPresent(), "result="+result);
199+
}
200+
201+
public void testExecutionContextGetImmediatelyBasicTaskSucceeds() throws Exception {
202+
final Task<String> t = newSleepTask(Duration.ZERO, "foo");
203+
204+
// Extracts job from task, tries to run it; because it doesn't block we'll get the result.
205+
// It will also render the task unusable (cancelled); calling `t.get()` will throw CancellationException.
206+
Maybe<String> result = app.getExecutionContext().getImmediately(t);
207+
Assert.assertTrue(result.isPresent(), "result="+result);
208+
Assert.assertEquals(result.get(), "foo", "result="+result);
209+
}
210+
211+
public void testExecutionContextGetImmediatelyTaskNotBasicFails() throws Exception {
212+
final TaskInternal<String> t = (TaskInternal<String>) newSleepTask(Duration.ZERO, "foo");
213+
final Task<String> t2 = new ForwardingTask<String>() {
214+
@Override
215+
protected TaskInternal<String> delegate() {
216+
return t;
217+
}
218+
@Override public boolean cancel(TaskCancellationMode mode) {
219+
return delegate().cancel();
220+
}
221+
public Task<String> asTask() {
222+
return this;
223+
}
224+
};
225+
226+
// Does not handle non-basic tasks; an acceptable limitation.
227+
// Previously it threw StackOverflowError; now says unsupported.
228+
try {
229+
Maybe<String> result = app.getExecutionContext().getImmediately(t2);
230+
Asserts.shouldHaveFailedPreviously("result="+result);
231+
} catch (ImmediateUnsupportedException e) {
232+
Asserts.expectedFailureContains(e, "cannot extract job");
233+
}
234+
}
235+
191236
public void testSwallowError() {
192237
ValueResolver<String> result = Tasks.resolving(newThrowTask(Duration.ZERO)).as(String.class).context(app).swallowExceptions();
193238
assertMaybeIsAbsent(result);

0 commit comments

Comments
 (0)