com.gmail.tracebachi.DeltaExecutor.DeltaExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.gmail.tracebachi.DeltaExecutor.DeltaExecutor.java

Source

/*
 * This file is part of DeltaExecutor.
 *
 * DeltaExecutor 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.
 *
 * DeltaExecutor 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 DeltaExecutor.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.gmail.tracebachi.DeltaExecutor;

import com.gmail.tracebachi.DeltaExecutor.Enums.CancelResult;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

/**
 * Created by Trace Bachi (tracebachi@gmail.com, BigBossZee) on 3/19/16.
 */
public class DeltaExecutor {
    private static DeltaExecutor instance;

    public static DeltaExecutor instance() {
        return Preconditions.checkNotNull(instance, "DeltaExecutor has not been initialized yet.");
    }

    static void initialize(Logger logger, int coreThreadCount, int maxThreadCount, int idleThreadTimeout,
            int niceShutdownPasses, boolean debugEnabled) {
        if (instance != null) {
            throw new IllegalStateException("An instance of DeltaExecutor already exists.");
        }

        instance = new DeltaExecutor(logger, coreThreadCount, maxThreadCount, idleThreadTimeout, niceShutdownPasses,
                debugEnabled);
    }

    private volatile boolean acceptingNewTasks;
    private volatile boolean debugEnabled;
    private final Logger logger;
    private final int niceShutdownPasses;
    private final ThreadPoolExecutor threadPoolExecutor;
    private final ConcurrentHashMap<Long, AsyncTask> taskMap;

    private DeltaExecutor(Logger logger, int coreThreadCount, int maxThreadCount, int idleThreadTimeout,
            int niceShutdownPasses, boolean debugEnabled) {
        this.logger = logger;
        this.niceShutdownPasses = niceShutdownPasses;
        this.debugEnabled = debugEnabled;
        this.taskMap = new ConcurrentHashMap<>();

        this.threadPoolExecutor = new ThreadPoolExecutor(coreThreadCount, maxThreadCount, idleThreadTimeout,
                TimeUnit.MINUTES, new LinkedBlockingQueue<>(),
                new ThreadFactoryBuilder().setNameFormat("delta-executor-pool-%d").build());

        this.acceptingNewTasks = true;
    }

    /**
     * @return {@code true} if debug is enabled.
     */
    public boolean isDebugEnabled() {
        return debugEnabled;
    }

    /**
     * @param debugEnabled {@code true} if debug should be enabled.
     */
    public void setDebugEnabled(boolean debugEnabled) {
        this.debugEnabled = debugEnabled;
    }

    /**
     * @return {@code true} if the executor will accept tasks to be run
     * asynchronously. If this method returns {@code false}, tasks can
     * still be executed using {@link #execute(Runnable)}, but they
     * will run synchronously.
     */
    public boolean isAcceptingNewTasks() {
        return acceptingNewTasks;
    }

    /**
     * If the executor is accepting new tasks (can be checked using
     * {@link #isAcceptingNewTasks()}, the runnable passed to this method
     * will be added to a queue to run asynchronously. If the executor is
     * not accepting new tasks, the runnable passed will run synchronously.
     *
     * @param toExecute Runnable to execute
     * @return Assigned task ID.
     */
    public long execute(Runnable toExecute) {
        AsyncTask task = new AsyncTask(toExecute);
        Long id = task.getId();

        taskMap.put(task.getId(), task);
        debug("Queued task #" + id);

        if (acceptingNewTasks) {
            threadPoolExecutor.execute(() -> runTask(id));
        } else {
            runTask(id);
        }

        return id;
    }

    /**
     * Attempts to cancel the task with the passed task ID.
     *
     * @param taskId Assigned task ID.
     * @return {@link CancelResult#CANCELLED} is the task was successfully
     * cancelled. {@link CancelResult#RUNNING} if the task is currently
     * being executed. {@link CancelResult#NOT_FOUND} if the task ID is
     * unknown (either the task has completed or never existed).
     */
    public CancelResult cancel(long taskId) {
        AsyncTask task = taskMap.get(taskId);

        if (task != null) {
            if (task.switchToCancelled()) {
                debug("Task #" + taskId + " has been cancelled.");
                return CancelResult.CANCELLED;
            } else {
                debug("Task #" + taskId + " could not be cancelled.");
                return CancelResult.RUNNING;
            }
        }

        debug("Task #" + taskId + " was not found during an attempt to switchToCancelled it.");
        return CancelResult.NOT_FOUND;
    }

    /**
     * Attempts to shut down the executor nicely by waiting for all tasks.
     * If the executor fails to shutdown nicely, it will be shutdown forcibly.
     * In which case, worker threads will be interrupted and queued tasks will
     * be run synchronously.
     */
    public void shutdown() {
        if (!acceptingNewTasks) {
            return;
        }

        info("Shutting down DeltaExecutor ...");

        acceptingNewTasks = false;
        threadPoolExecutor.shutdown();

        try {
            if (!shutdownNicely()) {
                shutdownForcibly();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            shutdownForcibly();
        }

        info("All tasks executed from this point on will be run synchronously.");
        info("Running tasks synchronously means your server may run slower if other plugins "
                + "relying on DeltaExecutor use it assuming it is running tasks not synchronously.");
    }

    private void runTask(long taskId) {
        AsyncTask asyncTask = taskMap.get(taskId);

        if (asyncTask == null) {
            severe("Task #" + taskId + " not found. Was it added it using DeltaExecutor#execute()?");
            return;
        }

        try {
            if (asyncTask.switchToRunning()) {
                debug("Running task #" + taskId);
                asyncTask.getRunnable().run();
                debug("Completed task #" + taskId);
            }
        } catch (Exception ex) {
            severe("Exception in task #" + taskId);
            ex.printStackTrace();
        } finally {
            debug("Cleaning up task #" + taskId);
            taskMap.remove(taskId);
        }
    }

    private boolean shutdownNicely() throws InterruptedException {
        boolean terminated = false;

        for (int i = 1; i <= niceShutdownPasses && !terminated; ++i) {
            info("(Pass " + i + " of " + niceShutdownPasses + ") "
                    + "Waiting 30 seconds for all tasks to complete ...");

            terminated = threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS);

            if (terminated) {
                info("DeltaExecutor has been shutdown nicely.");
            } else {
                info("Tasks Remaining: " + taskMap.size());
            }
        }

        return terminated;
    }

    private void shutdownForcibly() {
        info("Failed to shutdown DeltaExecutor nicely. Shutting down forcibly ...");

        for (Runnable runnable : threadPoolExecutor.shutdownNow()) {
            try {
                runnable.run();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        info("DeltaExecutor has been shutting down forcibly.");
    }

    private void info(String input) {
        logger.info(input);
    }

    private void severe(String input) {
        logger.severe(input);
    }

    private void debug(String input) {
        if (debugEnabled) {
            logger.info("[Debug] " + input);
        }
    }
}