protected Maybe getMaybeInternal()

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();
    }