package er.extensions.concurrency;
import java.util.concurrent.ExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSMutableArray;
import er.extensions.foundation.ERXStopWatch;
/**
* This is the custom {@link Thread} subclass that is used for running background tasks.
* This {@link Thread} subclass is automatically created by the {@link ERXTaskThreadFactory}
* which in turn is used by the {@link ERXTaskThreadPoolExecutor}
*
* The purpose of this subclass is
* <ul>
* <li> to identify threads that were created by {@link ERXTaskThreadFactory}
* using instanceof when enumerating all threads
* <li> to store a reference to the task itself in this Thread subclass while the task is executing
* making it easy to get a reference to tasks for application monitoring
* <li> provide related static utility methods to find all tasks or tasks of a certain class that are currently running and that were
* created by {@link ERXTaskThreadFactory}
* </ul>
*
* <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>
*
* @author kieran
*
* @see ERXTaskThreadPoolExecutor
* @see ERXExecutorService
* @see ERXTaskThreadFactory
*/
public class ERXTaskThread extends Thread {
private static final Logger log = LoggerFactory.getLogger(ERXTaskThread.class);
public ERXTaskThread(Runnable target) {
super(target);
}
private Runnable _task;
private ERXStopWatch _stopWatch;
/** @return the current task being executed */
public Runnable task() {
return _task;
}
/**
* @param task the current task being executed
*
* TODO: Check if the Runnable is a Future wrapping the real task and unwrap it. */
public void setTask(Runnable task){
_task = task;
}
/**
* @return NSArray of background tasks
*/
public static NSArray tasks() {
NSMutableArray tasks = new NSMutableArray();
Thread threads[] = new Thread[Thread.activeCount()];
Thread.enumerate(threads);
for (int i = 0; i < threads.length; i++) {
Thread thread = threads[i];
if (thread instanceof ERXTaskThread) {
Runnable task = ((ERXTaskThread)thread).task();
if (task != null) {
tasks.add(task);
} //~ if (task != null)
} //~ if (thread instanceof ERXTaskThread)
}
return tasks.immutableClone();
}
/**
* @return NSArray of {@link ERXTaskInfo}
*/
public static NSArray taskInfos() {
NSMutableArray taskInfos = new NSMutableArray();
Thread threads[] = new Thread[Thread.activeCount()];
Thread.enumerate(threads);
for (int i = 0; i < threads.length; i++) {
Thread thread = threads[i];
if (thread instanceof ERXTaskThread) {
Runnable task = ((ERXTaskThread)thread).task();
if (task != null) {
String elapsedTime = ((ERXTaskThread)thread).elapsedTime();
ERXTaskInfo info = new ERXTaskInfo(task, elapsedTime);
taskInfos.add(info);
} //~ if (task != null)
} //~ if (thread instanceof ERXTaskThread)
}
return taskInfos.immutableClone();
}
@SuppressWarnings("unchecked")
public static <T> NSArray<T> taskForTaskClass(Class<T> clazz) {
NSArray<ERXTaskInfo> taskInfos = taskInfos();
NSMutableArray<T> tasks = new NSMutableArray<>();
for (ERXTaskInfo taskInfo : taskInfos) {
Object r = taskInfo.task();
log.debug("ERXTaskThread.taskForTaskClass(): r = {}", r);
if (clazz.isInstance(r)) {
tasks.add((T)r);
}
}
return tasks.immutableClone();
}
public void startStopWatch() {
_stopWatch = new ERXStopWatch();
_stopWatch.start();
}
public String elapsedTime() {
return (_stopWatch == null ? null : _stopWatch.toString());
}
public void stopStopWatch() {
if (_stopWatch != null) {
_stopWatch.stop();
} //~ if (_stopWatch != null)
}
}