/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.util.core.task;
import groovy.lang.Closure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import org.apache.brooklyn.api.mgmt.HasTaskChildren;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.mgmt.TaskAdaptable;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
import org.apache.brooklyn.util.collections.MutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A {@link Task} that is comprised of other units of work: possibly a heterogeneous mix of {@link Task},
* {@link Runnable}, {@link Callable} and {@link Closure} instances.
*
* This class holds the collection of child tasks, but subclasses have the responsibility of executing them in a
* sensible manner by implementing the abstract {@link #runJobs} method.
*/
public abstract class CompoundTask<T> extends BasicTask<List<T>> implements HasTaskChildren {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(CompoundTask.class);
protected final List<Task<? extends T>> children;
protected final List<Object> result;
/**
* Constructs a new compound task containing the specified units of work.
*
* @param jobs A potentially heterogeneous mixture of {@link Runnable}, {@link Callable}, {@link Closure} and {@link Task} can be provided.
* @throws IllegalArgumentException if any of the passed child jobs is not one of the above types
*/
public CompoundTask(Object... jobs) {
this( Arrays.asList(jobs) );
}
/**
* Constructs a new compound task containing the specified units of work.
*
* @param jobs A potentially heterogeneous mixture of {@link Runnable}, {@link Callable}, {@link Closure} and {@link Task} can be provided.
* @throws IllegalArgumentException if any of the passed child jobs is not one of the above types
*/
public CompoundTask(Collection<?> jobs) {
this(MutableMap.of("tag", "compound"), jobs);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public CompoundTask(Map<String,?> flags, Collection<?> jobs) {
super(flags);
super.job = new Callable<List<T>>() {
@Override public List<T> call() throws Exception {
return runJobs();
}
};
this.result = new ArrayList<Object>(jobs.size());
this.children = new ArrayList<Task<? extends T>>(jobs.size());
for (Object job : jobs) {
Task subtask;
if (job instanceof TaskAdaptable) { subtask = ((TaskAdaptable)job).asTask(); }
else if (job instanceof Closure) { subtask = new BasicTask<T>((Closure) job); }
else if (job instanceof Callable) { subtask = new BasicTask<T>((Callable) job); }
else if (job instanceof Runnable) { subtask = new BasicTask<T>((Runnable) job); }
else throw new IllegalArgumentException("Invalid child "+(job == null ? null : job.getClass() + " ("+job+")")+
" passed to compound task; must be Runnable, Callable, Closure or Task");
BrooklynTaskTags.addTagDynamically(subtask, ManagementContextInternal.SUB_TASK_TAG);
children.add(subtask);
}
for (Task<?> t: getChildren()) {
((TaskInternal<?>)t).markQueued();
}
}
/** return value needs to be specified by subclass; subclass should also setBlockingDetails
* @throws ExecutionException
* @throws InterruptedException */
protected abstract List<T> runJobs() throws InterruptedException, ExecutionException;
protected void submitIfNecessary(TaskAdaptable<?> task) {
if (!task.asTask().isSubmitted()) {
if (BasicExecutionContext.getCurrentExecutionContext() == null) {
throw new IllegalStateException("Compound task ("+task+") launched from "+this+" missing required execution context");
} else {
BasicExecutionContext.getCurrentExecutionContext().submit(task);
}
}
}
public List<Task<? extends T>> getChildrenTyped() {
return children;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<Task<?>> getChildren() {
return (List) getChildrenTyped();
}
}