org.apache.brooklyn.api.mgmt.Task Java Examples

The following examples show how to use org.apache.brooklyn.api.mgmt.Task. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: BasicExecutionManager.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
protected boolean deleteTaskNonRecursive(Task<?> task) {
    Set<?> tags = TaskTags.getTagsFast(checkNotNull(task, "task"));
    for (Object tag : tags) {
        synchronized (tasksByTag) {
            Set<Task<?>> tasks = tasksWithTagLiveOrNull(tag);
            if (tasks != null) {
                tasks.remove(task);
                if (tasks.isEmpty()) {
                    tasksByTag.remove(tag);
                }
            }
        }
    }
    Task<?> removed = tasksById.remove(task.getId());
    incompleteTaskIds.remove(task.getId());
    if (removed!=null && removed.isSubmitted() && !removed.isDone(true)) {
        Entity context = BrooklynTaskTags.getContextEntity(removed);
        if (context!=null && !Entities.isManaged(context)) {
            log.debug("Forgetting about active task on unmanagement of "+context+": "+removed);
        } else {
            log.warn("Deleting submitted task before completion: "+removed+"; this task will continue to run in the background outwith "+this+", but perhaps it should have been cancelled?");
        }
    }
    return removed != null;
}
 
Example #2
Source File: EnricherWithDeferredSupplierTest.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
@Override
public Task<Entity> newTask() {
    return TaskBuilder.<Entity>builder()
            .displayName(toString())
            .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
            .body(new Callable<Entity>() {
                @Override
                public Entity call() {
                    EntityInternal entity = entity();
                    Collection<Entity> entitiesToSearch = entity.getManagementContext().getEntityManager().getEntities();
                    Optional<Entity> result = Iterables.tryFind(entitiesToSearch, EntityPredicates.configEqualTo(TAG, tag));
                    
                    if (result.isPresent()) {
                        return result.get();
                    } else {
                        throw new NoSuchElementException("No entity matching id " + tag+" in "+entitiesToSearch);
                    }
                }})
            .build();
}
 
Example #3
Source File: SoftwareProcessEntityLatchTest.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
private void assertEffectorBlockingDetailsEventually(final Entity entity, final String effectorName, final String blockingDetailsSnippet) {
    Asserts.succeedsEventually(new Runnable() {
        @Override public void run() {
            final Set<Task<?>> tasksWithAllTags = mgmt.getExecutionManager().getTasksWithAllTags(ImmutableList.of(BrooklynTaskTags.EFFECTOR_TAG, BrooklynTaskTags.tagForContextEntity(entity)));
            Task<?> entityTask = null;
            for (Task<?> item : tasksWithAllTags) {
                final String itemName = getEffectorName(item);
                entityTask = itemName.equals(effectorName) ? item : entityTask;
            }
            if (entityTask == null) {
                Asserts.fail("Could not find task for effector " + effectorName);
            }
            String blockingDetails = getBlockingDetails(entityTask);
            assertTrue(blockingDetails.contains(blockingDetailsSnippet));
        }});
}
 
Example #4
Source File: DependentConfigurationTest.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
@Test
public void testAttributeWhenReadyWithAbort() throws Exception {
    final Task<String> t = submit(DependentConfiguration.builder()
            .attributeWhenReady(entity, TestEntity.NAME)
            .abortIf(entity2, TestEntity.SEQUENCE, Predicates.equalTo(1))
            .build());

    assertNotDoneContinually(t);

    entity2.sensors().set(TestEntity.SEQUENCE, 321);
    assertNotDoneContinually(t);

    entity2.sensors().set(TestEntity.SEQUENCE, 1);
    try {
        assertDoneEventually(t);
        fail();
    } catch (Exception e) {
        if (!e.toString().contains("Aborted waiting for ready")) throw e;
    }
}
 
Example #5
Source File: AbstractSoftwareProcessDriver.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
private Function<SourceAndDestination, Task<?>> newCopyResourceFunction() {
    return new Function<SourceAndDestination, Task<?>>() {
        @Override
        public Task<?> apply(final SourceAndDestination input) {
            return Tasks.builder()
                    .displayName("Copying file: source=" + input.source + ", destination=" + input.destination)
                    .body(new Callable<Object>() {
                        @Override
                        public Integer call() {
                            return copyResource(input.source, input.destination, true);
                        }
                    })
                    .build();
        }
    };
}
 
Example #6
Source File: TasksTest.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
@Test
public void testErrorsResolvingPropagatesOrSwallowedAllCorrectly() throws Exception {
    app.config().set(TestEntity.CONF_OBJECT, ValueResolverTest.newThrowTask(Duration.ZERO));
    Task<Object> t = Tasks.builder().body(Functionals.callable(EntityFunctions.config(TestEntity.CONF_OBJECT), app)).build();
    ValueResolver<Object> v = Tasks.resolving(t).as(Object.class).context(app);
    
    ValueResolverTest.assertThrowsOnGetMaybe(v);
    ValueResolverTest.assertThrowsOnGet(v);
    
    v.swallowExceptions();
    ValueResolverTest.assertMaybeIsAbsent(v);
    ValueResolverTest.assertThrowsOnGet(v);
    
    v.defaultValue("foo");
    ValueResolverTest.assertMaybeIsAbsent(v);
    assertEquals(v.clone().get(), "foo");
    assertResolvesValue(v, Object.class, "foo");
}
 
Example #7
Source File: DependentConfigurationTest.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
@Test
public void testAttributeWhenReadyWithAbortWaitingNow() throws Exception {
    final Task<String> t = submit(new Callable<String>() {
        @Override
        public String call() {
            return DependentConfiguration.builder()
                    .attributeWhenReady(entity, TestEntity.NAME)
                    .abortIf(entity2, TestEntity.SEQUENCE, Predicates.equalTo(1))
                    .runNow();
        }});

    assertNotDoneContinually(t);

    entity2.sensors().set(TestEntity.SEQUENCE, 321);
    assertNotDoneContinually(t);

    entity2.sensors().set(TestEntity.SEQUENCE, 1);
    try {
        assertDoneEventually(t);
        fail();
    } catch (Exception e) {
        if (!e.toString().contains("Aborted waiting for ready")) throw e;
    }
}
 
Example #8
Source File: EffectorUtils.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
public static <T> Task<T> invokeEffectorAsync(Entity entity, Effector<T> eff, Map<String,?> parameters) {
    String name = eff.getName();

    if (log.isDebugEnabled()) log.debug("Invoking-async effector {} on {}", new Object[] { name, entity });
    if (log.isTraceEnabled()) log.trace("Invoking-async effector {} on {} with args {}", new Object[] { name, entity, Sanitizer.sanitize(parameters) });
    EntityManagementSupport mgmtSupport = ((EntityInternal)entity).getManagementSupport();
    if (!mgmtSupport.isDeployed()) {
        mgmtSupport.attemptLegacyAutodeployment(name);
    }
    ManagementContextInternal mgmtContext = (ManagementContextInternal) ((EntityInternal)entity).getManagementContext();

    // FIXME seems brittle to have the listeners in the Utils method; better to move into the context.invokeEff
    // (or whatever the last mile before invoking the effector is - though currently there is not such a canonical place!)
    mgmtSupport.getEntityChangeListener().onEffectorStarting(eff, parameters);
    try {
        return mgmtContext.invokeEffector(entity, eff, parameters);
    } finally {
        // FIXME this is really Effector submitted
        mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff);
    }
}
 
Example #9
Source File: DslTest.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
static Maybe<?> execDslImmediately(final BrooklynDslDeferredSupplier<?> dsl, final TypeToken<?> type, final Entity context, boolean execInTask) throws Exception {
    // Exec'ing immediately will call DSL in current thread. It needs to find the context entity,
    // and does this using BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()).
    // If we are not in a task executed by the context entity, then this lookup will fail. 
    Callable<Maybe<?>> job = new Callable<Maybe<?>>() {
        @Override
        public Maybe<?> call() throws Exception {
            return Tasks.resolving(dsl).as(type)
                    .context(context)
                    .description("Computing "+dsl)
                    .immediately(true)
                    .getMaybe();
        }
    };
    if (execInTask) {
        Task<Maybe<?>> task = ((EntityInternal)context).getExecutionContext().submit("Resolving DSL for test: "+dsl, job);
        task.get(Asserts.DEFAULT_LONG_TIMEOUT);
        assertTrue(task.isDone());
        return task.get();
        
    } else {
        return job.call();
    }
}
 
Example #10
Source File: PeriodicDeltaChangeListener.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
public void start() {
    synchronized (startStopMutex) {
        if (state==ListenerState.RUNNING || (scheduledTask!=null && !scheduledTask.isDone())) {
            LOG.warn("Request to start "+this+" when already running - "+scheduledTask+"; ignoring");
            return;
        }
        state = ListenerState.RUNNING;

        Callable<Task<?>> taskFactory = new Callable<Task<?>>() {
            @Override public Task<Void> call() {
                return Tasks.<Void>builder().dynamic(false).displayName("periodic-persister").body(new Callable<Void>() {
                    @Override
                    public Void call() {
                        persistNowSafely();
                        return null;
                    }}).build();
            }
        };
        scheduledTask = (ScheduledTask) executionContext.submit(
            ScheduledTask.builder(taskFactory).displayName("scheduled:[periodic-persister]").tagTransient().period(period).delay(period).build() );
    }
}
 
Example #11
Source File: DependentConfiguration.java    From brooklyn-server with Apache License 2.0 6 votes vote down vote up
/** 
 * Method which returns a Future containing an escaped URL string (see {@link Urls#encode(String)}).
 * The arguments can be normal objects, tasks or {@link DeferredSupplier}s.
 * tasks will be waited on (submitted if necessary) and their results substituted.
 */
@SuppressWarnings("unchecked")
public static Task<String> urlEncode(final Object arg) {
    List<TaskAdaptable<Object>> taskArgs = Lists.newArrayList();
    if (arg instanceof TaskAdaptable) taskArgs.add((TaskAdaptable<Object>)arg);
    else if (arg instanceof TaskFactory) taskArgs.add( ((TaskFactory<TaskAdaptable<Object>>)arg).newTask() );
    
    return transformMultiple(
            MutableMap.<String,String>of("displayName", "url-escaping '"+arg), 
            new Function<List<Object>, String>() {
                @Override
                @Nullable
                public String apply(@Nullable List<Object> input) {
                    Object resolvedArg;
                    if (arg instanceof TaskAdaptable || arg instanceof TaskFactory) resolvedArg = Iterables.getOnlyElement(input);
                    else if (arg instanceof DeferredSupplier) resolvedArg = ((DeferredSupplier<?>) arg).get();
                    else resolvedArg = arg;
                    
                    if (resolvedArg == null) return null;
                    String resolvedString = resolvedArg.toString();
                    return Urls.encode(resolvedString);
                }
            },
            taskArgs);
}
 
Example #12
Source File: BrooklynTaskTags.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
public static void setEffectorParameters(Task<?> task, ConfigBag parameters) {
    EffectorCallTag result = getEffectorCallTag(task, true);
    if (result == null) {
        throw new IllegalStateException("No EffectorCallTag found, is the task an effector? Task: " + task);
    }
    result.setParameters(parameters);
}
 
Example #13
Source File: AbstractInvokeEffectorPolicy.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
/**
 * Invoke effector with parameters on the entity that the policy is attached to.
 */
protected <T> Task<T> invoke(Effector<T> effector, Map<String, ?> parameters) {
    if (isBusySensorEnabled()) {
        getTaskCounter().incrementAndGet();
        publishIsBusy();
    }
    Task<T> task = entity.invoke(effector, parameters);
    if (isBusySensorEnabled()) {
        task.addListener(new EffectorListener(), MoreExecutors.sameThreadExecutor());
    }
    return task;
}
 
Example #14
Source File: CompoundTaskExecutionTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
@Test
public void testSequentialTaskFailsWhenIntermediateTaskThrowsException() throws Exception {
    BasicTask<String> t1 = taskReturning("a");
    BasicTask<String> t2 = new BasicTask<String>(new Callable<String>() {
            @Override public String call() throws Exception {
                throw new IllegalArgumentException("forced exception");
            }
        });
    BasicTask<String> t3 = taskReturning("c");
    SequentialTask<String> task = new SequentialTask<String>(t1, t2, t3);
    Task<List<String>> tSequence = ec.submit(task);

    try {
        tSequence.get();
        fail("t2 should have thrown an exception");
    } catch (Exception e) {}

    assertTrue(task.isDone());
    assertTrue(task.isError());
    assertTrue(t1.isDone());
    assertFalse(t1.isError());
    assertTrue(t2.isDone());
    assertTrue(t2.isError());
    // t3 not run because of t2 exception
    assertFalse(t3.isDone());
    assertFalse(t3.isBegun());
}
 
Example #15
Source File: EffectorTasks.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
public static <T> EffectorTaskFactory<T> of(final Task<T> task) {
    return new EffectorTaskFactory<T>() {
        @Override
        public Task<T> newTask(Entity entity, Effector<T> effector, ConfigBag parameters) {
            return task;
        }
    };
}
 
Example #16
Source File: BrooklynDslCommon.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
@Override
public Task<Object> newTask() {
    return Tasks.<Object>builder()
        .displayName("resolving external configuration: '" + key + "' from provider '" + providerName + "'")
        .dynamic(false)
        .body(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                return getImmediately().get();
            }
        })
        .build();
}
 
Example #17
Source File: EntityExecutionManagerTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
public void testGetTasksAndGcBoringTags() throws Exception {
    TestEntity e = app.createAndManageChild(EntitySpec.create(TestEntity.class));
    
    final Task<?> task = runEmptyTaskWithNameAndTags(e, "should-be-kept", ManagementContextInternal.NON_TRANSIENT_TASK_TAG);
    runEmptyTaskWithNameAndTags(e, "should-be-gcd", ManagementContextInternal.TRANSIENT_TASK_TAG);
    
    assertImportantTaskCountForEntityEventually(e, 1);
    Collection<Task<?>> tasks = removeSystemTasks( BrooklynTaskTags.getTasksInEntityContext(app.getManagementContext().getExecutionManager(), e) );
    assertEquals(tasks, ImmutableList.of(task), "Mismatched tasks, got: "+tasks);
}
 
Example #18
Source File: DslDeferredFunctionCall.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
@Override
public Task<Object> newTask() {
    return Tasks.builder()
            .displayName("Deferred function call " + object + "." + toStringF(fnName, args))
            .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
            .dynamic(false)
            .body(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    return invokeOnDeferred(object, false).get();
                }

            }).build();
}
 
Example #19
Source File: Poller.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
public boolean isRunning() {
    boolean hasActiveTasks = false;
    for (Task<?> task: tasks) {
        if (task.isBegun() && !task.isDone()) {
            hasActiveTasks = true;
            break;
        }
    }
    if (!started && hasActiveTasks) {
        log.warn("Poller should not be running, but has active tasks, tasks: "+tasks);
    }
    return started && hasActiveTasks;
}
 
Example #20
Source File: ScheduledExecutionTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
/** like testScheduledTask but the loop is terminated by the task itself adjusting the period */
@Test
public void testScheduledTaskSelfEnding() throws Exception {
    int PERIOD = 20;
    BasicExecutionManager m = new BasicExecutionManager("mycontextid");
    final AtomicInteger i = new AtomicInteger(0);
    ScheduledTask t = new ScheduledTask(MutableMap.of("delay", 2*PERIOD, "period", PERIOD), new Callable<Task<?>>() {
        @Override
        public Task<?> call() throws Exception {
            return new BasicTask<Integer>(new Callable<Integer>() {
                @Override
                public Integer call() {
                    ScheduledTask submitter = (ScheduledTask) ((BasicTask)Tasks.current()).getSubmittedByTask();
                    if (i.get() >= 4) submitter.period = null;
                    log.info("task running ("+i+"): "+Tasks.current()+" "+Tasks.current().getStatusDetail(false));
                    return i.incrementAndGet();
                }});
        }});

    log.info("submitting {} {}", t, t.getStatusDetail(false));
    m.submit(t);
    log.info("submitted {} {}", t, t.getStatusDetail(false));
    Integer interimResult = (Integer) t.get();
    log.info("done one ({}) {} {}", new Object[] {interimResult, t, t.getStatusDetail(false)});
    assertTrue(i.get() > 0);
    t.blockUntilEnded();
    Integer finalResult = (Integer) t.get();
    log.info("ended ({}) {} {}", new Object[] {finalResult, t, t.getStatusDetail(false)});
    assertEquals(finalResult, (Integer)5);
    assertEquals(i.get(), 5);
}
 
Example #21
Source File: EffectorTaskTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
public static Task<Integer> times(final Task<Integer> x, final int y) {
    return TaskBuilder.<Integer>builder()
            .displayName("times")
            .body(new Callable<Integer>() {
                @Override public Integer call() { return DynamicTasks.get(x)*y; }
            }).build();
}
 
Example #22
Source File: TaskPredicatesTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
@Test
public void testDisplayNameSatisfies() throws Exception {
    Task<Object> task = execManager.submit(TaskBuilder.builder()
            .body(Callables.<Object>returning("val"))
            .displayName("myname")
            .build());
    assertTrue(TaskPredicates.displayNameSatisfies(Predicates.equalTo("myname")).apply(task));
    assertFalse(TaskPredicates.displayNameSatisfies(Predicates.equalTo("wrong")).apply(task));
}
 
Example #23
Source File: EffectorSayHiTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
@Test
public void testCanExcludeNonEffectorTasks() throws Exception {
    ExecutionContext executionContext = mgmt.getExecutionContext(e);
    executionContext.submit(new BasicTask<Void>(new Runnable() { @Override public void run() {} }));

    Set<Task<?>> effectTasks = mgmt.getExecutionManager().getTasksWithAllTags(ImmutableList.of(
            BrooklynTaskTags.tagForContextEntity(e),ManagementContextInternal.EFFECTOR_TAG));
    assertEquals(effectTasks.size(), 0);
}
 
Example #24
Source File: AbstractYamlRebindTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
protected void waitForApplicationTasks(Entity app) {
    Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(mgmt().getExecutionManager(), app);
    getLogger().info("Waiting on " + tasks.size() + " task(s)");
    for (Task<?> t : tasks) {
        t.blockUntilEnded();
    }
}
 
Example #25
Source File: TasksTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
@Test
public void testSingleExecutionContextEntityWithTask() {
    // Should cause an exception to be thrown in future releases. For now will log a warning.
    // Until then make sure the task is tagged only with the context of the executor.
    final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
    Task<Void> task = Tasks.<Void>builder()
        .tag(BrooklynTaskTags.tagForContextEntity(entity))
        .body(new AssertContextRunnable(ImmutableList.of(app))).build();
    app.getExecutionContext().submit(task).getUnchecked();
}
 
Example #26
Source File: EntityTasks.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
/** creates an (unsubmitted) task which waits for the attribute to satisfy the given predicate,
 * returning false if it times out or becomes unmanaged */
public static <T> Task<Boolean> testingAttributeEventually(Entity entity, AttributeSensor<T> sensor, Predicate<T> condition, Duration timeout) {
    return DependentConfiguration.builder().attributeWhenReady(entity, sensor)
        .readiness(condition)
        .postProcess(Functions.constant(true))
        .timeout(timeout)
        .onTimeoutReturn(false)
        .onUnmanagedReturn(false)
        .build();
}
 
Example #27
Source File: EffectorTaskTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
/** the composed effector should allow us to inspect its children */
@Test
public void testComposedEffectorBasic() throws Exception {
    Entity txp1 = app.createAndManageChild(EntitySpec.create(Entity.class, Txp1Entity.class));
    
    Task<Integer> e = txp1.invoke(TWO_X_PLUS_ONE_BASIC, MutableMap.of("numberToStartWith", 3));
    Assert.assertTrue(e instanceof DynamicSequentialTask);
    Assert.assertEquals(e.get(), (Integer)7);
    Assert.assertEquals( Iterables.size( ((HasTaskChildren)e).getChildren() ), 2);
}
 
Example #28
Source File: NonBasicTaskExecutionTest.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
@Test
public void runBasicTaskWithWaits() throws Exception {
    final CountDownLatch signalStarted = new CountDownLatch(1);
    final CountDownLatch allowCompletion = new CountDownLatch(1);
    final TaskInternal<Object> t = new ConcreteForwardingTask<Object>(new BasicTask<Object>(new Callable<Object>() {
        @Override public Object call() throws Exception {
            Object result = data.put(1, "b");
            signalStarted.countDown();
            assertTrue(allowCompletion.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
            return result;
        }}));
    data.put(1, "a");

    Task<?> t2 = em.submit(MutableMap.of("tag", "A"), t);
    assertEquals(t, t2);
    assertFalse(t.isDone());
    
    assertTrue(signalStarted.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
    assertEquals("b", data.get(1));
    assertFalse(t.isDone());
    
    log.debug("runBasicTaskWithWaits, BasicTask status: {}", t.getStatusDetail(false));
    
    Asserts.succeedsEventually(new Runnable() {
        @Override public void run() {
            t.getStatusDetail(false).toLowerCase().contains("waiting");
        }});
    // "details="+t.getStatusDetail(false))
    
    allowCompletion.countDown();
    assertEquals("a", t.get());
}
 
Example #29
Source File: OpenShiftEntityImpl.java    From SeaCloudsPlatform with Apache License 2.0 5 votes vote down vote up
/**
 * If custom behaviour is required by sub-classes, consider overriding {@link #doRestart()}.
 */
@Override
public final void restart() {
    if (DynamicTasks.getTaskQueuingContext() != null) {
        doRestart(ConfigBag.EMPTY);
    } else {
        Task<?> task = Tasks.builder().name("restart").body(new Runnable() {
            public void run() {
                doRestart(ConfigBag.EMPTY);
            }
        }).build();
        Entities.submit(this, task).getUnchecked();
    }
}
 
Example #30
Source File: DependentConfiguration.java    From brooklyn-server with Apache License 2.0 5 votes vote down vote up
public Task<V2> build() {
    List<Task<V>> tasks = MutableList.of();
    for (AttributeAndSensorCondition<?> source: multiSource) {
        builder.source(source.source);
        builder.sensor((AttributeSensor)source.sensor);
        builder.readiness((Predicate)source.predicate);
        tasks.add(builder.build());
    }
    final Task<List<V>> parallelTask = Tasks.<List<V>>builder().parallel(true).addAll(tasks)
        .displayName(name)
        .description(descriptionBase+
            (builder.timeout!=null ? ", timeout "+builder.timeout : ""))
        .build();
    
    if (postProcessFromMultiple == null) {
        // V2 should be the right type in normal operations
        return (Task<V2>) parallelTask;
    } else {
        return Tasks.<V2>builder().displayName(name).description(descriptionBase)
            .tag("attributeWhenReady")
            .body(new Callable<V2>() {
                @Override public V2 call() throws Exception {
                    List<V> prePostProgress = DynamicTasks.queue(parallelTask).get();
                    return DynamicTasks.queue(
                        Tasks.<V2>builder().displayName("post-processing").description("Applying "+postProcessFromMultiple)
                            .body(Functionals.callable(postProcessFromMultiple, prePostProgress))
                            .build()).get();
                }
            })
            .build();
    }
}