package net.jxta.impl.util.threads;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Singleton manager for periodic, deferred and multi-threaded execution of tasks. The intention
* of this class is to abstract away the details of how tasks will be executed, and specifics
* of how many threads are used.
* <p>
* <em>NOTE</em>: This is <em>not</em> part of the stable API for JXTA, and should not be used
* by code which is not a core module of the JXTA-JXSE implementation. We anticipate that in
* future a similar mechanism to this will be adopted as the standard way of controlling the
* execution thread pools in JXTA, and may be later exposed to the outside world, but the
* details have not yet been adequately discussed.
*/
public class TaskManager {
protected static final Logger LOG = Logger.getLogger(TaskManager.class.getName());
static final String CORE_POOL_SIZE_SYSPROP = "net.jxta.util.threads.TaskManager.corePoolSize";
static final String MAX_WORKER_POOL_SIZE_SYSPROP = "net.jxta.util.threads.TaskManager.maxWorkerPoolSize";
static final String SCHEDULED_POOL_SIZE_SYSPROP = "net.jxta.util.threads.TaskManager.scheduledPoolSize";
static final String IDLE_THREAD_TIMEOUT_SYSPROP = "net.jxta.util.threads.TaskManager.idleThreadTimeout";
static final int DEFAULT_CORE_POOL_SIZE =4;
static final int DEFAULT_MAX_WORKER_POOL_SIZE = Integer.MAX_VALUE;
static final int DEFAULT_SCHEDULED_POOL_SIZE = 2;
static final int DEFAULT_IDLE_THREAD_TIMEOUT = 10;
private SharedThreadPoolExecutor normalExecutor;
private SharedScheduledThreadPoolExecutor scheduledExecutor;
private ScheduledExecutorService monitoringExecutor;
private CachedThreadExecutorService cachedExecutor;
private Map<String, ProxiedScheduledExecutorService> proxiedExecutors;
private boolean started;
static int getScheduledPoolSize(Integer scheduledPoolSize) {
int size = scheduledPoolSize == null ? Integer.getInteger(SCHEDULED_POOL_SIZE_SYSPROP, DEFAULT_SCHEDULED_POOL_SIZE)
: scheduledPoolSize;
return Math.max(1, size);
}
static int getCorePoolSize(Integer coreWorkerPoolSize) {
int size = coreWorkerPoolSize == null ? Integer.getInteger(CORE_POOL_SIZE_SYSPROP, DEFAULT_CORE_POOL_SIZE)
: coreWorkerPoolSize;
return Math.max(0, size);
}
static int getIdleThreadTimeout(Integer idleThreadTimeout) {
int timeout = idleThreadTimeout == null ? Integer.getInteger(IDLE_THREAD_TIMEOUT_SYSPROP, DEFAULT_IDLE_THREAD_TIMEOUT)
: idleThreadTimeout;
return Math.max(0, timeout);
}
static int getMaxWorkerPoolSize(int coreWorkerPoolSize, Integer maxWorkerPoolSize) {
// while core pool size is allowed to be zero, max pool size
// must be greater than the core pool size AND greater than
// 0.
int leastUpperBound = Math.max(1, coreWorkerPoolSize);
Integer size = maxWorkerPoolSize == null ? Integer.getInteger(MAX_WORKER_POOL_SIZE_SYSPROP, DEFAULT_MAX_WORKER_POOL_SIZE)
: maxWorkerPoolSize;
return Math.max(leastUpperBound, size);
}
/**
* Creates a task manager that uses the default or system property specified values for core pool size,
* max pool size, idle thread timeout and scheduled pool size.
*/
public TaskManager() {
this(null, null, null, null);
}
/**
* Allows the construction of a task manager with explicitly specified values for each of core pool size,
* max pool size, idle thread timeout and scheduled pool size. If null is passed for any of these parameters,
* the system property value, if specified, will be used. Failing that, a sensible default will be applied.
*
* @param coreWorkerPoolSize the number of threads that will be maintained in the executor service.
* @param maxWorkerPoolSize the maximum number of threads that will be allowed in the executor service.
* @param idleThreadTimeoutSecs the minimum amount of time that additional threads (beyond the core pool size)
* will stay alive before terminating.
* @param scheduledPoolSize the number of threads that will be used for the execution of deferred and periodic
* tasks.
*/
public TaskManager(Integer coreWorkerPoolSize, Integer maxWorkerPoolSize, Integer idleThreadTimeoutSecs, Integer scheduledPoolSize) {
NamedThreadFactory NTF = new NamedThreadFactory("JxtaTaskMonitor");
monitoringExecutor = Executors.newSingleThreadScheduledExecutor(NTF);
int corePoolSize = getCorePoolSize(coreWorkerPoolSize);
normalExecutor = new SharedThreadPoolExecutor(monitoringExecutor,
corePoolSize,
getMaxWorkerPoolSize(corePoolSize, maxWorkerPoolSize),
getIdleThreadTimeout(idleThreadTimeoutSecs),
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new NamedThreadFactory("JxtaWorker"));
scheduledExecutor = new SharedScheduledThreadPoolExecutor(monitoringExecutor, getScheduledPoolSize(scheduledPoolSize), new NamedThreadFactory("JxtaScheduledWorker"));
cachedExecutor = new CachedThreadExecutorService(NTF);
proxiedExecutors = Collections.synchronizedMap(new HashMap<String, ProxiedScheduledExecutorService>());
started=true;
}
/**
* Provides a potentially shared executor service.
* Note that since this instance could be shared, it is illegal to attempt to shut down the
* provided instance (an IllegalStateException will be thrown).
*/
public ExecutorService getExecutorService() {
return normalExecutor;
}
/**
* Provides a cached thread executor service.
* Note that since this instance could be shared, it is illegal to attempt to shut down the
* provided instance (an IllegalStateException will be thrown).
*/
public ExecutorService getCachedExecutorService() {
return cachedExecutor;
}
/**
* Provides a shared scheduled executor service.
* Note that since this instance could be shared, it is illegal to attempt to shut down the
* provided instance (an IllegalStateException will be thrown).
*/
public ScheduledExecutorService getScheduledExecutorService() {
return scheduledExecutor;
}
/**
* Provides a ScheduledExecutorService which the client can safely shut down independently
* of any other ScheduledExecutor provided by this class.
*
* NOTE: the current implementation of local scheduled executors incurs a cost of an additional
* thread per service. Please refrain from this unless you genuinely require your own
* executor service.
*/
public ScheduledExecutorService getLocalScheduledExecutorService(String serviceName) {
synchronized(proxiedExecutors) {
ProxiedScheduledExecutorService service = proxiedExecutors.get(serviceName);
if(service == null) {
service = new ProxiedScheduledExecutorService(serviceName, normalExecutor);
proxiedExecutors.put(serviceName, service);
}
return service;
}
}
public void shutdown() {
if(!started) {
throw new IllegalStateException("Task manager is already shut down");
}
normalExecutor.shutdownShared();
scheduledExecutor.shutdownShared();
monitoringExecutor.shutdownNow();
cachedExecutor.shutdownShared();
synchronized (proxiedExecutors) {
for(String serviceName : proxiedExecutors.keySet()) {
ProxiedScheduledExecutorService service = proxiedExecutors.get(serviceName);
if(!service.isShutdown()) {
LOG.log(Level.WARNING, "Local executor for \"" + serviceName + "\" has not been locally shut down - forcing termination now");
service.shutdownNow();
}
}
proxiedExecutors.clear();
}
started = false;
}
}