/* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program 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 General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ package com.l2jserver.communityserver; import java.util.logging.Logger; import com.l2jserver.communityserver.L2DatabaseFactory; import com.l2jserver.communityserver.model.L2Player; /** * * This class provides the functions for shutting down and restarting the server * It closes all open clientconnections and saves all data. * * @version $Revision: 1.2.4.5 $ $Date: 2005/03/27 15:29:09 $ */ public class Shutdown extends Thread { private static Logger _log = Logger.getLogger(Shutdown.class.getName()); private static Shutdown _instance; private static Shutdown _counterInstance = null; private int _secondsShut; private int _shutdownMode; public static final int SIGTERM = 0; public static final int GM_SHUTDOWN = 1; public static final int GM_RESTART = 2; public static final int ABORT = 3; private static final String[] MODE_TEXT = { "SIGTERM", "shutting down", "restarting", "aborting" }; /** * This function starts a shutdown countdown from Telnet (Copied from Function startShutdown()) * * @param ip IP Which Issued shutdown command * @param seconds seconds untill shutdown * @param restart true if the server will restart after shutdown */ public void startTelnetShutdown(String IP, int seconds, boolean restart) { _log.warning("IP: " + IP + " issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!"); //_an.announceToAll("Server is " + _modeText[shutdownMode] + " in "+seconds+ " seconds!"); if (restart) { _shutdownMode = GM_RESTART; } else { _shutdownMode = GM_SHUTDOWN; } if (_shutdownMode > 0) { switch (seconds) { case 540: case 480: case 420: case 360: case 300: case 240: case 180: case 120: case 60: case 30: case 10: case 5: case 4: case 3: case 2: case 1: break; default: } } if (_counterInstance != null) { _counterInstance._abort(); } _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); } /** * This function aborts a running countdown * * @param IP IP Which Issued shutdown command */ public void telnetAbort(String IP) { _log.warning("IP: " + IP + " issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!"); if (_counterInstance != null) { _counterInstance._abort(); } } /** * Default constucter is only used internal to create the shutdown-hook instance * */ public Shutdown() { _secondsShut = -1; _shutdownMode = SIGTERM; } /** * This creates a countdown instance of Shutdown. * * @param seconds how many seconds until shutdown * @param restart true is the server shall restart after shutdown * */ public Shutdown(int seconds, boolean restart) { if (seconds < 0) { seconds = 0; } _secondsShut = seconds; if (restart) { _shutdownMode = GM_RESTART; } else { _shutdownMode = GM_SHUTDOWN; } } /** * get the shutdown-hook instance * the shutdown-hook instance is created by the first call of this function, * but it has to be registrered externaly. * * @return instance of Shutdown, to be used as shutdown hook */ public static Shutdown getInstance() { if (_instance == null) { _instance = new Shutdown(); } return _instance; } /** * this function is called, when a new thread starts * * if this thread is the thread of getInstance, then this is the shutdown hook * and we save all data and disconnect all clients. * * after this thread ends, the server will completely exit * * if this is not the thread of getInstance, then this is a countdown thread. * we start the countdown, and when we finished it, and it was not aborted, * we tell the shutdown-hook why we call exit, and then call exit * * when the exit status of the server is 1, startServer.sh / startServer.bat * will restart the server. * */ @Override public void run() { if (this == _instance) { // last byebye, save all data and quit this server // logging doesnt work here :( saveData(); try { // LoginServerThread.getInstance().interrupt(); } catch (Throwable t) { // ignore } // saveData sends messages to exit players, so sgutdown selector after it try { //GameServer.gameServer.getSelectorThread().shutdown(); //GameServer.gameServer.getSelectorThread().setDaemon(true); } catch (Throwable t) { // ignore } // commit data, last chance try { L2DatabaseFactory.getInstance().shutdown(); } catch (Throwable t) { } // server will quit, when this function ends. if (_instance._shutdownMode == GM_RESTART) { Runtime.getRuntime().halt(2); } else { Runtime.getRuntime().halt(0); } } else { // gm shutdown: send warnings and then call exit to start shutdown sequence countdown(); // last point where logging is operational :( _log.warning("GM shutdown countdown is over. " + MODE_TEXT[_shutdownMode] + " NOW!"); switch (_shutdownMode) { case GM_SHUTDOWN: _instance.setMode(GM_SHUTDOWN); System.exit(0); break; case GM_RESTART: _instance.setMode(GM_RESTART); System.exit(2); break; } } } /** * This functions starts a shutdown countdown * * @param activeChar GM who issued the shutdown command * @param seconds seconds until shutdown * @param restart true if the server will restart after shutdown */ public void startShutdown(L2Player activeChar, int seconds, boolean restart) { _log.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjId() + ") issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!"); if (restart) { _shutdownMode = GM_RESTART; } else { _shutdownMode = GM_SHUTDOWN; } if (_shutdownMode > 0) { switch (seconds) { case 540: case 480: case 420: case 360: case 300: case 240: case 180: case 120: case 60: case 30: case 10: case 5: case 4: case 3: case 2: case 1: break; default: } } if (_counterInstance != null) { _counterInstance.abort(activeChar); } // the main instance should only run for shutdown hook, so we start a new instance _counterInstance = new Shutdown(seconds, restart); _counterInstance.start(); } /** * This function aborts a running countdown * * @param activeChar GM who issued the abort command */ public void abort(L2Player activeChar) { _log.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjId() + ") issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!"); if (_counterInstance != null) { _counterInstance._abort(); } } /** * set the shutdown mode * @param mode what mode shall be set */ private void setMode(int mode) { _shutdownMode = mode; } /** * set shutdown mode to ABORT * */ private void _abort() { _shutdownMode = ABORT; } /** * this counts the countdown and reports it to all players * countdown is aborted if mode changes to ABORT */ private void countdown() { try { while (_secondsShut > 0) { _secondsShut--; int delay = 1000; //milliseconds Thread.sleep(delay); if (_shutdownMode == ABORT) break; } } catch (InterruptedException e) { //this will never happen } } /** * this sends a last byebye, disconnects all players and saves data * */ private void saveData() { switch (_shutdownMode) { case SIGTERM: System.err.println("SIGTERM received. Shutting down NOW!"); break; case GM_SHUTDOWN: System.err.println("GM shutdown received. Shutting down NOW!"); break; case GM_RESTART: System.err.println("GM restart received. Restarting NOW!"); break; } try { int delay = 5000; Thread.sleep(delay); } catch (InterruptedException e) { //never happens :p } } }