package er.extensions.concurrency;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import er.extensions.eof.ERXEC;
/**
* This is a custom {@link ThreadPoolExecutor} subclass whose purpose in life is
* <ul>
* <li>to ensure that we initialize {@link ERXTaskThread} status before task execution and reset status after execution,
* <li>use ERXFutureTask subclass of {@link FutureTask} so we have a reference to the wrapped task.
* <li>tell ERXEC to unlock all editing contexts in the background thread at the end of task execution.
* </ul>
*
* <p>
* This is accomplished by overriding the protected hook methods {@link ThreadPoolExecutor#beforeExecute(Thread t, Runnable r)}
* and {@link ThreadPoolExecutor#afterExecute(Runnable r, Throwable t)}, and also the submit methods.
* </p>
*
* <p>
* A user does not generally need to instantiate this class. This class is generally used by {@link ExecutorService} instances
* that are created by {@link ERXExecutorService} static utility methods.
* </p>
*
* @see ERXExecutorService
* @see ERXTaskThreadFactory
* @see ERXTaskThread
*
* @author kieran
*/
public class ERXTaskThreadPoolExecutor extends ThreadPoolExecutor {
private static final Logger log = LoggerFactory.getLogger(ERXTaskThreadPoolExecutor.class);
public ERXTaskThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public ERXTaskThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public ERXTaskThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public ERXTaskThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
ERXFutureTask<Object> ftask = new ERXFutureTask<>(task, null);
execute(ftask);
return ftask;
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
ERXFutureTask<T> ftask = new ERXFutureTask<>(task, result);
execute(ftask);
return ftask;
}
@Override
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
ERXFutureTask<T> ftask = new ERXFutureTask<>(task);
execute(ftask);
return ftask;
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
// Store reference to the task
if (t instanceof ERXTaskThread) {
((ERXTaskThread)t).setTask(r);
((ERXTaskThread)t).startStopWatch();
log.debug("About to execute {} in thread {}", r, t);
}
if (r instanceof IERXExecutionStateTransition) {
((IERXExecutionStateTransition)r).beforeExecute();
} //~ if (r instanceof IERXExecutionStateTransition)
super.beforeExecute(t, r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
// Clear reference to the task
if (Thread.currentThread() instanceof ERXTaskThread) {
ERXTaskThread thread = (ERXTaskThread)Thread.currentThread();
thread.setTask(null);
thread.stopStopWatch();
if (log.isDebugEnabled()) {
String elapsedTime = thread.elapsedTime();
log.debug("Finished executing {} after {}", r, elapsedTime);
}
}
if (r instanceof IERXExecutionStateTransition) {
((IERXExecutionStateTransition)r).afterExecute();
} //~ if (r instanceof IERXExecutionStateTransition)
if (shouldUnlockContexts(r)) {
// Safety net to unlock any locked EC's at the end of this task's operation in this thread
ERXEC.unlockAllContextsForCurrentThread();
}
}
private boolean shouldUnlockContexts(Runnable r) {
if (r instanceof ERXFutureTask) {
Object task = ((ERXFutureTask)r).task();
if (task instanceof ERXTask || task instanceof ERXTimerTask) {
// these two classes already call ERXApplication._endRequest() at the end
return false;
}
}
return true;
}
}