All Downloads are FREE. Search and download functionalities are using the official Maven repository.

brooklyn.util.task.DynamicTasks Maven / Gradle / Ivy

There is a newer version: 0.7.0-M1
Show newest version
package brooklyn.util.task;

import java.util.List;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import brooklyn.management.Task;
import brooklyn.management.TaskAdaptable;
import brooklyn.management.TaskFactory;
import brooklyn.management.TaskQueueingContext;
import brooklyn.management.TaskWrapper;
import brooklyn.util.exceptions.Exceptions;

import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;

/** 
 * Contains static methods which detect and use the current {@link TaskQueueingContext} to execute tasks.
 * 
 * @since 0.6.0
 */
@Beta
public class DynamicTasks {

    private static final Logger log = LoggerFactory.getLogger(DynamicTasks.class);
    private static final ThreadLocal taskQueueingContext = new ThreadLocal();
    
    public static void setTaskQueueingContext(TaskQueueingContext newTaskQC) {
        taskQueueingContext.set(newTaskQC);
    }
    
    public static TaskQueueingContext getThreadTaskQueuingContext() {
        return taskQueueingContext.get();
    }
    
    public static TaskQueueingContext getTaskQueuingContext() {
        TaskQueueingContext adder = getThreadTaskQueuingContext();
        if (adder!=null) return adder;
        Task t = Tasks.current();
        if (t instanceof TaskQueueingContext) return (TaskQueueingContext) t;
        return null;
    }

    
    public static void removeTaskQueueingContext() {
        taskQueueingContext.remove();
    }

    public static class TaskQueueingResult implements TaskWrapper {
        private final Task task;
        private final boolean wasQueued;
        
        private TaskQueueingResult(TaskAdaptable task, boolean wasQueued) {
            this.task = task.asTask();
            this.wasQueued = wasQueued;
        }
        @Override
        public Task asTask() {
            return task;
        }
        @Override
        public Task getTask() {
            return task;
        }
        /** returns true if the task was queued */
        public boolean wasQueued() {
            return wasQueued;
        }
        /** returns true if the task either is currently queued or has been submitted */
        public boolean isQueuedOrSubmitted() {
            return wasQueued || Tasks.isQueuedOrSubmitted(task);
        }
        private boolean orSubmitInternal() {
            if (!wasQueued()) {
                if (isQueuedOrSubmitted()) {
                    log.warn("Redundant call to execute "+getTask()+"; skipping");
                    return false;
                } else {
                    BasicExecutionContext ec = BasicExecutionContext.getCurrentExecutionContext();
                    if (ec==null)
                        throw new IllegalStateException("Cannot execute "+getTask()+" without an execution context; ensure caller is in an ExecutionContext");
                    ec.submit(getTask());
                    return true;
                }
            } else {
                return false;
            }
        }
        /** causes the task to be submitted (asynchronously) if it hasn't already been */
        public TaskQueueingResult orSubmitAsync() {
            orSubmitInternal();
            return this;
        }
        /** causes the task to be submitted *synchronously* if it hasn't already been submitted;
         * useful in contexts such as libraries where callers may be either on a legacy call path 
         * (which assumes all commands complete immediately)
         *  */
        public TaskQueueingResult orSubmitAndBlock() {
            if (orSubmitInternal()) task.getUnchecked();
            return this;
        }
        /** blocks for the task to be completed
         * 

* needed in any context where subsequent commands assume the task has completed. * not needed in a context where the task is simply being built up and queued. *

* throws if there are any errors */ public void andWaitForSuccess() { task.getUnchecked(); } } /** tries to add the task to the current addition context if there is one, otherwise does nothing */ public static TaskQueueingResult queueIfPossible(TaskAdaptable task) { TaskQueueingContext adder = getTaskQueuingContext(); boolean result = false; if (adder!=null) result = Tasks.tryQueueing(adder, task); return new TaskQueueingResult(task, result); } public static TaskQueueingResult queueIfPossible(TaskFactory> task) { return queueIfPossible(task.newTask()); } /** adds the given task to the nearest task addition context, * either set as a thread-local, or in the current task, or the submitter of the task, etc *

* throws if it cannot add */ public static Task queueInTaskHierarchy(Task task) { Preconditions.checkNotNull(task, "Task to queue cannot be null"); Preconditions.checkState(!Tasks.isQueuedOrSubmitted(task), "Task to queue must not yet be submitted: {}", task); TaskQueueingContext adder = getTaskQueuingContext(); if (adder!=null) { if (Tasks.tryQueueing(adder, task)) { log.debug("Queued task {} at context {} (no hierarchy)", task, adder); return task; } } Task t = Tasks.current(); Preconditions.checkState(t!=null || adder!=null, "No task addition context available for queueing task "+task); while (t!=null) { if (t instanceof TaskQueueingContext) { if (Tasks.tryQueueing((TaskQueueingContext)t, task)) { log.debug("Queued task {} at hierarchical context {}", task, t); return task; } } t = t.getSubmittedByTask(); } throw new IllegalStateException("No task addition context available in current task hierarchy for adding task "+task); } public static > V queue(V task) { try { Preconditions.checkNotNull(task, "Task to queue cannot be null"); Preconditions.checkState(!Tasks.isQueuedOrSubmitted(task), "Task to queue must not yet be submitted: %s", task); TaskQueueingContext adder = getTaskQueuingContext(); if (adder==null) throw new IllegalStateException("Task "+task+" cannot be queued here; no queueing context available"); adder.queue(task.asTask()); return task; } catch (Throwable e) { log.warn("Error queueing "+task+" (rethrowing): "+e); throw Exceptions.propagate(e); } } public static void queue(TaskAdaptable task1, TaskAdaptable task2, TaskAdaptable ...tasks) { queue(task1); queue(task2); for (TaskAdaptable task: tasks) queue(task); } public static > T queue(TaskFactory taskFactory) { return queue(taskFactory.newTask()); } public static void queue(TaskFactory task1, TaskFactory task2, TaskFactory ...tasks) { queue(task1.newTask()); queue(task2.newTask()); for (TaskFactory task: tasks) queue(task.newTask()); } public static Task queue(String name, Callable job) { return DynamicTasks.queue(Tasks.builder().name(name).body(job).build()); } public static Task queue(String name, Runnable job) { return DynamicTasks.queue(Tasks.builder().name(name).body(job).build()); } public static > T queueIfNeeded(T task) { if (!Tasks.isQueuedOrSubmitted(task)) queue(task); return task; } /** submits the given task if needed, and gets the result (unchecked) * only permitted in a queueing context (ie a DST main job) */ // things get really confusing if you try to queueInTaskHierarchy -- easy to cause deadlocks! public static T get(TaskAdaptable t) { return queueIfNeeded(t).asTask().getUnchecked(); } /** Waits for the last task queued in this context to complete; * it throws if there is a problem, but happily returns null if there is no last task. *

* Preferred over {@link #last()}.get() because this waits on all tasks, * in sequentially (so that blocking information is always accurate) */ public static Task waitForLast() { TaskQueueingContext qc = DynamicTasks.getTaskQueuingContext(); Preconditions.checkNotNull(qc, "Cannot wait when their is no queueing context"); List> q = qc.getQueue(); Task last = null; do { for (Task t: q) { last = t; last.getUnchecked(); } } while (last!=qc.last()); return last; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy