Package restx.plugins

Source Code of restx.plugins.PluginsShellCommand$InstallPluginRunner

package restx.plugins;

import com.google.common.base.*;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import com.google.common.io.PatternFilenameFilter;
import com.google.common.io.Resources;
import jline.console.completer.ArgumentCompleter;
import jline.console.completer.Completer;
import jline.console.completer.StringsCompleter;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import restx.factory.Component;
import restx.shell.RestxShell;
import restx.shell.ShellCommandRunner;
import restx.shell.ShellIvy;
import restx.shell.StdShellCommand;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
* User: xavierhanin
* Date: 5/4/13
* Time: 2:32 PM
*/
@Component
public class PluginsShellCommand extends StdShellCommand {
    private static final Logger logger = LoggerFactory.getLogger(PluginsShellCommand.class);

    /*
     * this is the default exlcusions list used when fetching plugins (to avoid confusion between lib and plugins dir):
     * - we exclude the main modules already part of the shell itself (shell and shell-manager)
     * - we exclude logback-classic, to avoid a SLF4J warning at startup if multiple bindings are present
     */
    private static final List<String> defaultExcludes = ImmutableList.of(
            "io.restx:restx-shell",
            "io.restx:restx-shell-manager",
            "ch.qos.logback:logback-classic");

    private static final ModulesManager.DownloadOptions defaultDownloadOptions = new ModulesManager.DownloadOptions.Builder().exclusions(defaultExcludes).build();

    public PluginsShellCommand() {
        super(ImmutableList.of("shell"), "manages the shell itself: install / update plugins, upgrade restx shell version");
    }

    @Override
    protected String resourceMan() {
        return "restx/plugins/shell.man";
    }

    @Override
    protected Optional<? extends ShellCommandRunner> doMatch(String line) {
        final List<String> args = splitArgs(line);
        if (args.size() < 2) {
            return Optional.absent();
        }
        switch (args.get(1)) {
            case "install":
                return Optional.<ShellCommandRunner>of(new InstallPluginRunner(args));
            case "upgrade":
                return Optional.<ShellCommandRunner>of(new UpgradeShellRunner());
        }

        return Optional.absent();
    }

    @Override
    public Iterable<Completer> getCompleters() {
        return ImmutableList.<Completer>of(
                new ArgumentCompleter(new StringsCompleter("shell"), new StringsCompleter("install", "upgrade")));
    }

    @Override
    public void start(RestxShell shell) throws IOException {
        File versionFile = new File(shell.installLocation().toFile(), shell.version());
        if (versionFile.exists()) {
            // upgrade check already done
            return;
        }

        try {
            // upgrading to a new version, we check if plugins need to be upgraded
            File[] pluginFiles = pluginFiles(pluginsDir(shell));

            if (pluginFiles.length == 0) {
                // no plugin installed, nothing to upgrade
                return;
            }

            shell.printIn("upgrading to " + shell.version() + " ...", RestxShell.AnsiCodes.ANSI_YELLOW);
            shell.println("");

            ModulesManager modulesManager = new ModulesManager(
                    new URL("http://restx.io/modules"), ShellIvy.loadIvy(shell));

            List<ModuleDescriptor> plugins = modulesManager.searchModules("category=shell");

            Set<String> allJars = new LinkedHashSet<>();
            Set<String> keepJars = new LinkedHashSet<>();
            List<ModuleDescriptor> pluginsToInstall = new ArrayList<>();
            List<String> unmatchedPlugins = new ArrayList<>();
            for (File pluginFile : pluginFiles) {
                try {
                    List<String> desc = Files.readLines(pluginFile, Charsets.UTF_8);
                    String id = desc.get(0);
                    ModuleRevisionId mrid = ModulesManager.toMrid(id);
                    List<String> jars = desc.subList(2, desc.size());


                    allJars.addAll(jars);

                    ModuleDescriptor matchingModule = findMatchingPlugin(plugins, mrid);

                    if (matchingModule == null) {
                        keepJars.addAll(jars);
                        unmatchedPlugins.add(id);
                    } else if (ModulesManager.toMrid(matchingModule.getId()).getRevision()
                                        .equals(mrid.getRevision())) {
                        // up to date
                        keepJars.addAll(jars);
                    } else {
                        pluginsToInstall.add(matchingModule);
                    }
                } catch (Exception e) {
                    shell.printIn("error while parsing plugin file " + pluginFile + ": " + e, RestxShell.AnsiCodes.ANSI_RED);
                    shell.println("");
                }
            }

            if (!unmatchedPlugins.isEmpty()) {
                shell.printIn("found unmanaged installed plugins, they won't be upgraded automatically:\n"
                        + Joiner.on("\n").join(unmatchedPlugins), RestxShell.AnsiCodes.ANSI_YELLOW);
                shell.println("");
            }

            Set<String> jarsToRemove = new LinkedHashSet<>();
            jarsToRemove.addAll(allJars);
            jarsToRemove.removeAll(keepJars);

            for (String jarToRemove : jarsToRemove) {
                logger.debug("removing {}", jarToRemove);
                new File(jarToRemove).delete();
            }

            if (!pluginsToInstall.isEmpty()) {
                int count = 0;
                shell.println("found " + pluginsToInstall.size() + " plugins to upgrade");
                for (ModuleDescriptor md : pluginsToInstall) {
                    if (installPlugin(shell, modulesManager, pluginsDir(shell), md)) {
                        count++;
                    }
                }
                if (count > 0) {
                    shell.println("upgraded " + count + " plugins, restarting shell");
                    shell.restart();
                }
            }
        } finally {
            Files.write(DateTime.now().toString(), versionFile, Charsets.UTF_8);
        }
    }

    private ModuleDescriptor findMatchingPlugin(List<ModuleDescriptor> plugins, ModuleRevisionId mrid) {
        ModuleDescriptor matchingModule = null;
        for (ModuleDescriptor plugin : plugins) {
            ModuleRevisionId pluginId = ModulesManager.toMrid(plugin.getId());
            if (pluginId.getModuleId().equals(mrid.getModuleId())) {
                matchingModule = plugin;
                break;
            }
        }
        return matchingModule;
    }

    private class InstallPluginRunner implements ShellCommandRunner {
        private final Optional<List<String>> pluginIds;

        public InstallPluginRunner(List<String> args) {
            if (args.size() > 2) {
                pluginIds = Optional.<List<String>>of(new ArrayList<>(args.subList(2, args.size())));
            } else {
                pluginIds = Optional.absent();
            }
        }

        @Override
        public void run(RestxShell shell) throws Exception {
            ModulesManager modulesManager = new ModulesManager(
                    new URL("http://restx.io/modules"), ShellIvy.loadIvy(shell));

            shell.println("looking for plugins...");
            List<ModuleDescriptor> plugins = modulesManager.searchModules("category=shell");

            Iterable<String> selected = null;
            if (!pluginIds.isPresent()) {
                shell.printIn("found " + plugins.size() + " available plugins", RestxShell.AnsiCodes.ANSI_CYAN);
                shell.println("");

                for (int i = 0; i < plugins.size(); i++) {
                    ModuleDescriptor plugin = plugins.get(i);
                    shell.printIn(String.format(" [%3d] %s%n", i + 1, plugin.getId()), RestxShell.AnsiCodes.ANSI_PURPLE);
                    shell.println("\t\t" + plugin.getDescription());
                }

                String sel = shell.ask("Which plugin would you like to install (eg '1 3 5')? \nYou can also provide a plugin id in the form <groupId>:<moduleId>:<version>\n plugin to install: ", "");
                selected = Splitter.on(" ").trimResults().omitEmptyStrings().split(sel);
            } else {
                selected = pluginIds.get();
            }

            File pluginsDir = pluginsDir(shell);
            int count = 0;
            for (String s : selected) {
                ModuleDescriptor md;
                if (CharMatcher.DIGIT.matchesAllOf(s)) {
                    int i = Integer.parseInt(s);
                    md = plugins.get(i - 1);
                } else {
                    md = new ModuleDescriptor(s, "shell", "");
                }
                if (installPlugin(shell, modulesManager, pluginsDir, md)) {
                    count++;
                }
            }
            if (count > 0) {
                shell.printIn("installed " + count + " plugins, restarting shell to take them into account", RestxShell.AnsiCodes.ANSI_GREEN);
                shell.println("");

                shell.restart();
            } else {
                shell.println("no plugin installed");
            }
        }
    }

    private boolean installPlugin(RestxShell shell, ModulesManager modulesManager, File pluginsDir, ModuleDescriptor md) throws IOException {
        shell.printIn("installing " + md.getId() + "...", RestxShell.AnsiCodes.ANSI_CYAN);
        shell.println("");
        try {
            List<File> copied = modulesManager.download(ImmutableList.of(md), pluginsDir, defaultDownloadOptions);
            if (!copied.isEmpty()) {
                shell.printIn("installed " + md.getId(), RestxShell.AnsiCodes.ANSI_GREEN);
                shell.println("");
                Files.write(md.getId() + "\n"
                        + DateTime.now() + "\n"
                        + Joiner.on("\n").join(copied),
                        pluginFile(pluginsDir, md), Charsets.UTF_8);
                return true;
            } else {
                shell.printIn("problem while installing " + md.getId(), RestxShell.AnsiCodes.ANSI_RED);
                shell.println("");
            }
        } catch (IOException e) {
            shell.printIn("IO problem while installing " + md.getId() + "\n" + e.getMessage(), RestxShell.AnsiCodes.ANSI_RED);
            shell.println("");
        } catch (IllegalStateException e) {
            shell.printIn(e.getMessage(), RestxShell.AnsiCodes.ANSI_RED);
            shell.println("");
        }
        return false;
    }

    private File pluginsDir(RestxShell shell) {
        return new File(shell.installLocation().toFile(), "plugins");
    }

    private File pluginFile(File pluginsDir, ModuleDescriptor md) {
        return new File(pluginsDir, md.getModuleId() + ".plugin");
    }

    private File[] pluginFiles(File pluginsDir) {
        File[] files = pluginsDir.listFiles(new PatternFilenameFilter(".*\\.plugin"));
        return files == null ? new File[0] : files;
    }

    private class UpgradeShellRunner implements ShellCommandRunner {
        @Override
        public void run(RestxShell shell) throws Exception {
            shell.println("checking for upgrade of restx shell...");

            try (Reader reader = new InputStreamReader(new URL("http://restx.io/version").openStream(), Charsets.UTF_8)) {
                List<String> parts = CharStreams.readLines(reader);
                if (parts.size() < 2) {
                    shell.printIn(
                            "unexpected content at http://restx.io/version, try again later or contact the group.\n",
                            RestxShell.AnsiCodes.ANSI_RED);
                    shell.println("content: ");
                    shell.println(Joiner.on("\n").join(parts));
                    return;
                }

                String version = parts.get(0);
                String url = parts.get(1);

                if (!version.equals(shell.version())) {
                    shell.printIn("upgrading to " + version, RestxShell.AnsiCodes.ANSI_GREEN);
                    shell.println("");
                    shell.println("please wait while downloading new version, this may take a while...");

                    boolean isWindows = System.getProperty("os.name").toLowerCase().indexOf("win") >= 0;
                    String archiveExt = isWindows ? ".zip" : ".tar.gz";
                    String scriptExt = isWindows ? ".bat" : ".sh";

                    URL source = new URL(url + archiveExt);
                    shell.download(source, shell.installLocation().resolve("upgrade" + archiveExt).toFile());

                    Resources.asByteSource(Resources.getResource(PluginsShellCommand.class, "upgrade" + scriptExt))
                            .copyTo(Files.asByteSink(shell.installLocation().resolve("upgrade" + scriptExt).toFile()));

                    shell.println("downloaded version " + version + ", restarting");
                    shell.restart();
                }
            }
        }
    }
}
TOP

Related Classes of restx.plugins.PluginsShellCommand$InstallPluginRunner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.