diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java index 3262cf711b..d20d0be55e 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java @@ -125,7 +125,9 @@ public class TunnelControllerGroup implements ClientApp { loadControllers(_configFile); if (_mgr != null) _mgr.register(this); - _context.addShutdownTask(new Shutdown()); + // RouterAppManager registers its own shutdown hook + else + _context.addShutdownTask(new Shutdown()); } /** @@ -194,7 +196,9 @@ public class TunnelControllerGroup implements ClientApp { * * @since 0.8.8 */ - public void shutdown() { + public synchronized void shutdown() { + if (_state != STARTING && _state != RUNNING) + return; changeState(STOPPING); if (_mgr != null) _mgr.unregister(this); diff --git a/apps/jetty/java/src/net/i2p/jetty/JettyStart.java b/apps/jetty/java/src/net/i2p/jetty/JettyStart.java index ffc6d8ce27..1a7daa2bae 100644 --- a/apps/jetty/java/src/net/i2p/jetty/JettyStart.java +++ b/apps/jetty/java/src/net/i2p/jetty/JettyStart.java @@ -41,7 +41,6 @@ import org.eclipse.jetty.xml.XmlConfiguration; */ public class JettyStart implements ClientApp { - private final I2PAppContext _context; private final ClientAppManager _mgr; private final String[] _args; private final List _jettys; @@ -50,10 +49,12 @@ public class JettyStart implements ClientApp { /** * All args must be XML file names. * Does not support any of the other argument types from org.mortbay.start.Main. + * + * @param context unused, may be null + * @param mgr may be null e.g. for use in plugins */ public JettyStart(I2PAppContext context, ClientAppManager mgr, String[] args) throws Exception { _state = UNINITIALIZED; - _context = context; _mgr = mgr; _args = args; _jettys = new ArrayList(args.length); @@ -100,13 +101,13 @@ public class JettyStart implements ClientApp { private class Starter extends Thread { public Starter() { super("JettyStarter"); + changeState(STARTING); } /** * Modified from XmlConfiguration.main() */ public void run() { - changeState(STARTING); for (LifeCycle lc : _jettys) { if (!lc.isRunning()) { try { @@ -118,11 +119,12 @@ public class JettyStart implements ClientApp { } } changeState(RUNNING); - _mgr.register(JettyStart.this); + if (_mgr != null) + _mgr.register(JettyStart.this); } } - public void shutdown(String[] args) { + public synchronized void shutdown(String[] args) { if (_state != RUNNING) return; if (_jettys.isEmpty()) { @@ -135,10 +137,10 @@ public class JettyStart implements ClientApp { private class Stopper extends Thread { public Stopper() { super("JettyStopper"); + changeState(STOPPING); } public void run() { - changeState(STOPPING); for (LifeCycle lc : _jettys) { if (lc.isRunning()) { try { @@ -170,6 +172,22 @@ public class JettyStart implements ClientApp { private synchronized void changeState(ClientAppState state, Exception e) { _state = state; - _mgr.notify(this, state, null, e); + if (_mgr != null) + _mgr.notify(this, state, null, e); + } + + /** + * For use in a plugin clients.config + * @param args passed to constructor + * @since 0.9.6 + */ + public static void main(String[] args) { + try { + new JettyStart(null, null, args); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java index 069819963a..b7f20c492d 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -214,15 +214,19 @@ public class RouterConsoleRunner implements RouterApp { /////// ClientApp methods /** @since 0.9.4 */ - public void startup() { + public synchronized void startup() { changeState(STARTING); startTrayApp(_context); startConsole(); } /** @since 0.9.4 */ - public void shutdown(String[] args) { + public synchronized void shutdown(String[] args) { + if (_state == STOPPED) + return; changeState(STOPPING); + if (PluginStarter.pluginsEnabled(_context)) + (new I2PAppThread(new PluginStopper(_context), "PluginStopper")).start(); try { _server.stop(); } catch (Exception ie) {} @@ -653,10 +657,11 @@ public class RouterConsoleRunner implements RouterApp { t = new I2PAppThread(new PluginStarter(_context), "PluginStarter", true); t.setPriority(Thread.NORM_PRIORITY - 1); t.start(); - _context.addShutdownTask(new PluginStopper(_context)); } // stat summarizer registers its own hook - _context.addShutdownTask(new ServerShutdown()); + // RouterAppManager registers its own hook + if (_mgr == null) + _context.addShutdownTask(new ServerShutdown()); ConfigServiceHandler.registerSignalHandler(_context); } diff --git a/history.txt b/history.txt index 6fde6cb7e2..94b60c518a 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,8 @@ +2013-04-24 zzz + * Console, i2ptunnel: Don't register shutdown hook if ClientAppManager is present + * JettyStart: Fixes for use by plugins + * RouterAppManager: Add shutdown hook + 2013-04-23 zzz * Console: Fix Jetty digest auth bug causing repeated password requests * i2ptunnel: Block b32.i2p supercookies diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 7f4edb9752..31c35f8c3e 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 13; + public final static long BUILD = 14; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/startup/RouterAppManager.java b/router/java/src/net/i2p/router/startup/RouterAppManager.java index a311fe65d0..1b4441fcbf 100644 --- a/router/java/src/net/i2p/router/startup/RouterAppManager.java +++ b/router/java/src/net/i2p/router/startup/RouterAppManager.java @@ -5,8 +5,10 @@ import java.io.Writer; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import net.i2p.app.*; @@ -35,6 +37,7 @@ public class RouterAppManager implements ClientAppManager { _log = ctx.logManager().getLog(RouterAppManager.class); _clients = new ConcurrentHashMap(16); _registered = new ConcurrentHashMap(8); + ctx.addShutdownTask(new Shutdown()); } /** @@ -166,6 +169,32 @@ public class RouterAppManager implements ClientAppManager { return _registered.get(name); } + /// end ClientAppManager interface + + /** + * @since 0.9.6 + */ + public synchronized void shutdown() { + Set apps = new HashSet(_clients.keySet()); + for (ClientApp app : apps) { + ClientAppState state = app.getState(); + if (state == RUNNING || state == STARTING) { + try { + app.shutdown(null); + } catch (Throwable t) {} + } + } + } + + /** + * @since 0.9.6 + */ + public class Shutdown implements Runnable { + public void run() { + shutdown(); + } + } + /** * debug * @since 0.9.6