in brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java [265:423]
protected Maybe<T> getMaybeInternal() {
if (started.getAndSet(true))
throw new IllegalStateException("ValueResolver can only be used once");
if (expired) return Maybe.absent("Nested resolution of "+getOriginalValue()+" did not complete within "+timeout);
ExecutionContext exec = this.exec;
if (exec==null) {
// if execution context not specified, take it from the current task if present
exec = BasicExecutionContext.getCurrentExecutionContext();
}
CountdownTimer timerU = parentTimer;
if (timerU==null && timeout!=null)
timerU = timeout.countdownTimer();
final CountdownTimer timer = timerU;
if (timer!=null && !timer.isRunning())
timer.start();
checkTypeNotNull();
Object v = this.value;
//if the expected type is a closure or map and that's what we have, we're done (or if it's null);
//but not allowed to return a future or DeferredSupplier as the resolved value
if (v==null || (!forceDeep && type.isInstance(v) && !Future.class.isInstance(v) && !DeferredSupplier.class.isInstance(v)))
return Maybe.of((T) v);
try {
//if it's a task or a future, we wait for the task to complete
if (v instanceof TaskAdaptable<?>) {
//if it's a task, we make sure it is submitted
if (!((TaskAdaptable<?>) v).asTask().isSubmitted() ) {
if (exec==null)
return Maybe.absent("Value for unsubmitted task '"+getDescription()+"' requested but no execution context available");
exec.submit(((TaskAdaptable<?>) v).asTask());
}
}
if (v instanceof Future) {
final Future<?> vfuture = (Future<?>) v;
//including tasks, above
if (!vfuture.isDone()) {
Callable<Maybe> callable = new Callable<Maybe>() {
public Maybe call() throws Exception {
return Durations.get(vfuture, timer);
} };
String description = getDescription();
Maybe vm = Tasks.withBlockingDetails("Waiting for "+description, callable);
if (vm.isAbsent()) return vm;
v = vm.get();
} else {
v = vfuture.get();
}
} else if (v instanceof DeferredSupplier<?>) {
final DeferredSupplier<?> ds = (DeferredSupplier<?>) v;
if ((!Boolean.FALSE.equals(embedResolutionInTask) && (exec!=null || timeout!=null)) || Boolean.TRUE.equals(embedResolutionInTask)) {
if (exec==null)
return Maybe.absent("Embedding in task needed for '"+getDescription()+"' but no execution context available");
Callable<Object> callable = new Callable<Object>() {
public Object call() throws Exception {
try {
Tasks.setBlockingDetails("Retrieving "+ds);
return ds.get();
} finally {
Tasks.resetBlockingDetails();
}
} };
String description = getDescription();
TaskBuilder<Object> tb = Tasks.<Object>builder().body(callable).displayName("Resolving dependent value").description(description);
if (isTransientTask) tb.tag(BrooklynTaskTags.TRANSIENT_TASK_TAG);
Task<Object> vt = exec.submit(tb.build());
// TODO to handle immediate resolution, it would be nice to be able to submit
// so it executes in the current thread,
// or put a marker in the target thread or task while it is running that the task
// should never wait on anything other than another value being resolved
// (though either could recurse infinitely)
Maybe<Object> vm = Durations.get(vt, timer);
vt.cancel(true);
if (vm.isAbsent()) return (Maybe<T>)vm;
v = vm.get();
} else {
try {
Tasks.setBlockingDetails("Retrieving (non-task) "+ds);
v = ((DeferredSupplier<?>) ds).get();
} finally {
Tasks.resetBlockingDetails();
}
}
} else if (v instanceof Map) {
//and if a map or list we look inside
Map result = Maps.newLinkedHashMap();
for (Map.Entry<?,?> entry : ((Map<?,?>)v).entrySet()) {
Maybe<?> kk = new ValueResolver(entry.getKey(), type, this)
.description( (description!=null ? description+", " : "") + "map key "+entry.getKey() )
.getMaybe();
if (kk.isAbsent()) return (Maybe<T>)kk;
Maybe<?> vv = new ValueResolver(entry.getValue(), type, this)
.description( (description!=null ? description+", " : "") + "map value for key "+kk.get() )
.getMaybe();
if (vv.isAbsent()) return (Maybe<T>)vv;
result.put(kk.get(), vv.get());
}
return Maybe.of((T) result);
} else if (v instanceof Set) {
Set result = Sets.newLinkedHashSet();
int count = 0;
for (Object it : (Set)v) {
Maybe<?> vv = new ValueResolver(it, type, this)
.description( (description!=null ? description+", " : "") + "entry "+count )
.getMaybe();
if (vv.isAbsent()) return (Maybe<T>)vv;
result.add(vv.get());
count++;
}
return Maybe.of((T) result);
} else if (v instanceof Iterable) {
List result = Lists.newArrayList();
int count = 0;
for (Object it : (Iterable)v) {
Maybe<?> vv = new ValueResolver(it, type, this)
.description( (description!=null ? description+", " : "") + "entry "+count )
.getMaybe();
if (vv.isAbsent()) return (Maybe<T>)vv;
result.add(vv.get());
count++;
}
return Maybe.of((T) result);
} else {
return TypeCoercions.tryCoerce(v, TypeToken.of(type));
}
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
IllegalArgumentException problem = new IllegalArgumentException("Error resolving "+(description!=null ? description+", " : "")+v+", in "+exec+": "+e, e);
if (swallowExceptions) {
if (log.isDebugEnabled())
log.debug("Resolution of "+this+" failed, swallowing and returning: "+e);
return Maybe.absent(problem);
}
if (log.isDebugEnabled())
log.debug("Resolution of "+this+" failed, throwing: "+e);
throw problem;
}
return new ValueResolver(v, type, this).getMaybe();
}