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 boolean systemHaltDisabled = false;
034 private static volatile ExitException firstExitException;
035 private static volatile HaltException firstHaltException;
036
037 public static class ExitException extends RuntimeException {
038 private static final long serialVersionUID = 1L;
039 public final int status;
040
041 public ExitException(int status, String msg) {
042 super(msg);
043 this.status = status;
044 }
045 }
046
047 public static class HaltException extends RuntimeException {
048 private static final long serialVersionUID = 1L;
049 public final int status;
050
051 public HaltException(int status, String msg) {
052 super(msg);
053 this.status = status;
054 }
055 }
056
057 /**
058 * Disable the use of System.exit for testing.
059 */
060 public static void disableSystemExit() {
061 systemExitDisabled = true;
062 }
063
064 /**
065 * Disable the use of {@code Runtime.getRuntime().halt() } for testing.
066 */
067 public static void disableSystemHalt() {
068 systemHaltDisabled = true;
069 }
070
071 /**
072 * @return true if terminate has been called
073 */
074 public static boolean terminateCalled() {
075 // Either we set this member or we actually called System#exit
076 return firstExitException != null;
077 }
078
079 /**
080 * @return true if halt has been called
081 */
082 public static boolean haltCalled() {
083 return firstHaltException != null;
084 }
085
086 /**
087 * @return the first ExitException thrown, null if none thrown yet
088 */
089 public static ExitException getFirstExitException() {
090 return firstExitException;
091 }
092
093 /**
094 * @return the first {@code HaltException} thrown, null if none thrown yet
095 */
096 public static HaltException getFirstHaltException() {
097 return firstHaltException;
098 }
099
100 /**
101 * Reset the tracking of process termination. This is for use in unit tests
102 * where one test in the suite expects an exit but others do not.
103 */
104 public static void resetFirstExitException() {
105 firstExitException = null;
106 }
107
108 public static void resetFirstHaltException() {
109 firstHaltException = null;
110 }
111
112 /**
113 * Terminate the current process. Note that terminate is the *only* method
114 * that should be used to terminate the daemon processes.
115 *
116 * @param status
117 * exit code
118 * @param msg
119 * message used to create the {@code ExitException}
120 * @throws ExitException
121 * if System.exit is disabled for test purposes
122 */
123 public static void terminate(int status, String msg) throws ExitException {
124 LOG.info("Exiting with status " + status);
125 if (systemExitDisabled) {
126 ExitException ee = new ExitException(status, msg);
127 LOG.fatal("Terminate called", ee);
128 if (null == firstExitException) {
129 firstExitException = ee;
130 }
131 throw ee;
132 }
133 System.exit(status);
134 }
135
136 /**
137 * Forcibly terminates the currently running Java virtual machine.
138 *
139 * @param status
140 * exit code
141 * @param msg
142 * message used to create the {@code HaltException}
143 * @throws HaltException
144 * if Runtime.getRuntime().halt() is disabled for test purposes
145 */
146 public static void halt(int status, String msg) throws HaltException {
147 LOG.info("Halt with status " + status + " Message: " + msg);
148 if (systemHaltDisabled) {
149 HaltException ee = new HaltException(status, msg);
150 LOG.fatal("Halt called", ee);
151 if (null == firstHaltException) {
152 firstHaltException = ee;
153 }
154 throw ee;
155 }
156 Runtime.getRuntime().halt(status);
157 }
158
159 /**
160 * Like {@link terminate(int, String)} but uses the given throwable to
161 * initialize the ExitException.
162 *
163 * @param status
164 * @param t
165 * throwable used to create the ExitException
166 * @throws ExitException
167 * if System.exit is disabled for test purposes
168 */
169 public static void terminate(int status, Throwable t) throws ExitException {
170 terminate(status, StringUtils.stringifyException(t));
171 }
172
173 /**
174 * Forcibly terminates the currently running Java virtual machine.
175 *
176 * @param status
177 * @param t
178 * @throws ExitException
179 */
180 public static void halt(int status, Throwable t) throws HaltException {
181 halt(status, StringUtils.stringifyException(t));
182 }
183
184 /**
185 * Like {@link terminate(int, String)} without a message.
186 *
187 * @param status
188 * @throws ExitException
189 * if System.exit is disabled for test purposes
190 */
191 public static void terminate(int status) throws ExitException {
192 terminate(status, "ExitException");
193 }
194
195 /**
196 * Forcibly terminates the currently running Java virtual machine.
197 * @param status
198 * @throws ExitException
199 */
200 public static void halt(int status) throws HaltException {
201 halt(status, "HaltException");
202 }
203 }