
org.dihedron.patterns.concurrent.TaskExecutor Maven / Gradle / Ivy
/**
* Copyright (c) 2012-2014, Andrea Funto'. All rights reserved. See LICENSE for details.
*/
package org.dihedron.patterns.concurrent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import org.dihedron.core.License;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The engine that takes care of executing the tasks asynchronously (in parallel),
* waiting for their completion and optionally notifying observers of their
* advancement.
*
* @author Andrea Funto'
*/
@License
public class TaskExecutor {
/**
* The logger.
*/
private static final Logger logger = LoggerFactory.getLogger(TaskExecutor.class);
/**
* The actual executor service.
*/
private ExecutorService executor;
/**
* The queue used to synchronise task execution.
*/
private BlockingQueue queue;
/**
* The list of tasks being executed.
*/
private List> tasks = new ArrayList>();
/**
* The (optional) list of task observers.
*/
private List> observers = new ArrayList>();
/**
* Constructor.
*
* @param executor
* the actual executor service.
* @param observers
* an optional list of observers that will be notified of the several phases
* in a task execution life cycle.
*/
@SafeVarargs
public TaskExecutor(ExecutorService executor, TaskObserver... observers) {
this.executor = executor;
this.queue = new LinkedBlockingQueue();
addObservers(observers);
}
/**
* Adds task observers to the set of registered observers.
*
* @param observers
* an list of observers that will be notified of the several phases in a
* task execution life cycle.
* @return
* the object itself, to enable method chaining.
*/
@SafeVarargs
public final TaskExecutor addObservers(TaskObserver... observers) {
if(observers != null) {
for(TaskObserver observer : observers) {
this.observers.add(observer);
}
}
return this;
}
/**
* Removes all registered observers.
*
* @return
* the object itself, to enable method chaining.
*/
public TaskExecutor clearObservers() {
this.observers.clear();
return this;
}
/**
* Starts the given set of tasks asynchronously, returning their futures.
*
* @param tasks
* the set of tasks to execute.
* @return
* the corresponding set of futures.
*/
public List> execute(@SuppressWarnings("unchecked") Task ... tasks) {
List> futures = new ArrayList>();
if(tasks != null) {
synchronized(queue) {
int i = 0;
for(Task task : tasks) {
if(task != null) {
this.tasks.add(task);
TaskCallable callable = new TaskCallable(i++, queue, task);
for(TaskObserver observer : observers) {
observer.onTaskStarting(task);
}
futures.add(executor.submit(callable));
for(TaskObserver observer : observers) {
observer.onTaskStarted(task);
}
}
}
}
}
return futures;
}
/**
* Starts the given set of tasks asynchronously, returning their futures.
*
* @param tasks
* the set of tasks to execute.
* @return
* the corresponding set of futures.
*/
public List> execute(List> tasks) {
List> futures = new ArrayList>();
synchronized(queue) {
int i = 0;
for(Task task : tasks) {
if(task != null) {
this.tasks.add(task);
TaskCallable callable = new TaskCallable(i++, queue, task);
for(TaskObserver observer : observers) {
observer.onTaskStarting(task);
}
futures.add(executor.submit(callable));
for(TaskObserver observer : observers) {
observer.onTaskStarted(task);
}
}
}
}
return futures;
}
/**
* Waits for all tasks to complete before returning; if observers are provided,
* they are called each time a task completes.
*
* @param futures
* the list of futures to wait for.
* @param observers
* an optional set of observers.
* @return
* the list of results, once all tasks are done.
* @throws InterruptedException
* @throws ExecutionException
*/
public List waitForAll(List> futures) throws InterruptedException, ExecutionException {
Map results = new HashMap();
int count = futures.size();
while(count-- > 0) {
int id = queue.take();
logger.trace("task '{}' complete (count: {}, queue: {})", id, count, queue.size());
T result = futures.get(id).get();
results.put(id, result);
for(TaskObserver observer : observers) {
observer.onTaskComplete(tasks.get(id), result);
}
}
logger.debug("all tasks completed");
List values = new ArrayList();
for(int i = 0; i < results.size(); ++i) {
values.add(results.get(i));
}
return values;
}
/**
* Waits until the first task completes, then calls the (optional) observers
* to notify the completion and returns the result.
*
* @param futures
* the list of futures to wait for.
* @param observers
* an optional set of observers.
* @return
* the result of the first task to complete.
* @throws InterruptedException
* @throws ExecutionException
*/
public T waitForAny(List> futures, @SuppressWarnings("unchecked") TaskObserver... observers) throws InterruptedException, ExecutionException {
int count = futures.size();
while(count-- > 0) {
int id = queue.take();
logger.debug("task '{}' complete (count: {}, queue: {})", id, count, queue.size());
T result = futures.get(id).get();
for(TaskObserver observer : observers) {
observer.onTaskComplete(tasks.get(id), result);
}
return result;
}
return null;
}
/**
* Closes the task executor, releasing all associated resources.
*/
public void dispose() {
executor.shutdown();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy