/*
* Copyright (C) 2012 Jan Pokorsky
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.cas.lib.proarc.common.imports;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Dispatcher controls scheduling of {@link ImportProcess import processes}.
*
* For now it runs processes in single thread to preserve memory resources.
*
* @author Jan Pokorsky
*/
public final class ImportDispatcher {
private static final Logger LOG = Logger.getLogger(ImportDispatcher.class.getName());
private static ImportDispatcher INSTANCE = new ImportDispatcher();
private ExecutorService pool;
private final int threadCount;
public ImportDispatcher() {
this(1);
}
ImportDispatcher(int threadCount) {
if (threadCount < 1) {
throw new IllegalArgumentException("threadCount: " + threadCount);
}
this.threadCount = threadCount;
}
public static ImportDispatcher getDefault() {
return INSTANCE;
}
public static void setDefault(ImportDispatcher dispatcher) {
INSTANCE = dispatcher;
}
public void init() {
pool = Executors.newFixedThreadPool(threadCount, new ImportDispatcherThreadFactory());
}
public void stop() {
stop(60, TimeUnit.SECONDS);
}
public void stop(long timeout, TimeUnit unit) {
if (pool == null) {
return ;
}
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(timeout, unit)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(timeout, unit)) {
LOG.severe("ImportDispatcher thread pool did not terminate");
}
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
public Future<ImportProcess> addImport(ImportProcess task) {
return addTask(task);
}
<T extends Runnable> Future<T> addTask(T task) {
checkRunning();
return pool.submit(new ExceptionHandlingTask(task), task);
}
private void checkRunning() {
if (pool == null) {
throw new IllegalStateException("needs init");
}
if (pool.isShutdown()) {
throw new IllegalStateException("needs restart");
}
}
private static final class ExceptionHandlingTask implements Runnable {
private final Runnable delegate;
public ExceptionHandlingTask(Runnable delegate) {
this.delegate = delegate;
}
@Override
public void run() {
try {
delegate.run();
} catch (Throwable t) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), t);
}
}
}
private static final class ImportDispatcherThreadFactory implements ThreadFactory {
private final ThreadFactory factory;
public ImportDispatcherThreadFactory() {
factory = Executors.defaultThreadFactory();
}
@Override
public Thread newThread(Runnable r) {
Thread thread = factory.newThread(r);
String name = ImportDispatcher.class.getSimpleName() + '-' + thread.getName();
thread.setName(name);
UncaughtExceptionHandler uncaughtExceptionHandler = thread.getUncaughtExceptionHandler();
thread.setUncaughtExceptionHandler(new ImportDispatcherExceptionHandler(uncaughtExceptionHandler));
return thread;
}
}
private static final class ImportDispatcherExceptionHandler implements UncaughtExceptionHandler {
private final UncaughtExceptionHandler delegate;
public ImportDispatcherExceptionHandler(UncaughtExceptionHandler delegate) {
this.delegate = delegate;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
LOG.log(Level.SEVERE, t.getName(), e);
// if (delegate != null) {
// delegate.uncaughtException(t, e);
// }
}
}
}