org.apache.hadoop.util.ExitUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.util.ExitUtil.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.util;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Facilitates hooking process termination for tests, debugging
 * and embedding.
 * 
 * Hadoop code that attempts to call {@link System#exit(int)} 
 * or {@link Runtime#halt(int)} MUST invoke it via these methods.
 */
@InterfaceAudience.LimitedPrivate({ "HDFS", "MapReduce", "YARN" })
@InterfaceStability.Unstable
public final class ExitUtil {
    private static final Logger LOG = LoggerFactory.getLogger(ExitUtil.class.getName());
    private static volatile boolean systemExitDisabled = false;
    private static volatile boolean systemHaltDisabled = false;
    private static volatile ExitException firstExitException;
    private static volatile HaltException firstHaltException;
    /** Message raised from an exit exception if none were provided: {@value}. */
    public static final String EXIT_EXCEPTION_MESSAGE = "ExitException";
    /** Message raised from a halt exception if none were provided: {@value}. */
    public static final String HALT_EXCEPTION_MESSAGE = "HaltException";

    private ExitUtil() {
    }

    /**
     * An exception raised when a call to {@link #terminate(int)} was
     * called and system exits were blocked.
     */
    public static class ExitException extends RuntimeException implements ExitCodeProvider {
        private static final long serialVersionUID = 1L;
        /**
         * The status code.
         */
        public final int status;

        public ExitException(int status, String msg) {
            super(msg);
            this.status = status;
        }

        public ExitException(int status, String message, Throwable cause) {
            super(message, cause);
            this.status = status;
        }

        public ExitException(int status, Throwable cause) {
            super(cause);
            this.status = status;
        }

        @Override
        public int getExitCode() {
            return status;
        }

        /**
         * String value does not include exception type, just exit code and message.
         * @return the exit code and any message
         */
        @Override
        public String toString() {
            String message = getMessage();
            if (message == null) {
                message = super.toString();
            }
            return Integer.toString(status) + ": " + message;
        }
    }

    /**
     * An exception raised when a call to {@link #terminate(int)} was
     * called and system halts were blocked.
     */
    public static class HaltException extends RuntimeException implements ExitCodeProvider {
        private static final long serialVersionUID = 1L;
        public final int status;

        public HaltException(int status, Throwable cause) {
            super(cause);
            this.status = status;
        }

        public HaltException(int status, String msg) {
            super(msg);
            this.status = status;
        }

        public HaltException(int status, String message, Throwable cause) {
            super(message, cause);
            this.status = status;
        }

        @Override
        public int getExitCode() {
            return status;
        }

        /**
         * String value does not include exception type, just exit code and message.
         * @return the exit code and any message
         */
        @Override
        public String toString() {
            String message = getMessage();
            if (message == null) {
                message = super.toString();
            }
            return Integer.toString(status) + ": " + message;
        }

    }

    /**
     * Disable the use of System.exit for testing.
     */
    public static void disableSystemExit() {
        systemExitDisabled = true;
    }

    /**
     * Disable the use of {@code Runtime.getRuntime().halt() } for testing.
     */
    public static void disableSystemHalt() {
        systemHaltDisabled = true;
    }

    /**
     * @return true if terminate has been called.
     */
    public static boolean terminateCalled() {
        // Either we set this member or we actually called System#exit
        return firstExitException != null;
    }

    /**
     * @return true if halt has been called.
     */
    public static boolean haltCalled() {
        return firstHaltException != null;
    }

    /**
     * @return the first ExitException thrown, null if none thrown yet.
     */
    public static ExitException getFirstExitException() {
        return firstExitException;
    }

    /**
     * @return the first {@code HaltException} thrown, null if none thrown yet.
     */
    public static HaltException getFirstHaltException() {
        return firstHaltException;
    }

    /**
     * Reset the tracking of process termination. This is for use in unit tests
     * where one test in the suite expects an exit but others do not.
     */
    public static void resetFirstExitException() {
        firstExitException = null;
    }

    public static void resetFirstHaltException() {
        firstHaltException = null;
    }

    /**
     * Inner termination: either exit with the exception's exit code,
     * or, if system exits are disabled, rethrow the exception.
     * @param ee exit exception
     */
    public static synchronized void terminate(ExitException ee) throws ExitException {
        int status = ee.getExitCode();
        String msg = ee.getMessage();
        if (status != 0) {
            //exit indicates a problem, log it
            LOG.debug("Exiting with status {}: {}", status, msg, ee);
            LOG.info("Exiting with status {}: {}", status, msg);
        }
        if (systemExitDisabled) {
            LOG.error("Terminate called", ee);
            if (!terminateCalled()) {
                firstExitException = ee;
            }
            throw ee;
        }
        System.exit(status);
    }

    /**
     * Forcibly terminates the currently running Java virtual machine.
     * The exception argument is rethrown if JVM halting is disabled.
     * @param ee the exception containing the status code, message and any stack
     * trace.
     * @throws HaltException if {@link Runtime#halt(int)} is disabled.
     */
    public static synchronized void halt(HaltException ee) throws HaltException {
        int status = ee.getExitCode();
        String msg = ee.getMessage();
        try {
            if (status != 0) {
                //exit indicates a problem, log it
                LOG.debug("Halt with status {}: {}", status, msg, ee);
                LOG.info("Halt with status {}: {}", status, msg, msg);
            }
        } catch (Exception ignored) {
            // ignore exceptions here, as it may be due to an out of memory situation
        }
        if (systemHaltDisabled) {
            LOG.error("Halt called", ee);
            if (!haltCalled()) {
                firstHaltException = ee;
            }
            throw ee;
        }
        Runtime.getRuntime().halt(status);
    }

    /**
     * Like {@link #terminate(int, String)} but uses the given throwable to
     * build the message to display or throw as an
     * {@link ExitException}.
     * <p>
     * @param status exit code to use if the exception is not an ExitException.
     * @param t throwable which triggered the termination. If this exception
     * is an {@link ExitException} its status overrides that passed in.
     * @throws ExitException if {@link System#exit(int)}  is disabled.
     */
    public static void terminate(int status, Throwable t) throws ExitException {
        if (t instanceof ExitException) {
            terminate((ExitException) t);
        } else {
            terminate(new ExitException(status, t));
        }
    }

    /**
     * Forcibly terminates the currently running Java virtual machine.
     *
     * @param status exit code to use if the exception is not a HaltException.
     * @param t throwable which triggered the termination. If this exception
     * is a {@link HaltException} its status overrides that passed in.
     * @throws HaltException if {@link System#exit(int)}  is disabled.
     */
    public static void halt(int status, Throwable t) throws HaltException {
        if (t instanceof HaltException) {
            halt((HaltException) t);
        } else {
            halt(new HaltException(status, t));
        }
    }

    /**
     * Like {@link #terminate(int, Throwable)} without a message.
     *
     * @param status exit code
     * @throws ExitException if {@link System#exit(int)} is disabled.
     */
    public static void terminate(int status) throws ExitException {
        terminate(status, EXIT_EXCEPTION_MESSAGE);
    }

    /**
     * Terminate the current process. Note that terminate is the *only* method
     * that should be used to terminate the daemon processes.
     *
     * @param status exit code
     * @param msg message used to create the {@code ExitException}
     * @throws ExitException if {@link System#exit(int)} is disabled.
     */
    public static void terminate(int status, String msg) throws ExitException {
        terminate(new ExitException(status, msg));
    }

    /**
     * Forcibly terminates the currently running Java virtual machine.
     * @param status status code
     * @throws HaltException if {@link Runtime#halt(int)} is disabled.
     */
    public static void halt(int status) throws HaltException {
        halt(status, HALT_EXCEPTION_MESSAGE);
    }

    /**
     * Forcibly terminates the currently running Java virtual machine.
     * @param status status code
     * @param message message
     * @throws HaltException if {@link Runtime#halt(int)} is disabled.
     */
    public static void halt(int status, String message) throws HaltException {
        halt(new HaltException(status, message));
    }

    /**
     * Handler for out of memory events -no attempt is made here
     * to cleanly shutdown or support halt blocking; a robust
     * printing of the event to stderr is all that can be done.
     * @param oome out of memory event
     */
    public static void haltOnOutOfMemory(OutOfMemoryError oome) {
        //After catching an OOM java says it is undefined behavior, so don't
        //even try to clean up or we can get stuck on shutdown.
        try {
            System.err.println("Halting due to Out Of Memory Error...");
        } catch (Throwable err) {
            //Again we done want to exit because of logging issues.
        }
        Runtime.getRuntime().halt(-1);
    }
}