001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.util;
019
020import org.apache.commons.logging.Log;
021import org.apache.commons.logging.LogFactory;
022import org.apache.hadoop.classification.InterfaceAudience;
023import org.apache.hadoop.classification.InterfaceStability;
024
025/**
026 * Facilitates hooking process termination for tests and debugging.
027 */
028@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
029@InterfaceStability.Unstable
030public final class ExitUtil {
031  private final static Log LOG = LogFactory.getLog(ExitUtil.class.getName());
032  private static volatile boolean systemExitDisabled = false;
033  private static volatile ExitException firstExitException;
034
035  public static class ExitException extends RuntimeException {
036    private static final long serialVersionUID = 1L;
037    public final int status;
038
039    public ExitException(int status, String msg) {
040      super(msg);
041      this.status = status;
042    }
043  }
044
045  /**
046   * Disable the use of System.exit for testing.
047   */
048  public static void disableSystemExit() {
049    systemExitDisabled = true;
050  }
051
052  /**
053   * @return true if terminate has been called
054   */
055  public static boolean terminateCalled() {
056    // Either we set this member or we actually called System#exit
057    return firstExitException != null;
058  }
059
060  /**
061   * @return the first ExitException thrown, null if none thrown yet
062   */
063  public static ExitException getFirstExitException() {
064    return firstExitException;
065  }
066
067  /**
068   * Reset the tracking of process termination. This is for use
069   * in unit tests where one test in the suite expects an exit
070   * but others do not.
071   */
072  public static void resetFirstExitException() {
073    firstExitException = null;
074  }
075
076  /**
077   * Terminate the current process. Note that terminate is the *only* method
078   * that should be used to terminate the daemon processes.
079   * @param status exit code
080   * @param msg message used to create the ExitException
081   * @throws ExitException if System.exit is disabled for test purposes
082   */
083  public static void terminate(int status, String msg) throws ExitException {
084    LOG.info("Exiting with status " + status);
085    if (systemExitDisabled) {
086      ExitException ee = new ExitException(status, msg);
087      LOG.fatal("Terminate called", ee);
088      if (null == firstExitException) {
089        firstExitException = ee;
090      }
091      throw ee;
092    }
093    System.exit(status);
094  }
095
096  /**
097   * Like {@link terminate(int, String)} but uses the given throwable to
098   * initialize the ExitException.
099   * @param status
100   * @param t throwable used to create the ExitException
101   * @throws ExitException if System.exit is disabled for test purposes
102   */
103  public static void terminate(int status, Throwable t) throws ExitException {
104    terminate(status, StringUtils.stringifyException(t));
105  }
106
107  /**
108   * Like {@link terminate(int, String)} without a message.
109   * @param status
110   * @throws ExitException if System.exit is disabled for test purposes
111   */
112  public static void terminate(int status) throws ExitException {
113    terminate(status, "ExitException");
114  }
115}