package edu.uw.zookeeper;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import com.google.common.collect.Queues;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigValueType;
import edu.uw.zookeeper.common.Configurable;
import edu.uw.zookeeper.common.Configuration;
import edu.uw.zookeeper.common.Factory;
import edu.uw.zookeeper.common.ListeningExecutorServiceFactory;
import edu.uw.zookeeper.common.Pair;
import edu.uw.zookeeper.common.RuntimeModule;
import edu.uw.zookeeper.common.ServiceMonitor;
import edu.uw.zookeeper.common.SimpleArguments;
import edu.uw.zookeeper.common.TimeValue;
public class DefaultRuntimeModule implements RuntimeModule {
public static DefaultRuntimeModule defaults() {
return fromArgs(new String[0]);
}
public static DefaultRuntimeModule fromArgs(String[] args) {
return fromConfiguration(configuration(args));
}
public static DefaultRuntimeModule fromConfiguration(Configuration configuration) {
return create(
configuration,
ServiceMonitor.defaults(),
ListeningExecutorServiceFactory.newInstance(
DefaultApplicationExecutorFactory.fromConfiguration(configuration),
SingleThreadScheduledExectorFactory.defaults()),
ShutdownTimeoutConfiguration.get(configuration));
}
public static SimpleArguments arguments(String[] args) {
return SimpleArguments.create("", "", args);
}
public static Configuration configuration(String[] args) {
return Configuration.defaults(arguments(args));
}
public static int availableProcessors() {
return Math.max(1,
Runtime.getRuntime().availableProcessors());
}
public static enum PlatformThreadFactory implements Factory<ThreadFactory> {
PLATFORM_THREAD_FACTORY;
public static PlatformThreadFactory getInstance() {
return PLATFORM_THREAD_FACTORY;
}
@Override
public ThreadFactory get() {
return MoreExecutors.platformThreadFactory();
}
}
public static class ThreadPoolExecutorPurger extends Pair<ScheduledExecutorService, ThreadPoolExecutor> implements Runnable {
public static <T extends ThreadPoolExecutor> T purge(ScheduledExecutorService scheduler, T executor) {
return purge(scheduler, executor, DEFAULT_PURGE_INTERVAL);
}
public static <T extends ThreadPoolExecutor> T purge(ScheduledExecutorService scheduler, T executor, TimeValue interval) {
ThreadPoolExecutorPurger purger = new ThreadPoolExecutorPurger(scheduler, executor);
scheduler.scheduleAtFixedRate(purger, interval.value(), interval.value(), interval.unit());
return executor;
}
private static final TimeValue DEFAULT_PURGE_INTERVAL = TimeValue.seconds(30);
public ThreadPoolExecutorPurger(ScheduledExecutorService scheduler, ThreadPoolExecutor executor) {
super(scheduler, executor);
}
@Override
public void run() {
second.purge();
}
}
public static class SingleThreadScheduledExectorFactory implements Factory<ScheduledExecutorService> {
public static SingleThreadScheduledExectorFactory defaults() {
return defaults(PlatformThreadFactory.getInstance().get());
}
public static SingleThreadScheduledExectorFactory defaults(ThreadFactory threadFactory) {
return fromThreadFactoryBuilder(
new ThreadFactoryBuilder()
.setThreadFactory(threadFactory)
.setDaemon(true)
.setNameFormat("scheduled-%d"));
}
public static SingleThreadScheduledExectorFactory fromThreadFactoryBuilder(ThreadFactoryBuilder threadFactory) {
return new SingleThreadScheduledExectorFactory(threadFactory);
}
private final ThreadFactoryBuilder threadFactory;
protected SingleThreadScheduledExectorFactory(ThreadFactoryBuilder threadFactory) {
this.threadFactory = threadFactory;
}
@Override
public ScheduledThreadPoolExecutor get() {
ScheduledThreadPoolExecutor instance = new ScheduledThreadPoolExecutor(1, threadFactory.build());
// not available until Java 7 :-(
// instance.setRemoveOnCancelPolicy(true);
instance.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
instance.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
//MoreExecutors.addDelayedShutdownHook(instance, 100, TimeUnit.MILLISECONDS);
ThreadPoolExecutorPurger.purge(instance, instance);
return instance;
}
}
@Configurable(path="runtime", key="poolSize", value="0", type=ConfigValueType.NUMBER)
public static class DefaultApplicationExecutorFactory implements Factory<ExecutorService> {
public static DefaultApplicationExecutorFactory fromConfiguration(
Configuration configuration) {
return fromThreadFactory(configuration, PlatformThreadFactory.getInstance().get());
}
public static DefaultApplicationExecutorFactory fromThreadFactory(
Configuration configuration, ThreadFactory threadFactory) {
int corePoolSize = getCorePoolSize(configuration, DefaultApplicationExecutorFactory.class);
int maxPoolSize = corePoolSize;
return new DefaultApplicationExecutorFactory(
corePoolSize,
maxPoolSize,
TimeValue.seconds(60),
new ThreadFactoryBuilder().setThreadFactory(threadFactory).setNameFormat("main-pool-%d"));
}
protected static int getCorePoolSize(
Configuration configuration,
Class<?> cls) {
Configurable configurable = cls.getAnnotation(Configurable.class);
Config config = configuration.withConfigurable(configurable)
.getConfigOrEmpty(configurable.path());
int corePoolSize = config.getInt(configurable.key());
if (corePoolSize == 0) {
corePoolSize = availableProcessors();
}
return corePoolSize;
}
private final int corePoolSize;
private final int maxPoolSize;
private final TimeValue keepAlive;
private final ThreadFactoryBuilder threadFactory;
protected DefaultApplicationExecutorFactory(
int corePoolSize,
int maxPoolSize,
TimeValue keepAlive,
ThreadFactoryBuilder threadFactory) {
this.corePoolSize = corePoolSize;
this.maxPoolSize = maxPoolSize;
this.keepAlive = keepAlive;
this.threadFactory = threadFactory;
}
@Override
public ExecutorService get() {
return new ThreadPoolExecutor(
corePoolSize, maxPoolSize,
keepAlive.value(), keepAlive.unit(),
Queues.<Runnable>newLinkedBlockingQueue(),
threadFactory.build());
}
}
@Configurable(path="runtime", key="shutdown", value="30 seconds", type=ConfigValueType.STRING)
public static abstract class ShutdownTimeoutConfiguration {
public static TimeValue get(Configuration configuration) {
Configurable configurable = ShutdownTimeoutConfiguration.class.getAnnotation(Configurable.class);
Config config = configuration.withConfigurable(configurable)
.getConfigOrEmpty(configurable.path());
return TimeValue.fromString(config.getString(configurable.key()));
}
}
public static DefaultRuntimeModule create(
Configuration configuration,
ServiceMonitor serviceMonitor,
ListeningExecutorServiceFactory executors,
TimeValue shutdownTimeout) {
return new DefaultRuntimeModule(configuration, serviceMonitor, executors, shutdownTimeout);
}
protected final ServiceMonitor serviceMonitor;
protected final Configuration configuration;
protected final ListeningExecutorServiceFactory executors;
protected final TimeValue shutdownTimeout;
protected DefaultRuntimeModule(
Configuration configuration,
ServiceMonitor serviceMonitor,
ListeningExecutorServiceFactory executors,
TimeValue shutdownTimeout) {
this.configuration = configuration;
this.serviceMonitor = serviceMonitor;
this.executors = executors;
this.shutdownTimeout = shutdownTimeout;
serviceMonitor.add(executors);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
@Override
public Factory<ThreadFactory> getThreadFactory() {
return PlatformThreadFactory.getInstance();
}
@Override
public ServiceMonitor getServiceMonitor() {
return serviceMonitor;
}
@Override
public ListeningExecutorServiceFactory getExecutors() {
return executors;
}
@Override
public void shutdown() {
getServiceMonitor().stopAsync();
try {
getServiceMonitor().awaitTerminated(shutdownTimeout.value(), shutdownTimeout.unit());
} catch (Exception e) {}
}
}