/*
* (c) Rob Gordon 2005
*/
package org.oddjob.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.log4j.Logger;
import org.oddjob.FailedToStopException;
import org.oddjob.Stoppable;
/**
* The thread manager keeps track of active threads. It can be used to
* ensure that all threads are complete before a job terminates.
*/
public class SimpleThreadManager implements ThreadManager {
private static final Logger logger = Logger.getLogger(SimpleThreadManager.class);
/** Map of active threads to their description. */
private final Map<Runnable, Remember> active =
new HashMap<Runnable, Remember>();
private final ExecutorService executors;
/**
* Default Constructor. Uses a default ExecutorService.
*/
public SimpleThreadManager() {
this(Executors.newCachedThreadPool());
}
/**
* Constructor uses provided ExecutorService.
*
* @param executors
*/
public SimpleThreadManager(ExecutorService executors) {
this.executors = executors;
}
/**
* Run a job.
*
* @param runnable The job.
* @param description The description of the job.
*/
public void run(final Runnable runnable, final String description) {
Runnable wrapper = new Runnable() {
public void run() {
try {
runnable.run();
}
catch (Throwable t) {
logger.error("Failed running [" + description + "]", t);
}
finally {
synchronized (active) {
active.remove(runnable);
}
}
}
@Override
public String toString() {
return "Runnable for " + description;
}
};
synchronized (active) {
Future<?> future = executors.submit(wrapper);
active.put(runnable, new Remember(description, future));
}
}
/**
* Return a array of the descriptions of all active threads.
* The description of the thread making the request is excluded. This
* is because method is used to see if a server can stop, so a server
* can run a job which stops itself.
*
* @return A list of descriptions.
*/
public String[] activeDescriptions() {
List<String> results = new ArrayList<String>();
synchronized (active) {
for (Remember remember : active.values() ) {
results.add( remember.description );
}
return (String[]) results.toArray(new String[0]);
}
}
public String toString() {
return "ThreadManager: " + active.size() + " active threads.";
}
public void close() {
synchronized (active) {
for (Map.Entry<Runnable, Remember> entry : active.entrySet()) {
Runnable runnable = entry.getKey();
Remember remember = entry.getValue();
if (runnable instanceof Stoppable) {
try {
((Stoppable) runnable).stop();
} catch (FailedToStopException e) {
logger.warn("Failed to stop [" + runnable + "]", e);
}
}
else {
remember.future.cancel(true);
}
}
}
executors.shutdownNow();
}
class Remember {
private final String description;
private final Future<?> future;
Remember(String description, Future<?> runnable) {
this.description = description;
this.future = runnable;
}
}
}