T shutdownOnClose(final T service, Closer closer) {
closer.addToClose(new ShutdownExecutorOnClose(service));
return service;
}
diff --git a/core/src/main/java/org/jclouds/concurrent/config/ScheduledExecutorServiceModule.java b/core/src/main/java/org/jclouds/concurrent/config/ScheduledExecutorServiceModule.java
new file mode 100644
index 0000000000..c3d53ce56d
--- /dev/null
+++ b/core/src/main/java/org/jclouds/concurrent/config/ScheduledExecutorServiceModule.java
@@ -0,0 +1,121 @@
+package org.jclouds.concurrent.config;
+
+import static org.jclouds.concurrent.config.ExecutorServiceModule.shutdownOnClose;
+import static org.jclouds.concurrent.config.ExecutorServiceModule.getStackTraceHere;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.concurrent.config.ExecutorServiceModule.DescribedFuture;
+import org.jclouds.concurrent.config.ExecutorServiceModule.DescribingExecutorService;
+import org.jclouds.lifecycle.Closer;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+
+/**
+ * Provides an {@link ScheduledExecutorService} to run periodical tasks such as virtual machine monitoring, etc.
+ *
+ * This module is not registered by default in the context because some providers do not allow to spawn threads.
+ *
+ * @author Ignasi Barrera
+ *
+ * @see ExecutorServiceModule
+ *
+ */
+public class ScheduledExecutorServiceModule extends AbstractModule {
+
+ static class DescribingScheduledExecutorService extends DescribingExecutorService implements ScheduledExecutorService {
+
+ public DescribingScheduledExecutorService(ScheduledExecutorService delegate) {
+ super(delegate);
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ @Override
+ public ScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit) {
+ return new DescribedScheduledFuture(((ScheduledExecutorService) delegate)
+ .schedule(command, delay, unit), command.toString(), getStackTraceHere());
+ }
+
+ @Override
+ public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) {
+ return new DescribedScheduledFuture(((ScheduledExecutorService) delegate)
+ .schedule(callable, delay, unit), callable.toString(), getStackTraceHere());
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ @Override
+ public ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay,
+ long period, TimeUnit unit) {
+ return new DescribedScheduledFuture(((ScheduledExecutorService) delegate)
+ .scheduleAtFixedRate(command, initialDelay, period, unit), command.toString(), getStackTraceHere());
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ @Override
+ public ScheduledFuture> scheduleWithFixedDelay(Runnable command, long initialDelay,
+ long delay, TimeUnit unit) {
+ return new DescribedScheduledFuture(((ScheduledExecutorService) delegate)
+ .scheduleWithFixedDelay(command, initialDelay, delay, unit), command.toString(), getStackTraceHere());
+ }
+ }
+
+ static class DescribedScheduledFuture extends DescribedFuture implements ScheduledFuture {
+
+ public DescribedScheduledFuture(ScheduledFuture delegate, String description,
+ StackTraceElement[] submissionTrace) {
+ super(delegate, description, submissionTrace);
+ }
+
+ @Override
+ public long getDelay(TimeUnit unit) {
+ return ((ScheduledFuture) delegate).getDelay(unit);
+ }
+
+ @Override
+ public int compareTo(Delayed o) {
+ return ((ScheduledFuture) delegate).compareTo(o);
+ }
+ }
+
+ static ScheduledExecutorService addToStringOnSchedule(ScheduledExecutorService executor) {
+ if (executor != null) {
+ return new DescribingScheduledExecutorService(executor);
+ }
+ return executor;
+ }
+
+ @Provides
+ @Singleton
+ @Named(Constants.PROPERTY_SCHEDULER_THREADS)
+ ScheduledExecutorService provideScheduledExecutor(@Named(Constants.PROPERTY_SCHEDULER_THREADS) final int count,
+ final Closer closer) {
+ return shutdownOnClose(addToStringOnSchedule(newScheduledThreadPoolNamed("scheduler thread %d", count)), closer);
+ }
+
+ @VisibleForTesting
+ static ScheduledExecutorService newScheduledThreadPoolNamed(String name, int maxCount) {
+ ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat(name)
+ .setThreadFactory(Executors.defaultThreadFactory()).build();
+ return maxCount == 0 ? Executors.newSingleThreadScheduledExecutor(factory)
+ : Executors.newScheduledThreadPool(maxCount, factory);
+ }
+
+ @Override
+ protected void configure() {
+
+ }
+
+}
diff --git a/core/src/main/java/org/jclouds/lifecycle/config/LifeCycleModule.java b/core/src/main/java/org/jclouds/lifecycle/config/LifeCycleModule.java
index 6f5e85c7f4..5ecc35fd13 100644
--- a/core/src/main/java/org/jclouds/lifecycle/config/LifeCycleModule.java
+++ b/core/src/main/java/org/jclouds/lifecycle/config/LifeCycleModule.java
@@ -28,10 +28,10 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
-import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.Constants;
@@ -41,6 +41,7 @@ import org.jclouds.lifecycle.Closer;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.ExecutionList;
import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionListener;
@@ -74,12 +75,19 @@ public class LifeCycleModule extends AbstractModule {
@Inject
@Named(Constants.PROPERTY_IO_WORKER_THREADS)
ExecutorService ioExecutor;
+ // ScheduledExecutor is defined in an optional module
+ @Inject(optional = true)
+ @Named(Constants.PROPERTY_SCHEDULER_THREADS)
+ ScheduledExecutorService scheduledExecutor;
public void close() throws IOException {
assert userExecutor != null;
userExecutor.shutdownNow();
assert ioExecutor != null;
ioExecutor.shutdownNow();
+ // ScheduledExecutor is defined in an optional module
+ if (scheduledExecutor != null)
+ scheduledExecutor.shutdownNow();
}
};
diff --git a/core/src/test/java/org/jclouds/concurrent/config/ExecutorServiceModuleTest.java b/core/src/test/java/org/jclouds/concurrent/config/ExecutorServiceModuleTest.java
index 77784d6740..1e7a70b79f 100644
--- a/core/src/test/java/org/jclouds/concurrent/config/ExecutorServiceModuleTest.java
+++ b/core/src/test/java/org/jclouds/concurrent/config/ExecutorServiceModuleTest.java
@@ -230,7 +230,7 @@ Caused by: java.lang.IllegalStateException: foo
return io.submit((Runnable)t1, (Object)"shouldn't happen");
}
- static void checkFutureGetFailsWith(Future