From 1e7e214c5f3e5f32f9b63ceb133513b308977230 Mon Sep 17 00:00:00 2001 From: Jonathan Doklovic Date: Thu, 26 Jun 2014 10:54:34 -0700 Subject: [PATCH 1/2] Issue #1 : providing a way to shutdown executor services. --- .../LoadingMessageSourceProvider.java | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/github/fge/msgsimple/provider/LoadingMessageSourceProvider.java b/src/main/java/com/github/fge/msgsimple/provider/LoadingMessageSourceProvider.java index 7eff6df..152c47d 100644 --- a/src/main/java/com/github/fge/msgsimple/provider/LoadingMessageSourceProvider.java +++ b/src/main/java/com/github/fge/msgsimple/provider/LoadingMessageSourceProvider.java @@ -29,16 +29,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -89,6 +80,7 @@ public final class LoadingMessageSourceProvider public Thread newThread(final Runnable r) { final Thread ret = factory.newThread(r); + ret.setName(LoadingMessageSourceProvider.class.getSimpleName()); ret.setDaemon(true); return ret; } @@ -97,6 +89,9 @@ public Thread newThread(final Runnable r) private static final InternalBundle BUNDLE = InternalBundle.getInstance(); private static final int NTHREADS = 3; + + //We need a central place to put executors so we can shut them all down on request + private static final CopyOnWriteArrayList executors = new CopyOnWriteArrayList(); /* * Executor service for loading tasks @@ -146,6 +141,8 @@ private LoadingMessageSourceProvider(final Builder builder) * Mimic an already enabled expiry if, in fact, there is none */ expiryEnabled = new AtomicBoolean(expiryDuration == 0L); + + executors.addIfAbsent(service); } /** @@ -158,6 +155,23 @@ public static Builder newBuilder() return new Builder(); } + /** + * Shuts down all current ExecutorServices by calling shutdownNow() on each of them. + * This is handy for clients that need to explicitly destroy executors/threads before the jvm is terminated. + * + * For example, in an OSGi application, you may need to stop a bundle using this and start it up again later without + * a JVM restart. Without a way to shutdown the executors, it could lead to OOMEs. + */ + public static void shutdown() + { + for(ExecutorService e : executors) + { + e.shutdownNow(); + } + + executors.clear(); + } + @Override public MessageSource getMessageSource(final Locale locale) { From 8066614088851ec142848e61515f3e5f222b9ef1 Mon Sep 17 00:00:00 2001 From: Jonathan Doklovic Date: Fri, 27 Jun 2014 09:11:58 -0700 Subject: [PATCH 2/2] added a way to re-enable executors --- .../LoadingMessageSourceProvider.java | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/fge/msgsimple/provider/LoadingMessageSourceProvider.java b/src/main/java/com/github/fge/msgsimple/provider/LoadingMessageSourceProvider.java index 152c47d..e997640 100644 --- a/src/main/java/com/github/fge/msgsimple/provider/LoadingMessageSourceProvider.java +++ b/src/main/java/com/github/fge/msgsimple/provider/LoadingMessageSourceProvider.java @@ -93,11 +93,13 @@ public Thread newThread(final Runnable r) //We need a central place to put executors so we can shut them all down on request private static final CopyOnWriteArrayList executors = new CopyOnWriteArrayList(); + //Need to track if we're shutdown or not. + private static final AtomicBoolean shutdown = new AtomicBoolean(false); + /* * Executor service for loading tasks */ - private final ExecutorService service - = Executors.newFixedThreadPool(NTHREADS, THREAD_FACTORY); + private ExecutorService service = Executors.newFixedThreadPool(NTHREADS, THREAD_FACTORY); /* * Loader and default source @@ -169,12 +171,29 @@ public static void shutdown() e.shutdownNow(); } - executors.clear(); + shutdown.set(true); + } + + /** + * Restarts all ExecutorServices that have been shutdown + */ + public static void restartIfNeeded() + { + if(shutdown.get()) + { + shutdown.set(false); + } } @Override public MessageSource getMessageSource(final Locale locale) { + if(shutdown.get()) + { + return defaultSource; + } + + ensureServiceIsReady(); /* * Set up expiry, if necessary */ @@ -250,6 +269,18 @@ public MessageSource getMessageSource(final Locale locale) } } + private void ensureServiceIsReady() + { + if(null == service || service.isShutdown() || service.isTerminated()) + { + executors.remove(service); + + service = Executors.newFixedThreadPool(NTHREADS, THREAD_FACTORY); + + executors.addIfAbsent(service); + } + } + private FutureTask loadingTask(final Locale locale) { return new FutureTask(new Callable()