public void update(UpdateMemo memo) throws Exception {

    ArtifactData target = put(memo.best.urls.iterator().next());

    memo.current.version = new Version(memo.best.version);
    target.sync();
    memo.current.sha = target.sha;
    // memo.current.dependencies = target.dependencies;
    // memo.current.dependencies.add((new File(repoDir,
    // Hex.toHexString(target.sha))).getCanonicalPath());
    // memo.current.runbundles = target.runbundles;
    // memo.current.description = target.description;
    memo.current.time = target.time;

    if (memo.current instanceof ServiceData) {
      Service service = getService((ServiceData) memo.current);
      service.remove();
      createService((ServiceData) memo.current);
      IO.delete(new File(IO.getFile(serviceDir, memo.current.name), "data"));
      storeData(new File(IO.getFile(serviceDir, memo.current.name), "data"), memo.current);
    } else {
      platform.deleteCommand(memo.current);
      createCommand(memo.current, false);
      IO.delete(IO.getFile(commandDir, memo.current.name));
      storeData(IO.getFile(commandDir, memo.current.name), memo.current);
    }
  }
  public void deleteCommand(String name) throws Exception {
    CommandData cmd = getCommand(name);
    if (cmd == null) throw new IllegalArgumentException("No such command " + name);

    platform.deleteCommand(cmd);
    File tobedel = new File(commandDir, name);
    IO.deleteWithException(tobedel);
  }
  /**
   * @param data
   * @param target
   * @throws Exception
   * @throws IOException
   */
  public String createService(ServiceData data) throws Exception, IOException {

    File sdir = new File(serviceDir, data.name);
    if (!sdir.exists() && !sdir.mkdirs()) {
      throw new IOException("Could not create directory " + data.sdir);
    }
    data.sdir = sdir.getAbsolutePath();

    File lock = new File(data.sdir, LOCK);
    data.lock = lock.getAbsolutePath();

    if (data.work == null) data.work = new File(data.sdir, "work").getAbsolutePath();
    if (data.user == null) data.user = platform.user();

    if (data.user == null) data.user = "******";

    new File(data.work).mkdir();

    if (data.log == null) data.log = new File(data.sdir, "log").getAbsolutePath();

    // TODO
    // if (Data.validate(data) != null)
    // return "Invalid service data: " + Data.validate(data);

    if (service == null)
      throw new RuntimeException(
          "Missing biz.aQute.jpm.service in repo, should have been installed by init, try reiniting");

    data.serviceLib = service.getAbsolutePath();

    platform.chown(data.user, true, new File(data.sdir));

    String s = platform.createService(data, null, false);
    if (s == null) storeData(new File(data.sdir, "data"), data);
    return s;
  }
  /**
   * @param data
   * @param target
   * @throws Exception
   * @throws IOException
   */
  public String createCommand(CommandData data, boolean force) throws Exception, IOException {

    // TODO
    // if (Data.validate(data) != null)
    // return "Invalid command data: " + Data.validate(data);

    Map<String, String> map = null;
    if (data.trace) {
      map = new HashMap<String, String>();
      map.put("java.security.manager", "aQute.jpm.service.TraceSecurityManager");
      reporter.trace("tracing");
    }
    String s = platform.createCommand(data, map, force, service.getAbsolutePath());
    if (s == null) storeData(new File(commandDir, data.name), data);
    return s;
  }
  private String listSupportFiles(List<File> toDelete) throws Exception {
    Formatter f = new Formatter();
    try {
      if (toDelete == null) {
        toDelete = new ArrayList<File>();
      }
      int precount = toDelete.size();
      File confFile = IO.getFile(platform.getConfigFile()).getCanonicalFile();
      if (confFile.exists()) {
        f.format("    * %s \t0 Config file%n", confFile);
        toDelete.add(confFile);
      }

      String result = (toDelete.size() > precount) ? f.toString() : null;
      return result;
    } finally {
      f.close();
    }
  }
  private String listFiles(final File cache, List<File> toDelete) throws Exception {
    boolean stopServices = false;
    if (toDelete == null) {
      toDelete = new ArrayList<File>();
    } else {
      stopServices = true;
    }
    int count = 0;
    Formatter f = new Formatter();

    f.format(" - Cache:%n    * %s%n", cache.getCanonicalPath());
    f.format(" - Commands:%n");
    for (CommandData cdata : getCommands(new File(cache, COMMANDS))) {
      f.format("    * %s \t0 handle for \"%s\"%n", cdata.bin, cdata.name);
      toDelete.add(new File(cdata.bin));
      count++;
    }
    f.format(" - Services:%n");
    for (ServiceData sdata : getServices(new File(cache, SERVICE))) {
      if (sdata != null) {
        f.format("    * %s \t0 service directory for \"%s\"%n", sdata.sdir, sdata.name);
        toDelete.add(new File(sdata.sdir));
        File initd = platform.getInitd(sdata);
        if (initd != null && initd.exists()) {
          f.format("    * %s \t0 init.d file for \"%s\"%n", initd.getCanonicalPath(), sdata.name);
          toDelete.add(initd);
        }
        if (stopServices) {
          Service s = getService(sdata);
          try {
            s.stop();
          } catch (Exception e) {
          }
        }
        count++;
      }
    }
    f.format("%n");

    String result = (count > 0) ? f.toString() : null;
    f.close();
    return result;
  }
 public void register(boolean user) throws Exception {
   platform.installDaemon(user);
 }
  public void deinit(Appendable out, boolean force) throws Exception {
    Settings settings = new Settings(platform.getConfigFile());

    if (!force) {
      Justif justify = new Justif(80, 40);
      StringBuilder sb = new StringBuilder();
      Formatter f = new Formatter(sb);

      try {
        String list = listFiles(platform.getGlobal());
        if (list != null) {
          f.format("In global default environment:%n");
          f.format(list);
        }

        list = listFiles(platform.getLocal());
        if (list != null) {
          f.format("In local default environment:%n");
          f.format(list);
        }

        if (settings.containsKey(JPM_CACHE_GLOBAL)) {
          list = listFiles(IO.getFile(settings.get(JPM_CACHE_GLOBAL)));
          if (list != null) {
            f.format("In global configured environment:%n");
            f.format(list);
          }
        }

        if (settings.containsKey(JPM_CACHE_LOCAL)) {
          list = listFiles(IO.getFile(settings.get(JPM_CACHE_LOCAL)));
          if (list != null) {
            f.format("In local configured environment:%n");
            f.format(list);
          }
        }

        list = listSupportFiles();
        if (list != null) {
          f.format("jpm support files:%n");
          f.format(list);
        }

        f.format("%n%n");

        f.format(
            "All files listed above will be deleted if deinit is run with the force flag set"
                + " (\"jpm deinit -f\" or \"jpm deinit --force\"%n%n");
        f.flush();

        justify.wrap(sb);
        out.append(sb.toString());
      } finally {
        f.close();
      }
    } else { // i.e. if(force)
      int count = 0;
      File[] caches = {platform.getGlobal(), platform.getLocal(), null, null};
      if (settings.containsKey(JPM_CACHE_LOCAL)) {
        caches[2] = IO.getFile(settings.get(JPM_CACHE_LOCAL));
      }
      if (settings.containsKey(JPM_CACHE_GLOBAL)) {
        caches[3] = IO.getFile(settings.get(JPM_CACHE_GLOBAL));
      }
      ArrayList<File> toDelete = new ArrayList<File>();

      for (File cache : caches) {
        if (cache == null || !cache.exists()) {
          continue;
        }
        listFiles(cache, toDelete);
        if (toDelete.size() > count) {
          count = toDelete.size();
          if (!cache.canWrite()) {
            reporter.error(PERMISSION_ERROR + " (" + cache + ")");
            return;
          }
          toDelete.add(cache);
        }
      }
      listSupportFiles(toDelete);

      for (File f : toDelete) {
        if (f.exists() && !f.canWrite()) {
          reporter.error(PERMISSION_ERROR + " (" + f + ")");
        }
      }
      if (reporter.getErrors().size() > 0) {
        return;
      }

      for (File f : toDelete) {
        if (f.exists()) {
          IO.deleteWithException(f);
        }
      }
    }
  }