Package com.sleepycat.je.utilint

Source Code of com.sleepycat.je.utilint.StoppableThread$UncaughtHandler

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2011 Oracle and/or its affiliates.  All rights reserved.
*
*/

package com.sleepycat.je.utilint;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sleepycat.je.DbInternal;
import com.sleepycat.je.EnvironmentFailureException;
import com.sleepycat.je.ExceptionListener;
import com.sleepycat.je.dbi.EnvironmentFailureReason;
import com.sleepycat.je.dbi.EnvironmentImpl;

/**
* A StoppableThread is a daemon that obeys the following mandates:
* - it sets the daemon property for the thread
* - an uncaught exception handler is always registered
* - the thread registers with the JE exception listener mechanism.
* - its shutdown method can only be executed once. StoppableThreads are not
*   required to implement shutdown() methods, because in some cases their
*   shutdown processing must be coordinated by an owning, parent thread.
*
* StoppableThread is an alternative to the DaemonThread. It also assumes that
* the thread's run() method may be more complex that that of the work-queue,
* task oriented DaemonThread.
*
* A StoppableThread's run method should catch and handle all exceptions. By
* default, unhandled exceptions are considered programming errors, and
* invalidate the environment, but StoppableThreads may supply alternative
* uncaught exception handling.
*
* StoppableThreads usually are created with an EnvironmentImpl, but on
* occasion an environment may not be available (for components that can
* execute without an environment). In that case, the thread obviously does not
* invalidate the environment.
*
* Note that the StoppableThread.cleanup must be invoked upon, or soon after,
* thread exit.
*/
public abstract class StoppableThread extends Thread
    implements ExceptionListenerUser {

    private ExceptionListener exceptionListener;

    /* The environment, if any, that's associated with this thread. */
    protected final EnvironmentImpl envImpl;

    /*
     * Shutdown can only be executed once. The shutdown field protects against
     * multiple invocations.
     */
    private final AtomicBoolean shutdown = new AtomicBoolean(false);

    /* The exception (if any) that forced this node to shut down. */
    private Exception savedShutdownException = null;

    /* Total cpu time used by thread */
    private long totalCpuTime = -1;

    /* Total user time used by thread */
    private long totalUserTime = -1;

    protected StoppableThread(final String threadName) {
        this(null, null, null, threadName);
    }

    protected StoppableThread(final EnvironmentImpl envImpl,
                              final String threadName) {
        this(envImpl, null /* handler */, null /* runnable */,threadName);
    }

    protected StoppableThread(final EnvironmentImpl envImpl,
                              final UncaughtExceptionHandler handler,
                              final String threadName) {
        this(envImpl, handler, null /* runnable */, threadName);
    }

    protected StoppableThread(final EnvironmentImpl envImpl,
                              final UncaughtExceptionHandler handler,
                              final Runnable runnable,
                              final String threadName) {
        super(null, runnable, threadName);
        this.envImpl = envImpl;

        /*
         * Set the daemon property so this thread will not hang up the
         * application.
         */
        setDaemon(true);

        /*
         * Register for updates to the exception listener mechanism. Check if
         * the environmentImpl is null, because there are a few cases where a
         * StoppableThread is created for components that work both in
         * replicated nodes and independently.
         */
        if (envImpl != null) {
            envImpl.registerExceptionListenerUser(this);
        }
        setUncaughtExceptionHandler
            ((handler == null) ? new UncaughtHandler() : handler);
    }

    /**
     * @return a logger to use when logging uncaught exceptions.
     */
    abstract protected Logger getLogger();

    /**
     * Returns the exception if any that provoked the shutdown
     *
     * @return the exception, or null if it was a normal shutdown
     */
    public Exception getSavedShutdownException() {
        return savedShutdownException;
    }

    public void saveShutdownException(Exception shutdownException) {
        savedShutdownException = shutdownException;
    }

    public boolean isShutdown() {
        return shutdown.get();
    }

    /**
     * Set every time a new exception listener is registered.
     */
    public void setExceptionListener(ExceptionListener exceptionListener) {
        this.exceptionListener = exceptionListener;
    }

    /**
     * Shutdown methods should only be executed once.
     *
     * @return true if shutdown is already set.
     */
    protected boolean shutdownDone() {
        return (!shutdown.compareAndSet(false, true));
    }

    /**
     * Must be invoked upon, or soon after, exit from the thread to perform
     * any cleanup, and ensure that any allocated resources are freed.
     */
    protected void cleanup() {
        if (envImpl != null) {
            envImpl.unregisterExceptionListenerUser(this);
        }
    }

    /**
     * An uncaught exception should invalidate the environment. Check if the
     * environmentImpl is null, because there are a few cases where a
     * StoppableThread is created for components that work both in replicated
     * nodes and independently.
     */
    private class UncaughtHandler implements UncaughtExceptionHandler {

        /**
         * When an uncaught exception occurs, log it, publish it to the
         * exception handler, and invalidate the environment.
         */
        public void uncaughtException(Thread t, Throwable e) {
            Logger useLogger = getLogger();
            if (useLogger != null) {
                String envName = (envImpl == null)? "" : envImpl.getName();
                String message = envName + ":" + t.getName() +
                    " exited unexpectedly with exception " + e;
                if (e != null) {
                    message += LoggerUtils.getStackTrace(e);
                }

                if (envImpl != null) {
                    /*
                     * If we have an environment, log this to all three
                     * handlers.
                     */
                    LoggerUtils.severe(useLogger, envImpl, message);
                } else {
                    /*
                     * We don't have an environment, but at least log this
                     * to the console.
                     */
                    useLogger.log(Level.SEVERE, message);
                }
            }

            if ((exceptionListener != null) && (e instanceof Exception)) {
                exceptionListener.exceptionThrown
                    (DbInternal.makeExceptionEvent((Exception) e,
                                                   t.getName()));
            }

            if (envImpl == null) {
                return;
            }

            /*
             * If not already invalid, invalidate environment by creating an
             * EnvironmentFailureException.
             */
            if (envImpl.isValid()) {

                /*
                 * Create the exception to invalidate the environment, but do
                 * not throw it since the handle is invoked in some internal
                 * JVM thread and the exception is not meaningful to the
                 * invoker.
                 */
                new EnvironmentFailureException
                    (envImpl, EnvironmentFailureReason.UNCAUGHT_EXCEPTION, e);
            }
        }
    }

    /**
     * This method is invoked from another thread of control to shutdown this
     * thread. It first tries a "soft" shutdown by invoking
     * <code>initiateSoftShutdown()</code>. If the thread does not exit on its
     * own in <code>waitMs</code> the thread is interrupted.
     * <code>waitMs</code> is determined by the technique used for the soft
     * shutdown. For example, if the thread polls on a periodic basis, it could
     * span one or more polling periods.
     *
     * All Stoppable threads are expected to catch an interrupt, clean up and
     * then exit.
     *
     * @param logger the logger on which to log messages
     */
    public void shutdownThread(Logger logger) {

        /*
         * Save resource usage, since it will not be available once the
         * thread has exited.
         */
        //ONEJAVA/*
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        totalCpuTime = threadBean.getThreadCpuTime(getId());
        totalUserTime = threadBean.getThreadUserTime(getId());
        //ONEJAVA*/

        if (Thread.currentThread() == this) {
            return;
        }

        try {
            int waitMs = initiateSoftShutdown();

            /* Wait for a soft shutdown to take effect */
            if (waitMs >= 0) {
                join(waitMs);
            }

            if (isAlive()) {
                LoggerUtils.warning(logger, envImpl,
                                    "Soft shutdown failed for thread:" +
                                    this + " resorting to interrupt.");
                interrupt();

                /*
                 * Wait indefinitely for this join operation, since the thread
                 * must make provision to handle and exit on an interrupt.
                 */
                join();
                LoggerUtils.warning
                    (logger, envImpl, this + " shutdown via interrupt.");
            } else {
                LoggerUtils.fine(logger, envImpl, this + " has exited.");
            }
        } catch (InterruptedException e1) {
            LoggerUtils.warning(logger, envImpl,
                                "Interrupted while waiting to join thread:" +
                                this);
        }
    }

    /**
     * Threads that use shutdownThread() must define this method. It's invoked
     * by shutdownThread as an attempt at a soft shutdown.
     *
     * This method makes provisions for this thread to exit on its own. The
     * technique used to make the thread exit can vary based upon the nature of
     * the service being provided by the thread. For example, the thread may be
     * known to poll some shutdown flag on a periodic basis, or it may detect
     * that a channel that it waits on has been closed by this method.
     *
     * @return the amount of time in ms that the shutdownThread method will
     * wait for the thread to exit. A -ve value means that the method will not
     * wait. A zero value means it will wait indefinitely.
     */
    protected int initiateSoftShutdown() {
        return -1;
    }

    /**
     * Returns the total cpu time associated with the thread, after the thread
     * has been shutdown.
     */
    public long getTotalCpuTime() {
        return totalCpuTime;
    }

    /**
     * Returns the total cpu time associated with the thread, after the thread
     * has been shutdown.
     */
    public long getTotalUserTime() {
        return totalUserTime;
    }
}
TOP

Related Classes of com.sleepycat.je.utilint.StoppableThread$UncaughtHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.