/* * 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 java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.api.mgmt.TaskAdaptable; import org.apache.brooklyn.api.mgmt.TaskFactory; import org.apache.brooklyn.api.mgmt.TaskQueueingContext; import org.apache.brooklyn.util.JavaGroovyEquivalents; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.collections.MutableSet; import com.google.common.collect.Iterables; /** Convenience for creating tasks; note that DynamicSequentialTask is the default */ public class TaskBuilder<T> { String displayName = null; String description = null; Callable<T> body = null; Boolean swallowChildrenFailures = null; List<TaskAdaptable<?>> children = MutableList.of(); Set<Object> tags = MutableSet.of(); Map<String,Object> flags = MutableMap.of(); Boolean dynamic = null; boolean parallel = false; public static <T> TaskBuilder<T> builder() { return new TaskBuilder<T>(); } /** * @deprecated since 0.8.0; use {@link #displayName(String)} */ @Deprecated public TaskBuilder<T> name(String name) { return displayName(name); } public TaskBuilder<T> displayName(String displayName) { this.displayName = displayName; return this; } public TaskBuilder<T> description(String description) { this.description = description; return this; } /** whether task that is built has been explicitly specified to be a dynamic task * (ie a Task which is also a {@link TaskQueueingContext} * whereby new tasks can be added after creation */ public TaskBuilder<T> dynamic(boolean dynamic) { this.dynamic = dynamic; return this; } /** whether task that is built should be parallel; cannot (currently) also be dynamic */ public TaskBuilder<T> parallel(boolean parallel) { this.parallel = parallel; return this; } public TaskBuilder<T> body(Callable<T> body) { this.body = body; return this; } /** sets up a dynamic task not to fail even if children fail */ public TaskBuilder<T> swallowChildrenFailures(boolean swallowChildrenFailures) { this.swallowChildrenFailures = swallowChildrenFailures; return this; } public TaskBuilder<T> body(Runnable body) { this.body = JavaGroovyEquivalents.<T>toCallable(body); return this; } /** adds a child to the given task; the semantics of how the child is executed is set using * {@link #dynamic(boolean)} and {@link #parallel(boolean)} */ public TaskBuilder<T> add(TaskAdaptable<?> child) { children.add(child); return this; } public TaskBuilder<T> addAll(Iterable<? extends TaskAdaptable<?>> additionalChildren) { Iterables.addAll(children, additionalChildren); return this; } public TaskBuilder<T> add(TaskAdaptable<?>... additionalChildren) { children.addAll(Arrays.asList(additionalChildren)); return this; } /** adds a tag to the given task */ public TaskBuilder<T> tag(Object tag) { tags.add(tag); return this; } /** adds a flag to the given task */ public TaskBuilder<T> flag(String flag, Object value) { flags.put(flag, value); return this; } /** adds the given flags to the given task */ public TaskBuilder<T> flags(Map<String,Object> flags) { this.flags.putAll(flags); return this; } @SuppressWarnings({ "unchecked", "rawtypes" }) public Task<T> build() { MutableMap<String, Object> taskFlags = MutableMap.copyOf(flags); if (displayName!=null) taskFlags.put("displayName", displayName); if (description!=null) taskFlags.put("description", description); if (!tags.isEmpty()) taskFlags.put("tags", tags); if (Boolean.FALSE.equals(dynamic) && children.isEmpty()) { if (swallowChildrenFailures!=null) throw new IllegalArgumentException("Cannot set swallowChildrenFailures for non-dynamic task: "+this); return new BasicTask<T>(taskFlags, body); } // prefer dynamic set unless (a) user has said not dynamic, or (b) it's parallel (since there is no dynamic parallel yet) // dynamic has better cancel (will interrupt the thread) and callers can submit tasks flexibly; // however dynamic uses an extra thread and task and is noisy for contexts which don't need it if (Boolean.TRUE.equals(dynamic) || (dynamic==null && !parallel)) { if (parallel) throw new UnsupportedOperationException("No implementation of parallel dynamic aggregate task available"); DynamicSequentialTask<T> result = new DynamicSequentialTask<T>(taskFlags, body); if (swallowChildrenFailures!=null && swallowChildrenFailures.booleanValue()) result.swallowChildrenFailures(); for (TaskAdaptable t: children) result.queue(t.asTask()); return result; } // T must be of type List<V> for these to be valid if (body != null) { throw new UnsupportedOperationException("No implementation of non-dynamic task with both body and children"); } if (swallowChildrenFailures!=null) { throw new IllegalArgumentException("Cannot set swallowChildrenFailures for non-dynamic task: "+this); } if (parallel) return new ParallelTask(taskFlags, children); else return new SequentialTask(taskFlags, children); } /** returns a a factory based on this builder */ public TaskFactory<Task<T>> buildFactory() { return new TaskFactory<Task<T>>() { public Task<T> newTask() { return build(); } }; } @Override public String toString() { return super.toString()+"["+displayName+"]"; } }