/* * This file is part of VIUtils. * * Copyright © 2012-2015 Visual Illusions Entertainment * * VIUtils is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License along with this library. * If not, see http://www.gnu.org/licenses/lgpl.html. */ package net.visualillusionsent.utils; import java.util.Iterator; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static java.util.concurrent.TimeUnit.HOURS; import static java.util.concurrent.TimeUnit.MICROSECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; import static net.visualillusionsent.utils.Verify.notNegativeOrZero; import static net.visualillusionsent.utils.Verify.notNull; /** * Task Management System * <p/> * Creates a Thread Pool for handling executing delayed and continuous tasks * * @author Jason (darkdiplomat) * @version 1.3 * @since 1.0.0 */ public final class TaskManager { /** Class Version */ private static final float classVersion = 1.3F; /* VIUtils 1.3.1 | 1.3 */ /** The ThreadPool object */ private static final ScheduledThreadPoolExecutor threadPool; /** The Map of Tasks */ private static final ConcurrentHashMap<Task, ScheduledFuture<?>> tasks; static { threadPool = new ScheduledThreadPoolExecutor(8); // Set the max number of core idle threads threadPool.setKeepAliveTime(5, SECONDS); // How long to keep idle threads alive threadPool.allowCoreThreadTimeOut(true); // Allow the core threads to time out threadPool.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); //Don't execute anything after shutdown threadPool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); //Don't execute anything after shutdown tasks = new ConcurrentHashMap<Task, ScheduledFuture<?>>(); // Create the map for Task tracking scheduleContinuedTaskInMillis(new TaskCleaner(), 10, 10); // Schedule the clean up } /** Constructions disallowed */ private TaskManager() { } /** * Executes a {@link Runnable} task immediately * * @param task * the {@link Runnable} task to execute * * @throws java.lang.NullPointerException * if {@code task} is null * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static void executeTask(Runnable task) throws RejectedExecutionException { notNull(task, "Runnable task"); threadPool.execute(task); //wrap runnable for exception logging } /** * Submits a {@link Runnable} task to be executed. Execution may not happen immediately depending on the queued tasks * * @param task * the {@link Runnable} task to execute * * @return a Future representing pending completion of the task * * @throws java.lang.NullPointerException * if {@code task} is null * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static Future<?> submitTask(Runnable task) throws RejectedExecutionException { notNull(task, "Runnable task"); return threadPool.submit(task); } /** * Submits a {@link Callable} task to be executed. Execution may not happen immediately depending on the queued tasks * * @param task * the {@link Callable} task to execute * * @return a {@link Future} representing pending completion of the task * * @throws java.lang.NullPointerException * if {@code task} is null * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static <V> Future<V> submitTask(Callable<V> task) throws RejectedExecutionException { notNull(task, "Callable task"); return threadPool.submit(task); } /** * Executes a {@link Runnable} task after a delay in microseconds * * @param task * the {@link Runnable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleDelayedTaskInMicros(Runnable task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, MICROSECONDS); } /** * Executes a {@link Callable} task after a delay in microseconds * * @param task * the {@link Callable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static <V> ScheduledFuture<V> scheduleDelayedTaskInMicros(Callable<V> task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, MICROSECONDS); } /** * Executes a {@link Runnable} task after a delay in milliseconds * * @param task * the {@link Runnable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleDelayedTaskInMillis(Runnable task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, MILLISECONDS); } /** * Executes a {@link Callable} task after a delay in milliseconds * * @param task * the {@link Callable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static <V> ScheduledFuture<V> scheduleDelayedTaskInMillis(Callable<V> task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, MILLISECONDS); } /** * Executes a {@link Runnable} task after a delay in seconds * * @param task * the {@link Runnable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleDelayedTaskInSeconds(Runnable task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, SECONDS); } /** * Executes a {@link Callable} task after a delay in seconds * * @param task * the {@link Callable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static <V> ScheduledFuture<V> scheduleDelayedTaskInSeconds(Callable<V> task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, SECONDS); } /** * Executes a {@link Runnable} task after a delay in minutes * * @param task * the {@link Runnable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleDelayedTaskInMinutes(Runnable task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, MINUTES); } /** * Executes a {@link Callable} task after a delay in microseconds * * @param task * the {@link Callable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static <V> ScheduledFuture<V> scheduleDelayedTaskInMinutes(Callable<V> task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, MINUTES); } /** * Executes a {@link Runnable} task after a delay in Hours * * @param task * the {@link Runnable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleDelayedTaskInHours(Runnable task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, HOURS); } /** * Executes a {@link Callable} task after a delay in hours * * @param task * the {@link Callable} task to execute * @param delay * the delay before execution * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static <V> ScheduledFuture<V> scheduleDelayedTaskInHours(Callable<V> task, long delay) throws RejectedExecutionException { return scheduleDelayedTask(task, delay, HOURS); } /** * Executes a {@link Runnable} task after a delay in the specified {@link TimeUnit} * * @param task * the {@link Runnable} task to execute * @param delay * the delay before execution * @param timeUnit * the {@link TimeUnit} to use * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} or {@code timeUnit} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleDelayedTask(Runnable task, long delay, TimeUnit timeUnit) throws RejectedExecutionException { notNull(task, "Runnable task"); notNegativeOrZero(delay, "Delay"); notNull(timeUnit, "TimeUnit timeUnit"); ScheduledFuture<?> sTask = threadPool.schedule(task, delay, timeUnit); tasks.put(new Task(task), sTask); return sTask; } /** * Executes a {@link Callable} task after a delay in the specified {@link TimeUnit} * * @param task * the {@link Callable} task to execute * @param delay * the delay before execution * @param timeUnit * the {@link TimeUnit} to use * * @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion * * @throws java.lang.NullPointerException * if {@code task} or {@code timeUnit} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static <V> ScheduledFuture<V> scheduleDelayedTask(Callable<V> task, long delay, TimeUnit timeUnit) throws RejectedExecutionException { notNull(task, "Callable task"); notNegativeOrZero(delay, "Delay"); notNull(timeUnit, "TimeUnit timeUnit"); ScheduledFuture<V> sTask = threadPool.schedule(task, delay, timeUnit); tasks.put(new Task(task), sTask); return sTask; } /** * Executes a {@link Runnable} task continually on a fixed delay in microseconds * * @param task * the {@link Runnable} task to execute * @param initialDelay * the delay before initial execution * @param delay * the delay between additional executions * * @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleContinuedTaskInMicros(Runnable task, long initialDelay, long delay) throws RejectedExecutionException { return scheduleContinuedTask(task, initialDelay, delay, MICROSECONDS); } /** * Executes a {@link Runnable} task continually on a fixed delay in milliseconds * * @param task * the {@link Runnable} task to execute * @param initialDelay * the delay before initial execution * @param delay * the delay between additional executions * * @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleContinuedTaskInMillis(Runnable task, long initialDelay, long delay) throws RejectedExecutionException { return scheduleContinuedTask(task, initialDelay, delay, MILLISECONDS); } /** * Executes a {@link Runnable} task continually on a fixed delay in seconds * * @param task * the {@link Runnable} task to execute * @param initialDelay * the delay before initial execution * @param delay * the delay between additional executions * * @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleContinuedTaskInSeconds(Runnable task, long initialDelay, long delay) throws RejectedExecutionException { return scheduleContinuedTask(task, initialDelay, delay, SECONDS); } /** * Executes a {@link Runnable} task continually on a fixed delay in minutes * * @param task * the {@link Runnable} task to execute * @param initialDelay * the delay before initial execution * @param delay * the delay between additional executions * * @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleContinuedTaskInMinutes(Runnable task, long initialDelay, long delay) throws RejectedExecutionException { return scheduleContinuedTask(task, initialDelay, delay, MINUTES); } /** * Executes a {@link Runnable} task continually on a fixed delay in the specified TimeUnit * * @param task * the {@link Runnable} task to execute * @param initialDelay * the delay before initial execution * @param delay * the delay between additional executions * @param timeUnit * the {@link TimeUnit} to use * * @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation * * @throws java.lang.NullPointerException * if {@code task} is null * @throws java.lang.IllegalArgumentException * if the {@code delay} is equal to or less than zero * @throws RejectedExecutionException * at discretion of <tt>RejectedExecutionHandler</tt>, if task cannot be accepted * for execution because the executor has been shut down. */ public static ScheduledFuture<?> scheduleContinuedTask(Runnable task, long initialDelay, long delay, TimeUnit timeUnit) throws RejectedExecutionException { notNull(task, "Runnable task"); notNegativeOrZero(initialDelay, "long initialDelay"); ScheduledFuture<?> sTask = threadPool.scheduleAtFixedRate(task, initialDelay, delay, timeUnit); tasks.put(new Task(task), sTask); return sTask; } /** * Removes a {@link Runnable} task from the pool * * @param task * the {@link Runnable} task to be removed * * @return {@code true} if successfully removed, {@code false} if already stopped or non-existent */ public static boolean removeTask(Runnable task) { boolean check = false; Task wrappedTask = Task.wrap(task); if (tasks.containsKey(wrappedTask)) { check = tasks.get(wrappedTask).cancel(true); tasks.remove(wrappedTask); } if (!check) { check = threadPool.remove(task); } if (check) { threadPool.purge(); } return check; } /** * Removes a {@link Callable} task from the pool * * @param task * the {@link Callable} task to be removed * * @return {@code true} if successfully removed, {@code false} if already stopped or non-existent */ public static boolean removeTask(Callable<?> task) { boolean check = false; Task wrappedTask = Task.wrap(task); if (tasks.containsKey(wrappedTask)) { check = tasks.get(wrappedTask).cancel(true); tasks.remove(wrappedTask); } if (check) { threadPool.purge(); } return check; } /** * Internal Task cleanup * * @author Jason (darkdiplomat) */ private static final class TaskCleaner implements Runnable { @Override public final void run() { //Run to clean up any memory leaks we may cause by holding dead tasks if (tasks.isEmpty()) { return; // skip cleaning if there are no tasks } Iterator<Task> taskItr = tasks.keySet().iterator(); while (taskItr.hasNext()) { Task task = taskItr.next(); //Get the task if (tasks.get(task).isDone()) { // Check task for completion try { tasks.get(task).get(); // Test for execution exceptions } catch (CancellationException cex) { // Don't care if it was cancelled } catch (InterruptedException e) { // The task was probably canceled so skip this } catch (ExecutionException eex) { //This is important if an exception was caused in execution, print out the exception immediately task.printError(eex.getCause()); // Call the printError message for the proper Task name rather than the wrapper's name } taskItr.remove(); } } } } /** * Gets this class's version number * * @return the class version */ public static float getClassVersion() { return classVersion; } }