package com.venky.swf.plugins.background.core;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import com.venky.core.util.Bucket;
import com.venky.swf.plugins.background.core.Task.Priority;
import com.venky.swf.plugins.background.core.workers.DelayedTaskManager;
import com.venky.swf.plugins.background.db.model.DelayedTask;
import com.venky.swf.routing.Config;
public abstract class AsyncTaskManager<T extends Task & Comparable<? super T>> {
@SuppressWarnings("rawtypes")
private static Map<Class, AsyncTaskManager> _instance = new HashMap<Class,AsyncTaskManager>();
@SuppressWarnings("unchecked")
public static <T extends Task & Comparable<? super T>> AsyncTaskManager<T> getInstance(Class<T> taskClass) {
AsyncTaskManager<T> tm = _instance.get(taskClass);
if (tm == null) {
synchronized (_instance) {
tm = _instance.get(taskClass);
if (tm == null) {
tm = buldTaskManager(taskClass);
_instance.put(taskClass, tm);
}
}
}
return tm;
}
@SuppressWarnings("unchecked")
private static <T extends Task & Comparable<? super T>> AsyncTaskManager<T> buldTaskManager(Class<T> taskClass) {
if (DelayedTask.class.isAssignableFrom(taskClass)) {
return (AsyncTaskManager<T>) new DelayedTaskManager();
} else if (TaskHolder.class.isAssignableFrom(taskClass)) {
return (AsyncTaskManager<T>) new AsyncTaskManager<TaskHolder>(){
@Override
protected void pushAsyncTasks(Collection<Task> tasks, Priority priority) {
List<TaskHolder> taskHolders = new LinkedList<TaskHolder>();
for (Task task : tasks){
taskHolders.add(new TaskHolder(task, priority));
}
addAll(taskHolders);
}
};
} else {
throw new RuntimeException("Don't know how tp build TaskManager for " + taskClass.getName());
}
}
protected AsyncTaskManager() {
this(getInitialNumWorkerThreads());
}
protected AsyncTaskManager(int numWorkers) {
queue();
for (int i = 0; i < numWorkers; i++) {
addWorker();
}
}
public void addWorker(){
AsyncTaskWorker<T> worker = null;
incrementWorkerCount();
worker = createWorker();
workerThreads.add(worker);
worker.start();
}
public void evictWorker(int number){
synchronized (numWorkersToEvict) {
numWorkersToEvict.increment(number);
numWorkersToEvict.notifyAll();
}
wakeUp();
}
Bucket numWorkersToEvict = new Bucket();
public boolean evicted(){
synchronized (numWorkersToEvict) {
if (numWorkersToEvict.intValue() > 0 && Thread.currentThread() instanceof AsyncTaskWorker<?>){
if (workerThreads.remove(Thread.currentThread())){
numWorkersToEvict.decrement();
}
numWorkersToEvict.notifyAll();
return true;
}
return false;
}
}
public static final int getInitialNumWorkerThreads() {
return Config.instance().getIntProperty("swf.plugins.background.core.workers.numThreads", 1);
}
private AtomicInteger instanceNumber = new AtomicInteger();
public AsyncTaskWorker<T> createWorker() {
return new AsyncTaskWorker<T>(this, instanceNumber.incrementAndGet());
}
private List<AsyncTaskWorker<T>> workerThreads = new Vector<>();
public int getNumWorkers(){
return workerThreads.size();
}
private Queue<T> queue = null;
protected Queue<T> queue() {
if (queue != null) {
return queue;
}
synchronized (this) {
if (queue == null)
queue = new PriorityQueue<T>();
}
return queue;
}
public static class ShutdownInitiatedException extends RuntimeException {
private static final long serialVersionUID = -8216421138960049897L;
}
public void addAll(Collection<T> tasks) {
if (tasks.isEmpty()) {
return;
}
synchronized (queue) {
if (!keepAlive()) {
throw new ShutdownInitiatedException();
}
queue.addAll(tasks);
queue.notifyAll();
}
}
protected boolean keepAlive() {
synchronized (queue) {
if (shutdown) {
return false;
} else {
return true;
}
}
}
private boolean shutdown = false;
public void shutdown() {
while (true) {
Config.instance().getLogger(getClass().getName()).info("Waiting for all Threads to shutdown");
evictWorker(workerThreads.size());
waitUntilWorkersAreEvicted();
break;
}
synchronized (queue) {
shutdown = true;
queue.notifyAll();
}
}
public T waitUntilNextTask() {
T dt = null;
synchronized (queue) {
while (!evicted() && keepAlive() && queue.isEmpty() ) {
try {
Config.instance().getLogger(getClass().getName())
.finest("Worker: going back to sleep as there is no work to be done.");
//queue.wait(30*1000);
queue.wait();
} catch (InterruptedException ex) {
Config.instance().getLogger(getClass().getName()).finest("Worker: waking up to look for work.");
//
}
}
if (!evicted() && keepAlive() && !queue.isEmpty() ) {
dt = queue.poll();
Config.instance().getLogger(getClass().getName())
.finest("Number of Tasks remaining in Queue pending workers:" + queue.size());
queue.notifyAll();
}
}
return dt;
}
public T next() {
decrementWorkerCount();
T task = waitUntilNextTask();
if (task != null) {
incrementWorkerCount();
}
return task;
}
private Bucket numWorkersWorking = new Bucket();
public void decrementWorkerCount() {
synchronized (numWorkersWorking) {
numWorkersWorking.decrement();
numWorkersWorking.notifyAll();
Config.instance().getLogger(getClass().getName())
.info("Number of workers working " + numWorkersWorking.intValue());
}
}
public void incrementWorkerCount() {
synchronized (numWorkersWorking) {
numWorkersWorking.increment();
numWorkersWorking.notifyAll();
Config.instance().getLogger(getClass().getName())
.info("Number of workers working " + numWorkersWorking.intValue());
}
}
public void waitUntilWorkersFinish() {
synchronized (numWorkersWorking) {
while (numWorkersWorking.intValue() != 0) {
try {
Config.instance().getLogger(getClass().getName())
.info("Number of workers working " + numWorkersWorking.intValue());
numWorkersWorking.wait();
} catch (InterruptedException ex) {
}
}
}
}
public void waitUntilWorkersAreEvicted() {
synchronized (numWorkersToEvict) {
while (numWorkersToEvict.intValue() != 0){
try {
numWorkersToEvict.wait(5000);
} catch (InterruptedException e) {
}
}
}
}
public void wakeUp() {
synchronized (queue) {
queue.notifyAll();
Config.instance().getLogger(getClass().getName()).finest("Waking up Daemon.");
}
}
public void waitIfQueueIsEmpty(int seconds){
synchronized (queue) {
if (queue.isEmpty() && keepAlive()) {
try {
queue.wait(seconds * 1000);
} catch (InterruptedException e) {
//
}
}
}
}
public void waitUntilQueueIsEmpty() {
synchronized (queue) {
int initialSize = queue.size();
while (!queue.isEmpty()) {
try {
Config.instance().getLogger(getClass().getName())
.finest("Daemon going back to sleep as there are pending tasks still to be completed.");
queue.wait();
} catch (InterruptedException ex) {
Config.instance().getLogger(getClass().getName())
.finest("Daemon woke up to check if all tasks are complete.");
}
int newSize = queue.size();
if (newSize == initialSize) {
// Why was my sleep distubed. May be I am to kill myself!!
if (!keepAlive()) {
break;
}
} else {
initialSize = newSize;
}
}
}
}
public void execute(Task task, Priority priority) {
execute(Arrays.asList(task),priority);
}
public void execute(Collection<Task> tasks, Priority priority) {
if (getInitialNumWorkerThreads() == 0) {
for (Task task:tasks) {
task.execute();
}
} else {
pushAsyncTasks(tasks, priority);
}
}
protected abstract void pushAsyncTasks(Collection<Task> task, Priority priority) ;
public static void shutdownAll() {
AsyncTaskManager.getInstance(TaskHolder.class).shutdown();
AsyncTaskManager.getInstance(DelayedTask.class).shutdown();
}
public static void wakeUpAll() {
AsyncTaskManager.getInstance(TaskHolder.class).wakeUp();
AsyncTaskManager.getInstance(DelayedTask.class).wakeUp();
}
}