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 }