package com.asteria.utility;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
* The class that allows a series of tasks or services to be executed
* asynchronously in the background.
* <p>
* <p>
* Please note that a single background loader instance can only be used once.
* Once the background load finishes awaiting completion, the executor is
* shutdown and therefore cannot be reused. Subsequent attempts to reuse
* background loaders after they've been shutdown will throw an
* {@link java.lang.IllegalStateException}.
*
* @author lare96 <http://github.com/lare96>
*/
public final class BackgroundLoader {
/**
* The executor that will execute the queue of tasks asynchronously in the
* background.
*/
private final ExecutorService service = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(
"BackgroundLoaderThread").setDaemon(true).build());
/**
* The queue of tasks that will be executed by the executor.
*/
private final Queue<Runnable> tasks = new ArrayDeque<>();
/**
* The flag that determines if this background loader has been shutdown.
*/
private boolean shutdown;
/**
* Starts this background loader by polling the queue of tasks into the
* executor.
* <p>
* <p>
* Please note that {@code awaitCompletion()} can be called after this in
* order to block the underlying thread until the tasks are completed.
*
* @param backgroundTasks
* the collection of tasks to execute in the background.
* @throws IllegalStateException
* if this background loader has been shutdown.
*/
public void start(Collection<Runnable> backgroundTasks) {
Preconditions.checkState(!shutdown && !service.isShutdown(), "This background loader has been shutdown!");
tasks.addAll(backgroundTasks);
Runnable t;
while ((t = tasks.poll()) != null)
service.execute(t);
service.shutdown();
}
/**
* Awaits the completion of the execution of the tasks by the executor. This
* will block the thread for an infinite amount of time or until the tasks
* are completed. Once the tasks complete, this background loader will be
* shutdown and cannot be used again.
* <p>
* <p>
* Please note that {@code start()} must be called before this in order to
* submit the tasks to the executor.
*
* @return {@code true} if the tasks complete and this loader is shutdown
* normally, {@code false} otherwise.
* @throws IllegalStateException
* if this background loader has been shutdown.
*/
public boolean awaitCompletion() {
Preconditions.checkState(!shutdown, "This background loader has been shutdown!");
try {
service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {
LoggerUtils.getLogger(BackgroundLoader.class).log(Level.SEVERE, "The background service loader was interrupted.", e);
return false;
}
shutdown = true;
return true;
}
}