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 java.io.PrintWriter; 021 import java.io.StringWriter; 022 023 import org.apache.commons.logging.Log; 024 import org.apache.commons.logging.LogFactory; 025 import org.apache.hadoop.classification.InterfaceAudience; 026 import org.apache.hadoop.classification.InterfaceStability; 027 028 /** 029 * Facilitates hooking process termination for tests and debugging. 030 */ 031 @InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"}) 032 @InterfaceStability.Unstable 033 public final class ExitUtil { 034 private final static Log LOG = LogFactory.getLog(ExitUtil.class.getName()); 035 private static volatile boolean systemExitDisabled = false; 036 private static volatile ExitException firstExitException; 037 038 public static class ExitException extends RuntimeException { 039 private static final long serialVersionUID = 1L; 040 public final int status; 041 042 public ExitException(int status, String msg) { 043 super(msg); 044 this.status = status; 045 } 046 } 047 048 /** 049 * Disable the use of System.exit for testing. 050 */ 051 public static void disableSystemExit() { 052 systemExitDisabled = true; 053 } 054 055 /** 056 * @return true if terminate has been called 057 */ 058 public static boolean terminateCalled() { 059 // Either we set this member or we actually called System#exit 060 return firstExitException != null; 061 } 062 063 /** 064 * @return the first ExitException thrown, null if none thrown yet 065 */ 066 public static ExitException getFirstExitException() { 067 return firstExitException; 068 } 069 070 /** 071 * Reset the tracking of process termination. This is for use 072 * in unit tests where one test in the suite expects an exit 073 * but others do not. 074 */ 075 public static void resetFirstExitException() { 076 firstExitException = null; 077 } 078 079 /** 080 * Terminate the current process. Note that terminate is the *only* method 081 * that should be used to terminate the daemon processes. 082 * @param status exit code 083 * @param msg message used to create the ExitException 084 * @throws ExitException if System.exit is disabled for test purposes 085 */ 086 public static void terminate(int status, String msg) throws ExitException { 087 LOG.info("Exiting with status " + status); 088 if (systemExitDisabled) { 089 ExitException ee = new ExitException(status, msg); 090 LOG.fatal("Terminate called", ee); 091 if (null == firstExitException) { 092 firstExitException = ee; 093 } 094 throw ee; 095 } 096 System.exit(status); 097 } 098 099 /** 100 * Like {@link terminate(int, String)} but uses the given throwable to 101 * initialize the ExitException. 102 * @param status 103 * @param t throwable used to create the ExitException 104 * @throws ExitException if System.exit is disabled for test purposes 105 */ 106 public static void terminate(int status, Throwable t) throws ExitException { 107 StringWriter sw = new StringWriter(); 108 t.printStackTrace(new PrintWriter(sw)); 109 terminate(status, "Fatal exception with message " + t.getMessage() + 110 "\nstack trace\n" + sw.toString()); 111 } 112 113 /** 114 * Like {@link terminate(int, String)} without a message. 115 * @param status 116 * @throws ExitException if System.exit is disabled for test purposes 117 */ 118 public static void terminate(int status) throws ExitException { 119 terminate(status, "ExitException"); 120 } 121 }