package net.sf.colossus.common;
import java.util.logging.Level;
import java.util.logging.Logger;
public class WhatNextManager
{
private static final Logger LOGGER = Logger
.getLogger(WhatNextManager.class.getName());
private final Options startOptions;
private boolean interactive;
private WhatToDoNext whatToDoNext;
private int howManyGamesLeft;
public WhatNextManager(Options startOpts)
{
this.startOptions = startOpts;
this.interactive = false;
this.howManyGamesLeft = Options.getHowManyStresstestRoundsProperty();
}
public WhatToDoNext getWhatToDoNext()
{
return whatToDoNext;
}
/**
* Returns true if this action was caused by interactive means.
* If so, it makes sense to display a error message dialog box if
* something went wrong.
* @return action to do is marked as "was triggered interactively"
*/
public boolean isInteractive()
{
return interactive;
}
/**
* Set the action what shall be executed next.
* Trigger also the timer for the "Timed Quit", if requested so.
*
* @param whatToDoNext
* @param triggerQuitTimer
* @param interactive
*/
public void setWhatToDoNext(WhatToDoNext whatToDoNext,
boolean triggerQuitTimer, boolean interactive)
{
this.whatToDoNext = whatToDoNext;
this.interactive = interactive;
LOGGER.log(Level.INFO,
"Set what to do next to " + whatToDoNext.toString());
if (triggerQuitTimer)
{
triggerTimedQuit();
}
}
/**
* A convenient shortcut to the 3-argument-form,
* for the many calls where interactive is to be set to false.
* @param whatToDoNext
* @param triggerQuitTimer
*/
public void setWhatToDoNext(WhatToDoNext whatToDoNext,
boolean triggerQuitTimer)
{
setWhatToDoNext(whatToDoNext, triggerQuitTimer, false);
}
/**
*
* @return Returns the same startOptions object that Start object uses.
*/
public Options getStartOptions()
{
return startOptions;
}
public void setWhatToDoNext(WhatToDoNext whatToDoNext, String loadFile,
boolean interactive)
{
setWhatToDoNext(whatToDoNext, false, interactive);
startOptions.setOption(Options.loadGameFileName, loadFile);
}
public int getHowManyGamesLeft()
{
return howManyGamesLeft;
}
public int decrementHowManyGamesLeft()
{
howManyGamesLeft--;
LOGGER.log(Level.INFO, "howManyGamesLeft now " + howManyGamesLeft);
return howManyGamesLeft;
}
/**
* Trigger a timed Quit, which will (by using a demon thread) terminate
* the JVM after a timeout (currently 10 (120) seconds)
* - unless the JVM has quit already anyway because cleanup has
* succeeded as planned.
*/
public void triggerTimedQuit()
{
LOGGER.log(Level.FINEST, "triggerTimedQuit called.");
if (Options.isFunctionalTest())
{
LOGGER.info("Functional test ongoing - ignoring the "
+ "request to trigger a timed quit.");
}
else if (Options.isStresstest() && howManyGamesLeft > 0)
{
LOGGER.info("HowManyGamesLeft now " + howManyGamesLeft
+ " not zero yet - ignoring the "
+ "request to trigger a timed quit.");
}
else
{
new TimedJvmQuit().start();
}
}
/**
* A demon thread which is started by triggerTimedQuit.
* It will then (currently) sleep 10 (120) seconds, and if it is then
* still alive, do a System.exit(1) to terminate the JVM.
* If, however, the game shutdown proceeded successfully as planned,
* Start.main() will already have reached it's end and there should
* not be any other non-demon threads alive, so the JVM *should*
* terminate by itself cleanly.
* So, if this TimedJvmQuit strikes, it means the "clean shutdown"
* has somehow failed.
*/
public static class TimedJvmQuit extends Thread
{
private static final Logger LOGGER = Logger
.getLogger(WhatNextManager.TimedJvmQuit.class.getName());
private static final String defaultName = "TimedJvmQuit thread";
private final String name;
// For now, on the web server, 120, because there were cases where
// in case of a draw Clients did not catch up.
// I suspect they are too busy processing all the legion cleanup
// (and related updateCreatureCount) messages.
// So give them more time for a while.
// try 30, reconnect causes server to hang (not cleanly exit itself) at the moment
// 17.10.2013: 120 again, quite many games on CPGS
// the QUIT hit before catch up processing completed...
private final long timeOutInSecs = 120;
public TimedJvmQuit()
{
super();
this.setDaemon(true);
this.name = defaultName;
}
@Override
public void run()
{
LOGGER.info(this.name + ": started... (sleeping " + timeOutInSecs
+ " seconds)");
sleepFor(this.timeOutInSecs * 1000);
LOGGER.warning(this.name + ": JVM still alive? "
+ "Ok, it's time to do System.exit()...");
System.exit(1);
}
}
public static void sleepFor(long millis)
{
try
{
Thread.sleep(millis);
}
catch (InterruptedException e)
{
LOGGER.log(Level.FINEST,
"InterruptException caught... ignoring it...");
}
}
/**
* The various constants for activities what the Start class should do
* as next thing, typically when a dialog is closed or a games ended.
*/
public static enum WhatToDoNext
{
START_GAME("Start Game"), START_NET_CLIENT("Start network client"), START_WEB_CLIENT(
"Start Web Client"), LOAD_GAME("Load Game"), GET_PLAYERS_DIALOG(
"GetPlayers dialog"), NET_CLIENT_DIALOG("Network Client dialog"), QUIT_ALL(
"Quit All");
private final String activity;
private WhatToDoNext(String act)
{
this.activity = act;
}
/**
* Returns a non-localized UI string for the "whatToDoNext" activity.
*/
@Override
public String toString()
{
return activity;
}
}
}