morphy.service.ThreadService.java Source code

Java tutorial

Introduction

Here is the source code for morphy.service.ThreadService.java

Source

/*
 *   Morphy Open Source Chess Server
 *   Copyright (C) 2008-2010, 2016  http://code.google.com/p/morphy-chess-server/
 *
 *  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 morphy.service;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Date;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import morphy.Morphy;
import morphy.properties.MorphyPreferences;
import morphy.properties.PreferenceKeys;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ThreadService implements Service {
    protected static final class RunnableExceptionDecorator implements Runnable {
        protected Runnable runnable;

        public RunnableExceptionDecorator(Runnable runnable) {
            this.runnable = runnable;
        }

        public void run() {
            try {
                runnable.run();
            } catch (Throwable t) {
                if (LOG.isErrorEnabled())
                    LOG.error("Error in ThreadService Runnable.", t);
            }
        }

    }

    protected static final Log LOG = LogFactory.getLog(ThreadService.class);

    public String THREAD_DUMP_FILE_PATH = null;

    private static final ThreadService instance = new ThreadService();

    public static ThreadService getInstance() {
        return instance;
    }

    /**
     * Dumps stack traces of all threads to threaddump.txt.
     */
    public void threadDump() {
        LOG.error("All threads are in use. Logging the thread stack trace to threaddump.txt and exiting.");
        final ThreadMXBean threads = ManagementFactory.getThreadMXBean();
        long[] threadIds = threads.getAllThreadIds();
        PrintWriter printWriter = null;
        try {
            printWriter = new PrintWriter(new FileWriter(THREAD_DUMP_FILE_PATH, false));
            printWriter.println("Morphy ThreadService initiated dump " + new Date());
            for (long threadId : threadIds) {
                ThreadInfo threadInfo = threads.getThreadInfo(threadId, 10);
                printWriter.println("Thread " + threadInfo.getThreadName() + " Block time:"
                        + threadInfo.getBlockedTime() + " Block count:" + threadInfo.getBlockedCount()
                        + " Lock name:" + threadInfo.getLockName() + " Waited Count:" + threadInfo.getWaitedCount()
                        + " Waited Time:" + threadInfo.getWaitedTime() + " Is Suspended:"
                        + threadInfo.isSuspended());
                StackTraceElement[] stackTrace = threadInfo.getStackTrace();
                for (StackTraceElement element : stackTrace) {
                    printWriter.println(element);
                }

            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            if (printWriter != null) {
                try {
                    printWriter.flush();
                    printWriter.close();
                } catch (Exception e2) {
                }
            }
        }
    }

    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(200);

    protected boolean isDisposed = false;

    private ThreadService() {
        MorphyPreferences morphyPreferences = Morphy.getInstance().getMorphyPreferences();

        THREAD_DUMP_FILE_PATH = Morphy.getInstance().getMorphyFileProvider().getUserDirectory()
                + "/logs/threaddump_" + System.currentTimeMillis() + ".txt";

        executor.setCorePoolSize(morphyPreferences.getInt(PreferenceKeys.ThreadServiceCoreThreads));
        executor.setMaximumPoolSize(morphyPreferences.getInt(PreferenceKeys.ThreadServiceMaxThreads));
        executor.setKeepAliveTime(morphyPreferences.getInt(PreferenceKeys.ThreadServiceKeepAlive),
                TimeUnit.SECONDS);
        executor.prestartAllCoreThreads();
        if (LOG.isInfoEnabled()) {
            LOG.info("Initialized ThreadService");
        }
    }

    public void dispose() {
        executor.shutdownNow();
        isDisposed = true;
    }

    public ScheduledThreadPoolExecutor getExecutor() {
        return executor;
    }

    /**
     * Executes a runnable asynch in a controlled way. Exceptions are monitored
     * and displayed if they occur.
     */
    public void run(Runnable runnable) {
        if (!Morphy.getInstance().isShutdown()) {
            try {
                executor.execute(new RunnableExceptionDecorator(runnable));
            } catch (RejectedExecutionException rej) {
                if (!Morphy.getInstance().isShutdown()) {
                    LOG.error("Error executing runnable: ", rej);
                    threadDump();
                    if (LOG.isErrorEnabled())
                        LOG.error("ThreadService has no more threads. A thread dump can be found at "
                                + THREAD_DUMP_FILE_PATH, rej);
                }
            }
        } else {
            if (LOG.isInfoEnabled()) {
                LOG.info("Vetoing runnable in ThreadService." + runnable);
            }
        }
    }

    /**
     * Runs the runnable one time after a delay. Exceptions are monitored and
     * displayed if they occur.
     * 
     * @param delay
     *            Delay in milliseconds
     * @param runnable
     *            The runnable.
     * @return The Future, may return null if there was an error scheduling the
     *         Runnable or if execution was vetoed.
     */
    public Future<?> scheduleOneShot(long delay, Runnable runnable) {
        if (!Morphy.getInstance().isShutdown()) {
            try {
                return executor.schedule(new RunnableExceptionDecorator(runnable), delay, TimeUnit.MILLISECONDS);
            } catch (RejectedExecutionException rej) {
                if (!Morphy.getInstance().isShutdown()) {
                    LOG.error("Error executing runnable in scheduleOneShot: ", rej);
                    threadDump();
                    if (LOG.isErrorEnabled())
                        LOG.error("ThreadService has no more threads. A thread dump can be found at "
                                + THREAD_DUMP_FILE_PATH);
                }
                return null;
            }
        } else {
            LOG.info("Vetoing runnable " + runnable + " because Morphy is shutdown.");
            return null;
        }
    }
}