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 */
018 package org.apache.hadoop.util;
019
020 import org.apache.commons.logging.Log;
021 import org.apache.commons.logging.LogFactory;
022 import org.apache.hadoop.classification.InterfaceAudience;
023 import 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
030 public 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 * Clear the previous exit record.
078 */
079 public static void clearTerminateCalled() {
080 resetFirstExitException();
081 }
082
083 /**
084 * Terminate the current process. Note that terminate is the *only* method
085 * that should be used to terminate the daemon processes.
086 * @param status exit code
087 * @param msg message used to create the ExitException
088 * @throws ExitException if System.exit is disabled for test purposes
089 */
090 public static void terminate(int status, String msg) throws ExitException {
091 LOG.info("Exiting with status " + status);
092 if (systemExitDisabled) {
093 ExitException ee = new ExitException(status, msg);
094 LOG.fatal("Terminate called", ee);
095 if (null == firstExitException) {
096 firstExitException = ee;
097 }
098 throw ee;
099 }
100 System.exit(status);
101 }
102
103 /**
104 * Like {@link terminate(int, String)} but uses the given throwable to
105 * initialize the ExitException.
106 * @param status
107 * @param t throwable used to create the ExitException
108 * @throws ExitException if System.exit is disabled for test purposes
109 */
110 public static void terminate(int status, Throwable t) throws ExitException {
111 terminate(status, StringUtils.stringifyException(t));
112 }
113
114 /**
115 * Like {@link terminate(int, String)} without a message.
116 * @param status
117 * @throws ExitException if System.exit is disabled for test purposes
118 */
119 public static void terminate(int status) throws ExitException {
120 terminate(status, "ExitException");
121 }
122 }