Skip to content

Commit

Permalink
Apply a timeout to app init [IMMUTANT-559]
Browse files Browse the repository at this point in the history
The default timeout is 4 minutes (240 seconds), but can be overridden
with -Dwunderboss.deployment.timeout=<n seconds>.
  • Loading branch information
tobias committed May 15, 2015
1 parent 184276e commit b0239b5
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 3 deletions.
Expand Up @@ -24,6 +24,10 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;

public class WunderBoss {

Expand Down Expand Up @@ -109,6 +113,8 @@ public static void shutdownAndReset() throws Exception {
component.stop();
}
components.clear();

shutdownWorkerPool();
}

private static <T extends Component> ComponentProvider<T> getComponentProvider(Class<T> iface, boolean throwIfMissing) {
Expand Down Expand Up @@ -198,12 +204,37 @@ public static synchronized void mergeOptions(Options<String> other) {
options = options.merge(other);
}

public static synchronized ExecutorService workerPool() {
if (workerExecutor == null) {
final String deploymentName = options().getString("deployment-name", "wunderboss");
workerExecutor = Executors.newCachedThreadPool(new ThreadFactory() {
private final AtomicLong counter = new AtomicLong(0);

@Override
public Thread newThread(Runnable r) {
return new Thread(r, String.format("%s-worker-%d",
deploymentName, counter.getAndIncrement()));
}
});
}

return workerExecutor;
}

public static synchronized void shutdownWorkerPool() {
if (workerExecutor != null) {
workerExecutor.shutdown();
workerExecutor = null;
}
}

private static Locator locator;
private static Options<String> options;
private static final Map<String, Language> languages = new HashMap<>();
private static final Map<Class, ComponentProvider> componentProviders = new HashMap<>();
private static final Map<String, Component> components = new HashMap<>();
private static DynamicClassLoader classLoader;
private static ExecutorService workerExecutor;
private static final Logger log = logger(WunderBoss.class);

}
Expand Up @@ -30,8 +30,16 @@
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ServletListener implements ServletContextListener {
public static final String TIMEOUT_PROPERTY = "wunderboss.deployment.timeout";
public static final long DEFAULT_TIMEOUT = 240; // 4 minutes

@Override
public void contextInitialized(ServletContextEvent sce) {
WunderBoss.putOption("servlet-context-path", sce.getServletContext().getContextPath());
Expand Down Expand Up @@ -61,10 +69,35 @@ protected URL jarURL() {
}
};

Future<?> future = WunderBoss.workerPool().submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
applicationRunner.start(null);

return null;
}
});

long timeout = DEFAULT_TIMEOUT;
String timeoutProp = System.getProperty(TIMEOUT_PROPERTY);
if (timeoutProp != null) {
timeout = Long.parseLong(timeoutProp);
}

try {
applicationRunner.start(null);
} catch (Exception e) {
throw new RuntimeException(e);
future.get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException _) {
future.cancel(true);
String message = String.format("Timed out waiting for initialization to complete after %d seconds. If you need more time, use the %s sysprop.",
timeout, TIMEOUT_PROPERTY);
log.error(message);
throw new RuntimeException(message);
} catch (InterruptedException _) {
future.cancel(true);
throw new RuntimeException("Initialization failed");
} catch (ExecutionException e) {
future.cancel(true);
throw new RuntimeException("Initialization failed", e.getCause());
}
}

Expand Down

0 comments on commit b0239b5

Please sign in to comment.