/** * Blocking, may take a while, up to 20 seconds */ public synchronized void stop() { if (_log.shouldLog(Log.DEBUG)) _log.debug("UPnP Stop"); _shouldBeRunning = false; _rescanner.cancel(); if (_isRunning) _upnp.terminate(); _isRunning = false; _detectedAddress = null; if (_log.shouldLog(Log.DEBUG)) _log.debug("UPnP Stop Done"); }
/** * Blocking, may take a while */ public synchronized void start() { if (_log.shouldLog(Log.DEBUG)) _log.debug("UPnP Start"); _shouldBeRunning = true; if (!_isRunning) { long b = _context.clock().now(); try { _isRunning = _upnp.runPlugin(); if (_log.shouldLog(Log.INFO)) _log.info("UPnP runPlugin took " + (_context.clock().now() - b)); } catch (Exception e) { // NPE in UPnP (ticket #728), can't let it bring us down if (!_errorLogged) { _log.error("UPnP error, please report", e); _errorLogged = true; } } } if (_isRunning) { _rescanner.schedule(RESCAN_LONG_DELAY); if (_log.shouldLog(Log.DEBUG)) _log.debug("UPnP Start Done"); } else { _rescanner.schedule(RESCAN_SHORT_DELAY); // Do we have a non-loopback, non-broadcast address? // If not, that's why it failed (HTTPServer won't start) if (!Addresses.isConnected()) { if (!_disconLogged) { _log.logAlways(Log.WARN, "UPnP start failed - no network connection?"); _disconLogged = true; } } else { _log.error("UPnP start failed - port conflict?"); } } }
/** * @param action "start" or "stop" or "uninstall" * @throws just about anything if an app has a delay less than zero, caller would be wise to catch * Throwable If no apps have a delay less than zero, it shouldn't throw anything */ private static void runClientApps( RouterContext ctx, File pluginDir, List<ClientAppConfig> apps, String action) throws Exception { Log log = ctx.logManager().getLog(PluginStarter.class); // initialize pluginThreadGroup and _pendingPluginClients String pluginName = pluginDir.getName(); if (!pluginThreadGroups.containsKey(pluginName)) pluginThreadGroups.put(pluginName, new ThreadGroup(pluginName)); ThreadGroup pluginThreadGroup = pluginThreadGroups.get(pluginName); if (action.equals("start")) _pendingPluginClients.put(pluginName, new ConcurrentHashSet<SimpleTimer2.TimedEvent>()); for (ClientAppConfig app : apps) { // If the client is a running ClientApp that we want to stop, // bypass all the logic below. if (action.equals("stop")) { String[] argVal = LoadClientAppsJob.parseArgs(app.args); // We must do all the substitution just as when started, so the // argument array comparison in getClientApp() works. // Do this after parsing so we don't need to worry about quoting for (int i = 0; i < argVal.length; i++) { if (argVal[i].indexOf("$") >= 0) { argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath()); argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath()); argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath()); } } ClientApp ca = ctx.routerAppManager().getClientApp(app.className, argVal); if (ca != null) { // even if (ca.getState() != ClientAppState.RUNNING), we do this, we don't want to fall // thru try { ca.shutdown(LoadClientAppsJob.parseArgs(app.stopargs)); } catch (Throwable t) { throw new Exception(t); } continue; } } if (action.equals("start") && app.disabled) continue; String argVal[]; if (action.equals("start")) { // start argVal = LoadClientAppsJob.parseArgs(app.args); } else { String args; if (action.equals("stop")) args = app.stopargs; else if (action.equals("uninstall")) args = app.uninstallargs; else throw new IllegalArgumentException("bad action"); // args must be present if (args == null || args.length() <= 0) continue; argVal = LoadClientAppsJob.parseArgs(args); } // do this after parsing so we don't need to worry about quoting for (int i = 0; i < argVal.length; i++) { if (argVal[i].indexOf("$") >= 0) { argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath()); argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath()); argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath()); } } ClassLoader cl = null; if (app.classpath != null) { String cp = app.classpath; if (cp.indexOf("$") >= 0) { cp = cp.replace("$I2P", ctx.getBaseDir().getAbsolutePath()); cp = cp.replace("$CONFIG", ctx.getConfigDir().getAbsolutePath()); cp = cp.replace("$PLUGIN", pluginDir.getAbsolutePath()); } // Old way - add for the whole JVM // addToClasspath(cp, app.clientName, log); // New way - add only for this client // We cache the ClassLoader we start the client with, so // we can reuse it for stopping and uninstalling. // If we don't, the client won't be able to find its // static members. String clCacheKey = pluginName + app.className + app.args; if (!action.equals("start")) cl = _clCache.get(clCacheKey); if (cl == null) { URL[] urls = classpathToURLArray(cp, app.clientName, log); if (urls != null) { cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); if (action.equals("start")) _clCache.put(clCacheKey, cl); } } } if (app.delay < 0 && action.equals("start")) { // this will throw exceptions LoadClientAppsJob.runClientInline(app.className, app.clientName, argVal, log, cl); } else if (app.delay == 0 || !action.equals("start")) { // quick check, will throw ClassNotFoundException on error LoadClientAppsJob.testClient(app.className, cl); // run this guy now LoadClientAppsJob.runClient( app.className, app.clientName, argVal, ctx, log, pluginThreadGroup, cl); } else { // If there is some delay, there may be a really good reason for it. // Loading a class would be one of them! // So we do a quick check first, If it bombs out, we delay and try again. // If it bombs after that, then we throw the ClassNotFoundException. try { // quick check LoadClientAppsJob.testClient(app.className, cl); } catch (ClassNotFoundException ex) { // Try again 1 or 2 seconds later. // This should be enough time. Although it is a lousy hack // it should work for most cases. // Perhaps it may be even better to delay a percentage // if > 1, and reduce the delay time. // Under normal circumstances there will be no delay at all. try { if (app.delay > 1) { Thread.sleep(2000); } else { Thread.sleep(1000); } } catch (InterruptedException ie) { } // quick check, will throw ClassNotFoundException on error LoadClientAppsJob.testClient(app.className, cl); } // wait before firing it up SimpleTimer2.TimedEvent evt = new TrackedDelayedClient( pluginName, ctx.simpleTimer2(), ctx, app.className, app.clientName, argVal, pluginThreadGroup, cl); evt.schedule(app.delay); } } }