/*
* Copyright (c) 2009-2015
* IT-Consulting Stephan Schloepke (http://www.schloepke.de/)
* klemm software consulting Mirko Klemm (http://www.klemm-scs.com/)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jbasics.jee.servlet;
import org.jbasics.checker.ContractCheck;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The {@link ServletExecutorPool} is designed specific for use in a servlet containter. You need to register the {@link
* ServletExecutorPool} as a {@link ServletContextListener} in the web.xml in order to function correctly. <p> Currently
* not finished yet! So this is in alpha, beta or what so ever </p>
*
* @author Stephan Schloepke
* @since 1.0
*/
public class ServletExecutorPool implements ServletContextListener {
private final static AtomicBoolean available = new AtomicBoolean(false);
private final static ConcurrentMap<Enum<?>, ExecutorService> executors = new ConcurrentHashMap<Enum<?>, ExecutorService>();
public static <T> Future<T> queueTask(final Enum<?> name, final Callable<T> task) {
return ServletExecutorPool.getOrCreateExecutorService(name).submit(ContractCheck.mustNotBeNull(task, "task")); //$NON-NLS-1$
}
/**
* Returns an already created {@link ExecutorService} for the given name or creates one and returns it.
*
* @param name The name of the executor pool to create
*
* @return The {@link ExecutorService} already created or a newly created one.
*/
protected final static ExecutorService getOrCreateExecutorService(final Enum<?> name) {
if (!ServletExecutorPool.available.get()) {
throw new IllegalStateException("Executor is no longer available (terminating or is terminated)"); //$NON-NLS-1$
}
ExecutorService result = ServletExecutorPool.executors.get(ContractCheck.mustNotBeNull(name, "name")); //$NON-NLS-1$
if (result == null) {
result = Executors.newSingleThreadExecutor();
ExecutorService t = ServletExecutorPool.executors.putIfAbsent(name, result);
if (t != null) {
result.shutdown();
result = t;
}
}
return result;
}
public void contextInitialized(final ServletContextEvent sce) {
ServletExecutorPool.available.set(false);
}
public void contextDestroyed(final ServletContextEvent sce) {
ServletExecutorPool.shutdownAll();
}
public static void shutdownAll() {
if (ServletExecutorPool.available.compareAndSet(true, false)) {
for (ExecutorService service : ServletExecutorPool.executors.values()) {
try {
service.shutdown();
} catch (RuntimeException e) {
// ignore this here
}
}
long startTime = System.currentTimeMillis();
long timeWait = 120; // Seconds
for (ExecutorService service : ServletExecutorPool.executors.values()) {
long timeUsed = System.currentTimeMillis() - startTime;
if (timeUsed > 0) {
timeUsed = timeUsed / 1000;
}
timeWait -= timeUsed;
try {
if (timeWait <= 0) {
service.awaitTermination(5, TimeUnit.SECONDS);
} else {
service.awaitTermination(timeWait, TimeUnit.SECONDS);
}
} catch (InterruptedException e) {
// Ignore
}
if (!service.isTerminated()) {
service.shutdownNow();
}
}
}
}
}