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 SCHEDULED_POOL_SIZE_SYSPROP = "net.jxta.util.threads.TaskManager.scheduledPoolSize"; static final int DEFAULT_CORE_POOL_SIZE =4; static final int DEFAULT_SCHEDULED_POOL_SIZE = 2; private static TaskManager singleton; private static SharedThreadPoolExecutor normalExecutor; private static SharedScheduledThreadPoolExecutor scheduledExecutor; private static ScheduledExecutorService monitoringExecutor; private static Map<String, ProxiedScheduledExecutorService> proxiedExecutors; private static boolean started; static int getScheduledPoolSize() { return Math.max(1, Integer.getInteger(SCHEDULED_POOL_SIZE_SYSPROP, DEFAULT_SCHEDULED_POOL_SIZE)); } static int getCorePoolSize() { return Math.max(1, Integer.getInteger(CORE_POOL_SIZE_SYSPROP, DEFAULT_CORE_POOL_SIZE)); } public static TaskManager getTaskManager() { if(TaskManager.singleton == null) { TaskManager.singleton = new TaskManager(); monitoringExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("JxtaTaskMonitor")); normalExecutor = new SharedThreadPoolExecutor(monitoringExecutor, getCorePoolSize(), Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new NamedThreadFactory("JxtaWorker")); scheduledExecutor = new SharedScheduledThreadPoolExecutor(monitoringExecutor, getScheduledPoolSize(), new NamedThreadFactory("JxtaScheduledWorker")); proxiedExecutors = Collections.synchronizedMap(new HashMap<String, ProxiedScheduledExecutorService>()); started=true; } return TaskManager.singleton; } /** * discards any existing TaskManager singleton. Intended for testing * purposes only. */ static void resetTaskManager() { TaskManager.singleton = null; } /** * 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 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(); 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; } }