package org.jabref;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Responsible for managing of all threads (except Swing threads) in JabRef
*/
public class JabRefExecutorService implements Executor {
public static final JabRefExecutorService INSTANCE = new JabRefExecutorService();
private static final Log LOGGER = LogFactory.getLog(JabRefExecutorService.class);
private final ExecutorService executorService = Executors.newCachedThreadPool(r -> {
Thread thread = new Thread(r);
thread.setName("JabRef CachedThreadPool");
thread.setUncaughtExceptionHandler(new FallbackExceptionHandler());
return thread;
});
private final ExecutorService lowPriorityExecutorService = Executors.newCachedThreadPool(r -> {
Thread thread = new Thread(r);
thread.setName("JabRef LowPriorityCachedThreadPool");
thread.setUncaughtExceptionHandler(new FallbackExceptionHandler());
return thread;
});
private final Timer timer = new Timer("timer", true);
private Thread remoteThread;
private JabRefExecutorService() { }
@Override
public void execute(Runnable command) {
if (command == null) {
LOGGER.debug("Received null as command for execution");
return;
}
executorService.execute(command);
}
public void executeAndWait(Runnable command) {
if (command == null) {
LOGGER.debug("Received null as command for execution");
return;
}
Future<?> future = executorService.submit(command);
while (true) {
try {
future.get();
return;
} catch (InterruptedException ignored) {
// Ignored
} catch (ExecutionException e) {
LOGGER.error("Problem executing command", e);
}
}
}
public void executeInterruptableTask(final Runnable runnable) {
this.lowPriorityExecutorService.execute(runnable);
}
public void executeInterruptableTask(final Runnable runnable, String taskName) {
this.lowPriorityExecutorService.execute(new NamedRunnable(taskName, runnable));
}
public void executeInterruptableTaskAndWait(Runnable runnable) {
if (runnable == null) {
LOGGER.debug("Received null as command for execution");
return;
}
Future<?> future = lowPriorityExecutorService.submit(runnable);
while (true) {
try {
future.get();
return;
} catch (InterruptedException ignored) {
// Ignored
} catch (ExecutionException e) {
LOGGER.error("Problem executing command", e);
}
}
}
public void manageRemoteThread(Thread thread) {
if (this.remoteThread != null) {
throw new IllegalStateException("Remote thread is already attached");
} else {
this.remoteThread = thread;
remoteThread.start();
}
}
public void stopRemoteThread() {
if (remoteThread != null) {
remoteThread.interrupt();
remoteThread = null;
}
}
public void submit(TimerTask timerTask, long millisecondsDelay) {
timer.schedule(timerTask, millisecondsDelay);
}
public void shutdownEverything() {
// those threads will be allowed to finish
this.executorService.shutdown();
//those threads will be interrupted in their current task
this.lowPriorityExecutorService.shutdownNow();
// kill the remote thread
stopRemoteThread();
// timer doesn't need to be canceled as it is run in daemon mode, which ensures that it is stopped if the application is shut down
}
class NamedRunnable implements Runnable {
private final String name;
private final Runnable task;
public NamedRunnable(String name, Runnable runnable) {
this.name = name;
this.task = runnable;
}
@Override
public void run() {
final String orgName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
task.run();
} finally {
Thread.currentThread().setName(orgName);
}
}
}
}