package net.sourceforge.pain.network.console; import net.sourceforge.pain.*; import net.sourceforge.pain.logic.*; import net.sourceforge.pain.network.console.telnet.*; import net.sourceforge.pain.util.*; import java.util.*; /** * Network console management system. Implementors should supply * events handling routines only. */ public abstract class AbstractConsoleManager extends PulsePeriodListener implements LogicReloadListener { protected ArrayList consoles = new ArrayList(); private TelnetConsoleServer server = null; protected int port; Class EVENT_CONSOLE_INPUT_CLASS; Class EVENT_CONSOLE_EXPIRED_CLASS; Class EVENT_NEW_CONNECTION_CLASS; Class EVENT_CONSOLE_DISCONNECT_CLASS; Class EVENT_PROMPT_CLASS; private static final int CHECK_PERIOD_IN_PULSE = 10; private String logName; private ConsoleFactory cf; public AbstractConsoleManager(String logName, int port, ConsoleFactory cf) { super(CHECK_PERIOD_IN_PULSE, PERIOD_IN_PULSE); this.logName = logName; this.cf = cf; this.port = port; } public void start() { Codebase.getLogicLoader().addLogicReloadListener(this); Codebase.getPulse().addListener(this); server = new TelnetConsoleServer(port, this); } public boolean isStarted() { return server != null; } public void stop() { Codebase.getLogicLoader().removeReloadLogicListener(this); Codebase.getPulse().removeListener(this); server.stop(); server = null; } protected synchronized void onPeriod(int time) throws Exception { if (EVENT_PROMPT_CLASS == null) { initEventClasses(); } BasicConsole console; int nCons = consoles.size(); for (int i = 0; i < nCons; i++) { console = (BasicConsole) consoles.get(i); boolean wasPrompt = false; if (console.state == BasicConsole.STATE_ACTIVE) { if (console.input.size() > 0) { try { console.newLineProcessingStarted = true; emitEvent(EVENT_CONSOLE_INPUT_CLASS, console); } catch (Exception e) { Log.error(e); console.popInputLine();// removing this error line from out queue } if (console.state == BasicConsole.STATE_ACTIVE) { // Note: console could be marked inactive during event processing! console.expireTime = time + BasicConsole.MAX_INACTIVE_TIME; if (console.isCommandMode()) { actPrompt(console); wasPrompt = true; if (console.input.size() > 0 && console.isCommandMode()) { console.out(ConsoleAdapter.NEW_LINE); } } } } else { if (time > console.expireTime) { try { emitEvent(EVENT_CONSOLE_EXPIRED_CLASS, console); } catch (Exception e) { // if error occured we will force to close console Log.error(e.getMessage(), e); } finally { if (console.state != BasicConsole.STATE_SYSTEM_CLOSED) { closeConsole(console); } } } } if (console.state == BasicConsole.STATE_ACTIVE) { if (console.hasBufferedOutput) { if (!wasPrompt && console.isCommandMode()) { actPrompt(console); } console.flushOutput(); } } } else if (console.state == BasicConsole.STATE_NEW) { try { emitEvent(EVENT_NEW_CONNECTION_CLASS, console); } catch (Exception e) { Log.error(e); if (console.state != BasicConsole.STATE_SYSTEM_CLOSED) { closeConsole(console); } } if (console.state == BasicConsole.STATE_NEW) { console.expireTime = time + BasicConsole.MAX_INACTIVE_TIME; console.state = BasicConsole.STATE_ACTIVE; } else { // console was closed.. } } else if (console.state == BasicConsole.STATE_SYSTEM_CLOSED) { if (console.adapter != null) { // if was not closed remotely (disconnected) console.adapter.forceClose(); } consoles.remove(i); i--; nCons--; } else if (console.state == BasicConsole.STATE_REMOTE_CLOSED_1) { console.state = BasicConsole.STATE_REMOTE_CLOSED_2; try { emitEvent(EVENT_CONSOLE_DISCONNECT_CLASS, console); } catch (Exception e) { Log.error(e.getMessage(), e); closeConsole(console); } } else { // we do nothing with REMOTE closed consoles here // system MUST close it from it's side -> console should become STATE_SYSTEM_CLOSED!! } } } private void actPrompt(BasicConsole console) { try { if (EVENT_PROMPT_CLASS == null) { initEventClasses(); } emitEvent(EVENT_PROMPT_CLASS, console); } catch (Exception e) { Log.error(e.getMessage(), e); } } protected synchronized BasicConsole register(ConsoleAdapter adapter) { BasicConsole console = cf.provideConsole(adapter); consoles.add(console); return console; } public synchronized void closeConsole(BasicConsole console) { Log.debug(logName + ":manual close!"); if (console.state == BasicConsole.STATE_NEW) { // This state is possible since we allow // to get a consoles Set copy by consoles() method for outer code console.flushOutput(); } else if (console.state == BasicConsole.STATE_ACTIVE) { console.flushOutput(); } else { // console also could have STATE_REMOTE_CLOSE_2 here // we should not do any special in this case } console.state = BasicConsole.STATE_SYSTEM_CLOSED; } protected synchronized void onRemoteClose(BasicConsole console) { Log.debug(logName + ":remote close!"); if (console.state == BasicConsole.STATE_NEW) { consoles.remove(console); } else if (console.state == BasicConsole.STATE_ACTIVE) { console.adapter = null; console.state = BasicConsole.STATE_REMOTE_CLOSED_1; } } public synchronized List consoles() { return Collections.unmodifiableList(consoles); } public void onLogicReloading() { EVENT_CONSOLE_INPUT_CLASS = null; EVENT_CONSOLE_EXPIRED_CLASS = null; EVENT_NEW_CONNECTION_CLASS = null; EVENT_CONSOLE_DISCONNECT_CLASS = null; EVENT_PROMPT_CLASS = null; } protected abstract void emitEvent(Class eventClass, BasicConsole c) throws Exception; protected abstract void initEventClasses() throws Exception; }