// $Id: ExitLogger.java,v 1.2 2005/03/05 18:39:04 Dave Exp $ /* * ExitLogger.java * Copyright (C) 2005 David Clausen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later * version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package net.dclausen.util; import java.security.Permission; import java.util.logging.Level; import java.util.logging.Logger; /** * A simple {@link java.lang.SecurityManager} which logs calls to {@link #checkExit}. * When an instance of ExitLogger is installed as the System's default * SecurityManager, it acts as a trap for {@link System#exit} and * {@link Runtime#exit}, because those two functions both call * System.getSecurityManager().checkExit() before terminating the JVM. *

* All permission checks (including checkExit) are forwarded to the * System's previous SecurityManager, if one was set. *

* Use the static {@link #install} method to install a new instance of this class * as the System's SecurityManager. * * @see System#exit(int) * @see Runtime#exit(int) * @see SecurityManager * @see System#setSecurityManager(SecurityManager) * @see Logger */ public class ExitLogger extends SecurityManager { /** * Install an instance of ExitLogger as the System's SecurityManager. * The existing SecurityManager, if one exists, will be used as a * delegate for all permission checks. * * @param logger the Logger to use when logging calls to System.exit() and * Runtime.exit() * @param level the level to use * @see System#setSecurityManager * @throws SecurityException if the current SecurityManager doesn't want to be * replaced */ public static void install(Logger logger, Level level) { new ExitLogger(logger, level); } private final Logger logger; private final Level level; private final SecurityManager parent; private ExitLogger(Logger logger, Level level) { if ((logger == null) || (level == null)) { throw new NullPointerException(); } this.logger = logger; this.level = level; parent = System.getSecurityManager(); // race condition System.setSecurityManager(this); } /** * Delegate the check to the parent SecurityManager, if there is one. */ public void checkPermission(Permission perm) { if (parent != null) { parent.checkPermission(perm); } } /** * Use stack introspection to identify the calling class/method/line, and * log it. This method is called by Runtime.exit(int) before * actually stopping the JVM. */ public void checkExit(int status) { Exception e = new Exception(); StackTraceElement[] trace = e.getStackTrace(); String method = ""; if ((trace != null) && (trace.length > 2)) { int idx = 2; // System.exit() calls Runtime.exit(), so if that is how // we got here, we need to go up one more stack frame // to get into user-level code if ((trace.length > 3) && "java.lang.System".equals(trace[2].getClassName()) && "exit".equals(trace[2].getMethodName())) { idx++; } method = trace[idx].toString(); } logger.log(level, "exit({0}) called at {1}", new Object[] { new Integer(status), method }); super.checkExit(status); } }