예제 #1
0
  /**
   * @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);
      }
    }
  }
예제 #2
0
 /**
  * threaded
  *
  * @since 0.8.13
  */
 static void updateAll(RouterContext ctx) {
   Thread t = new I2PAppThread(new PluginUpdater(ctx), "PluginUpdater", true);
   t.start();
 }
예제 #3
0
  /**
   * inline
   *
   * @since 0.8.13
   */
  private static void updateAll(RouterContext ctx, boolean delay) {
    List<String> plugins = getPlugins();
    Map<String, String> toUpdate = new HashMap<String, String>();
    for (String appName : plugins) {
      Properties props = pluginProperties(ctx, appName);
      String url = props.getProperty("updateURL");
      if (url != null) toUpdate.put(appName, url);
    }
    if (toUpdate.isEmpty()) return;

    ConsoleUpdateManager mgr = UpdateHandler.updateManager(ctx);
    if (mgr == null) return;
    if (mgr.isUpdateInProgress()) return;

    if (delay) {
      // wait for proxy
      mgr.update(TYPE_DUMMY, 3 * 60 * 1000);
      mgr.notifyProgress(null, Messages.getString("Checking for plugin updates", ctx));
      int loop = 0;
      do {
        try {
          Thread.sleep(5 * 1000);
        } catch (InterruptedException ie) {
        }
        if (loop++ > 40) break;
      } while (mgr.isUpdateInProgress(TYPE_DUMMY));
    }

    String proxyHost =
        ctx.getProperty(
            ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST);
    int proxyPort = ConfigUpdateHandler.proxyPort(ctx);
    if (proxyPort == ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT
        && proxyHost.equals(ConfigUpdateHandler.DEFAULT_PROXY_HOST)
        && ctx.portMapper().getPort(PortMapper.SVC_HTTP_PROXY) < 0) {
      mgr.notifyComplete(
          null,
          Messages.getString("Plugin update check failed", ctx)
              + " - "
              + Messages.getString("HTTP client proxy tunnel must be running", ctx));
      return;
    }

    Log log = ctx.logManager().getLog(PluginStarter.class);
    int updated = 0;
    for (Map.Entry<String, String> entry : toUpdate.entrySet()) {
      String appName = entry.getKey();
      if (log.shouldLog(Log.WARN)) log.warn("Checking for update plugin: " + appName);

      // blocking
      if (mgr.checkAvailable(PLUGIN, appName, 60 * 1000) == null) {
        if (log.shouldLog(Log.WARN)) log.warn("No update available for plugin: " + appName);
        continue;
      }

      if (log.shouldLog(Log.WARN)) log.warn("Updating plugin: " + appName);
      // non-blocking
      mgr.update(PLUGIN, appName, 30 * 60 * 1000);
      int loop = 0;
      do {
        // only wait for 4 minutes, then we will
        // keep going
        try {
          Thread.sleep(5 * 1000);
        } catch (InterruptedException ie) {
        }
        if (loop++ > 48) break;
      } while (mgr.isUpdateInProgress(PLUGIN, appName));

      if (mgr.getUpdateAvailable(PLUGIN, appName) != null) updated++;
    }
    if (updated > 0)
      mgr.notifyComplete(null, ngettext("1 plugin updated", "{0} plugins updated", updated, ctx));
    else mgr.notifyComplete(null, Messages.getString("Plugin update check complete", ctx));
  }