Package org.jboss.forge.shell.plugins.builtin

Source Code of org.jboss.forge.shell.plugins.builtin.ForgePlugin

/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.jboss.forge.shell.plugins.builtin;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.enterprise.event.Event;
import javax.inject.Inject;

import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Ref;
import org.jboss.forge.ForgeEnvironment;
import org.jboss.forge.env.Configuration;
import org.jboss.forge.git.GitUtils;
import org.jboss.forge.parser.ParserException;
import org.jboss.forge.parser.java.util.Assert;
import org.jboss.forge.parser.java.util.Strings;
import org.jboss.forge.parser.xml.Node;
import org.jboss.forge.parser.xml.XMLParser;
import org.jboss.forge.project.Project;
import org.jboss.forge.project.dependencies.CompositeDependencyFilter;
import org.jboss.forge.project.dependencies.Dependency;
import org.jboss.forge.project.dependencies.DependencyBuilder;
import org.jboss.forge.project.dependencies.DependencyFilter;
import org.jboss.forge.project.dependencies.DependencyQuery;
import org.jboss.forge.project.dependencies.DependencyQueryBuilder;
import org.jboss.forge.project.dependencies.DependencyRepositoryImpl;
import org.jboss.forge.project.dependencies.DependencyResolver;
import org.jboss.forge.project.dependencies.NonSnapshotDependencyFilter;
import org.jboss.forge.project.dependencies.ScopeType;
import org.jboss.forge.project.facets.DependencyFacet;
import org.jboss.forge.project.facets.MetadataFacet;
import org.jboss.forge.project.facets.PackagingFacet;
import org.jboss.forge.project.packaging.PackagingType;
import org.jboss.forge.resources.DependencyResource;
import org.jboss.forge.resources.DirectoryResource;
import org.jboss.forge.resources.FileResource;
import org.jboss.forge.resources.Resource;
import org.jboss.forge.shell.InstalledPluginRegistry;
import org.jboss.forge.shell.PluginEntry;
import org.jboss.forge.shell.Shell;
import org.jboss.forge.shell.ShellColor;
import org.jboss.forge.shell.ShellMessages;
import org.jboss.forge.shell.ShellPrintWriter;
import org.jboss.forge.shell.ShellPrompt;
import org.jboss.forge.shell.Wait;
import org.jboss.forge.shell.events.PluginInstalled;
import org.jboss.forge.shell.events.PluginRemoved;
import org.jboss.forge.shell.events.ReinitializeEnvironment;
import org.jboss.forge.shell.exceptions.Abort;
import org.jboss.forge.shell.exceptions.AbortedException;
import org.jboss.forge.shell.plugins.Alias;
import org.jboss.forge.shell.plugins.Command;
import org.jboss.forge.shell.plugins.DefaultCommand;
import org.jboss.forge.shell.plugins.Help;
import org.jboss.forge.shell.plugins.Option;
import org.jboss.forge.shell.plugins.PipeOut;
import org.jboss.forge.shell.plugins.Plugin;
import org.jboss.forge.shell.plugins.Topic;
import org.jboss.forge.shell.util.Files;
import org.jboss.forge.shell.util.PluginRef;
import org.jboss.forge.shell.util.PluginUtil;

/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
@Alias("forge")
@Topic("Shell Environment")
@Help("Forge control and writer environment commands. Manage plugins and other forge addons.")
public class ForgePlugin implements Plugin
{

   private static final String MODULE_TEMPLATE_XML = "/org/jboss/forge/modules/module-template.xml";
   private final Event<ReinitializeEnvironment> reinitializeEvent;

   private final Event<PluginInstalled> pluginInstalledEvent;
   private final Event<PluginRemoved> pluginRemovedEvent;
   private final ShellPrintWriter writer;
   private final DependencyResolver resolver;
   private final ForgeEnvironment environment;
   private final ShellPrompt prompt;
   private final Shell shell;
   private final Configuration configuration;

   @Inject
   private Wait wait;

   @Inject
   public ForgePlugin(final ForgeEnvironment environment, final Event<ReinitializeEnvironment> reinitializeEvent,
            final ShellPrintWriter writer, final ShellPrompt prompt, final DependencyResolver resolver,
            final Shell shell, final Configuration configuration, final Event<PluginInstalled> pluginInstalledEvent,
            final Event<PluginRemoved> pluginRemovedEvent)
   {
      this.environment = environment;
      this.reinitializeEvent = reinitializeEvent;
      this.writer = writer;
      this.prompt = prompt;
      this.shell = shell;
      this.resolver = resolver;
      this.configuration = configuration;
      this.pluginInstalledEvent = pluginInstalledEvent;
      this.pluginRemovedEvent = pluginRemovedEvent;
   }

   /*
    * General methods
    */

   @DefaultCommand
   public void about(final PipeOut out)
   {
      out.println("    _____                    ");
      out.println("   |  ___|__  _ __ __ _  ___ ");
      out.println("   | |_ / _ \\| `__/ _` |/ _ \\  "
               + out.renderColor(ShellColor.YELLOW, "\\\\"));
      out.println("   |  _| (_) | | | (_| |  __/  "
               + out.renderColor(ShellColor.YELLOW, "//"));
      out.println("   |_|  \\___/|_|  \\__, |\\___| ");
      out.println("                   |___/      ");
      out.println("");
      out.print(ShellColor.ITALIC, "JBoss Forge");
      out.print(", version [ ");
      out.print(ShellColor.BOLD, environment.getRuntimeVersion());
      out.print(" ] - JBoss, by ");
      out.print(ShellColor.RED, "Red Hat, Inc.");
      out.println(" [ http://forge.jboss.org ]");
   }

   @Command(value = "restart", help = "Reload all plugins and default configurations")
   public void restart() throws Exception
   {
      reinitializeEvent.fire(new ReinitializeEnvironment());
   }

   @Command(value = "list-plugins", help = "List all installed plugin JAR files.")
   public void listInstalled(PipeOut out, String input)
   {
      List<PluginEntry> plugins = InstalledPluginRegistry.list();
      for (PluginEntry plugin : plugins)
      {
         if (Strings.isNullOrEmpty(input) || plugin.toString().contains(input))
         {
            out.println(plugin.toString());
         }
      }
   }

   /*
    * Plugin installation
    */

   @Command(value = "find-plugin",
            help = "Searches the configured Forge plugin index for a plugin matching the given search text")
   public void find(@Option(description = "search string") final String searchString, final PipeOut out)
            throws Exception
   {
      List<PluginRef> pluginList = PluginUtil.findPlugin(shell, configuration, searchString);

      if (!pluginList.isEmpty())
      {
         out.println();
      }
      for (PluginRef ref : pluginList)
      {
         out.println(" - " + out.renderColor(ShellColor.BOLD, ref.getName()) + " (" + ref.getArtifact() + ")");
         out.println("\tAuthor: " + ref.getAuthor());
         out.println("\tWebsite: " + ref.getWebsite());
         out.println("\tLocation: " + ref.getLocation());
         out.println("\tTags: " + ref.getTags());
         out.println("\tDescription: " + ref.getDescription());
         out.println();
      }
   }

   @Command(value = "remove-plugin",
            help = "Removes a plugin from the current Forge runtime configuration")
   public void removePlugin(
            @Option(completer = InstalledPluginCompleter.class, description = "plugin-name", required = true,
                     help = "The fully qualified plugin name e.g: 'org.jboss.forge.plugin:version'") final String pluginName,
            final PipeOut out) throws Exception
   {

      PluginEntry plugin = PluginEntry.fromCoordinates(pluginName);
      if (!InstalledPluginRegistry.has(plugin))
      {
         throw new RuntimeException("No such installed plugin [" + pluginName + "]");
      }
      PluginEntry installedPlugin = InstalledPluginRegistry.get(plugin);
      InstalledPluginRegistry.remove(installedPlugin);
      pluginRemovedEvent.fire(new PluginRemoved(installedPlugin));
      if (!InstalledPluginRegistry.has(plugin))
      {
         ShellMessages.success(out, "Successfully removed [" + pluginName + "]");
         restart();
      }
      else
      {
         ShellMessages.error(out, "Failed to remove [" + pluginName + "");
      }
   }

   @Command(value = "install-plugin",
            help = "Installs a plugin from the configured Forge plugin index")
   public void installFromIndex(
            @Option(description = "plugin-name", completer = IndexPluginNameCompleter.class) final String pluginName,
            @Option(name = "version", description = "branch, tag, or version to build") final String version,
            final PipeOut out) throws Exception
   {
      PluginRef plugin = PluginUtil.findPluginByName(shell, configuration, pluginName, true);

      if (plugin == null)
      {
         throw new RuntimeException("no plugin found with name [" + pluginName + "]");
      }
      else
      {
         ShellMessages.info(out, "Preparing to install plugin: " + plugin.getName());

         if (plugin.isGit())
         {
            installFromGit(plugin.getGitRepo(), Strings.isNullOrEmpty(version) ? plugin.getGitRef() : version, null,
                     false,
                     out);
         }
         else
         {
            throw new UnsupportedOperationException("Not yet implemented");
         }
      }
   }

   @Command(value = "source-plugin",
            help = "Install a plugin from a local project folder")
   public void installFromLocalProject(
            @Option(description = "project directory", required = true) final Resource<?> projectFolder,
            final PipeOut out) throws Exception
   {
      DirectoryResource workspace = projectFolder.reify(DirectoryResource.class);
      if ((workspace == null) || !workspace.exists())
      {
         throw new IllegalArgumentException("Project folder must be specified.");
      }

      buildFromCurrentProject(out, workspace);

      ShellMessages.success(out, "Installed from [" + workspace + "] successfully.");
      restart();
   }

   @Command(value = "git-plugin",
            help = "Install a plugin from a public git repository")
   public void installFromGit(
            @Option(description = "git repo", required = true) final String gitRepo,
            @Option(name = "ref", description = "branch or tag to build") final String refName,
            @Option(name = "checkoutDir", description = "directory in which to clone the repository") final Resource<?> checkoutResource,
            @Option(name = "keepSources", description = "keep the sources after checking out", defaultValue = "false", flagOnly = true) final boolean keepSources,
            final PipeOut out) throws Exception
   {
      DirectoryResource buildDir;
      if (checkoutResource != null)
      {
         if (!(checkoutResource instanceof FileResource<?>))
         {
            throw new IllegalArgumentException("Checkout dir must be a directory path");
         }
         FileResource<?> checkoutDir = (FileResource<?>) checkoutResource;
         // Resource already exists
         if (checkoutDir.exists())
         {
            // Check if it is already a directory
            if (!checkoutDir.isDirectory())
            {
               throw new RuntimeException("Resource " + checkoutDir.getFullyQualifiedName()
                        + " is not a valid directory.");
            }
            buildDir = checkoutDir.reify(DirectoryResource.class);
            if (!shell.promptBoolean("Directory " + buildDir.getFullyQualifiedName()
                     + " already exists. Do you want to overwrite?", false))
            {
               throw new AbortedException("Directory " + buildDir.getFullyQualifiedName()
                        + " already exists");
            }
            buildDir.delete(true);
            buildDir.mkdirs();
         }
         else
         {
            // Resource does not exist. Create it
            checkoutDir.mkdirs();
            buildDir = checkoutDir.reify(DirectoryResource.class);
         }
      }
      else
      {
         buildDir = shell.getCurrentDirectory().createTempResource();
      }

      try
      {
         ShellMessages.info(out, "Checking out plugin source files to [" + buildDir.getFullyQualifiedName()
                  + "] via 'git'");
         Git repo = GitUtils.clone(buildDir, gitRepo);

         Ref ref = null;
         String targetRef = refName;
         if (targetRef == null)
         {
            // Default to Forge runtime version if no Ref name is supplied.
            targetRef = environment.getRuntimeVersion();
         }

         if (targetRef != null)
         {
            // Try to find a Tag matching the given Ref name or runtime version
            Map<String, Ref> tags = repo.getRepository().getTags();
            ref = tags.get(targetRef);

            // Now try to find a matching Branch
            if (ref == null)
            {
               List<Ref> refs = GitUtils.getRemoteBranches(repo);
               for (Ref branchRef : refs)
               {
                  String branchName = branchRef.getName();
                  if (branchName != null && branchName.endsWith(targetRef))
                  {
                     ref = repo.branchCreate().setName(targetRef).setUpstreamMode(SetupUpstreamMode.TRACK)
                              .setStartPoint("origin/" + targetRef).call();
                  }
               }
            }

            // Now try to find a tag or branch with same Major.Minor.(x) version.
            if (ref == null)
            {
               // All
               List<String> sortedVersions = new ArrayList<String>();

               // Branches
               for (Ref branchRef : GitUtils.getRemoteBranches(repo))
               {
                  String branchName = branchRef.getName();
                  branchName = branchName.replaceFirst("refs/heads/", "");
                  if (InstalledPluginRegistry.isApiCompatible(targetRef, branchName))
                     sortedVersions.add(branchName);
               }

               // Tags

               // Branches
               for (String tag : tags.keySet())
               {
                  if (InstalledPluginRegistry.isApiCompatible(targetRef, tag))
                     sortedVersions.add(tag);
               }

               // Sort
               Collections.sort(sortedVersions);

               if (!sortedVersions.isEmpty())
               {
                  String version = sortedVersions.get(sortedVersions.size() - 1);
                  if (InstalledPluginRegistry.isApiCompatible(targetRef, version))
                  {
                     ref = tags.get(version);

                     if (ref == null)
                     {
                        ref = repo.branchCreate().setName(version).setUpstreamMode(SetupUpstreamMode.TRACK)
                                 .setStartPoint("origin/" + version).call();
                     }
                  }
               }
            }
         }

         if (ref == null)
         {
            ref = repo.getRepository().getRef("master");
         }

         if (ref != null)
         {
            ShellMessages.info(out, "Switching to branch/tag [" + ref.getName() + "]");
            GitUtils.checkout(repo, ref, false, SetupUpstreamMode.TRACK, false);
         }
         else if (refName != null)
         {
            throw new RuntimeException("Could not locate ref [" + targetRef + "] in repository ["
                     + repo.getRepository().getDirectory().getAbsolutePath() + "]");
         }
         else
         {
            ShellMessages.warn(
                     out,
                     "Could not find a Ref matching the current Forge version ["
                              + environment.getRuntimeVersion()
                              + "], building Plugin from HEAD.");
         }

         buildFromCurrentProject(out, buildDir);
      }
      finally
      {
         if (buildDir != null)
         {
            if (keepSources)
            {
               ShellMessages.info(out,
                        "Sources are kept in [" + buildDir.getFullyQualifiedName() + "]");
            }
            else
            {
               ShellMessages.info(out,
                        "Cleaning up temp workspace [" + buildDir.getFullyQualifiedName()
                                 + "]");
               buildDir.delete(true);
            }
         }
      }

      ShellMessages.success(out, "Installed from [" + gitRepo + "] successfully.");
      restart();
   }

   /**
    * Aborts a forge update
    */
   @Command(value = "update-abort", help = "Aborts a previous forge update")
   public void updateAbort() throws IOException
   {
      DirectoryResource forgeHome = environment.getForgeHome();
      DirectoryResource updateDirectory = forgeHome.getChildDirectory(".update");
      if (updateDirectory.exists())
      {
         if (updateDirectory.delete(true))
         {
            ShellMessages.success(shell,
                     "Update files were deleted. Run 'forge update' if you want to update this installation again.");
         }
         else
         {
            ShellMessages.info(shell, "Could not abort. Try to run 'forge update-abort' again");
         }
      }
      else
      {
         ShellMessages.info(shell, "No update files found");
      }
   }

   /**
    * Updates the forge version
    */
   @Command(value = "update", help = "Update this forge installation")
   public void update() throws IOException
   {
      DirectoryResource forgeHome = environment.getForgeHome();
      DirectoryResource updateDir = forgeHome.getChildDirectory(".update");
      if (updateDir.exists())
      {
         ShellMessages
                  .warn(shell,
                           "There is an update pending. Restart Forge for the update to take effect. To abort this update, type 'forge update-abort'");
         return;
      }
      Dependency forgeDistribution = getLatestAvailableDistribution();
      if (forgeDistribution == null)
      {
         ShellMessages.info(shell, "Forge is up to date! Enjoy!");
      }
      else
      {
         shell.print(ShellColor.YELLOW, "***INFO*** ");
         shell.print("This Forge installation will be updated to ");
         shell.println(ShellColor.BOLD, forgeDistribution.getVersion());
         if (prompt.promptBoolean("Is that ok ?", true))
         {
            updateForge(forgeDistribution);
         }
      }
   }

   /**
    * Returns the latest available distribution
    *
    * @return
    */
   private Dependency getLatestAvailableDistribution()
   {
      final String runtimeVersion = environment.getRuntimeVersion();
      DependencyQuery query = DependencyQueryBuilder.create(DependencyBuilder
               .create("org.jboss.forge:forge-distribution:::zip")).setFilter(
               new CompositeDependencyFilter(
                        new NonSnapshotDependencyFilter(),
                        new DependencyFilter()
                        {
                           @Override
                           public boolean accept(Dependency dependency)
                           {
                              // We are only interested in 1.x Final versions higher than the current version
                              // TODO: Consider the version string for 2.x and higher releases when porting.
                              String version = dependency.getVersion();
                              return version.compareTo(runtimeVersion) > 0 && version.startsWith("1.")
                                       && version.endsWith(".Final");
                           }
                        }
               )).setRepositories(new DependencyRepositoryImpl(DependencyFacet.KnownRepository.JBOSS_NEXUS));
      List<Dependency> versions = resolver.resolveVersions(query);
      return versions.isEmpty() ? null : versions.get(versions.size() - 1);
   }

   /**
    * Unpacks the dependency info a specific folder
    *
    * @param dependency
    */
   private void updateForge(Dependency dependency) throws IOException
   {
      wait.start("Update in progress. Please wait");
      List<DependencyResource> resolvedArtifacts = resolver.resolveArtifacts(dependency);
      Assert.isTrue(resolvedArtifacts.size() == 1, "Artifact was not found");
      DependencyResource resource = resolvedArtifacts.get(0);
      DirectoryResource forgeHome = environment.getForgeHome();
      Files.unzip(resource.getUnderlyingResourceObject(), forgeHome.getUnderlyingResourceObject());

      DirectoryResource childDirectory = forgeHome.getChildDirectory(dependency.getArtifactId() + "-"
               + dependency.getVersion());

      DirectoryResource updateDirectory = forgeHome.getChildDirectory(".update");
      if (updateDirectory.exists())
      {
         updateDirectory.delete(true);
      }
      childDirectory.renameTo(updateDirectory);
      wait.stop();
      ShellMessages.success(shell, "Forge will now restart to complete the update...");
      System.exit(0);
   }

   /*
    * Helpers
    */
   private void buildFromCurrentProject(final PipeOut out, final DirectoryResource buildDir) throws Abort
   {
      DirectoryResource savedLocation = shell.getCurrentDirectory();
      try
      {
         shell.setCurrentResource(buildDir);
         Project project = shell.getCurrentProject();
         if (project == null)
         {
            throw new IllegalStateException("Unable to recognise plugin project in ["
                     + buildDir.getFullyQualifiedName() + "]");
         }

         DependencyFacet deps = project.getFacet(DependencyFacet.class);
         DependencyBuilder shellApi = DependencyBuilder.create("org.jboss.forge:forge-shell-api");

         String apiVersion = null;
         if (!deps.hasEffectiveDependency(shellApi)
                  && !prompt.promptBoolean(
                           "The project does not appear to be a Forge Plugin Project, install anyway?",
                           false))
         {
            throw new Abort("Installation aborted");
         }
         else
         {
            Dependency directDependency = deps.getDirectDependency(shellApi);
            if ((directDependency != null) && !Strings.isNullOrEmpty(directDependency.getVersion()))
               apiVersion = directDependency.getVersion();

            if (apiVersion == null)
            {
               // Fall back to checking managed dependencies for a version
               Dependency managedDependency = deps.getManagedDependency(shellApi);
               if ((managedDependency != null) && !Strings.isNullOrEmpty(managedDependency.getVersion()))
                  apiVersion = managedDependency.getVersion();
            }

            if (apiVersion == null)
            {
               // Now completely give up and just use the result from the build
               Dependency effectiveDependency = deps.getEffectiveDependency(shellApi);
               if (effectiveDependency != null)
                  apiVersion = effectiveDependency.getVersion();
               else
                  apiVersion = environment.getRuntimeVersion();
            }
         }

         /**
          * Make sure that our PROVIDED modules are not included in the module dependencies
          */
         // TODO Weld bug requires us to correct /add module for Seam Render dependency
         List<String> groupIds = Arrays.asList("org.jboss.seam.render", "org.jboss.forge");
         List<String> providedDeps = Arrays.asList("forge-javaee-api", "forge-maven-api", "forge-scaffold-api",
                  "forge-scaffoldx-api", "forge-shell-api");
         List<Dependency> dependencies = deps.getDependencies();
         for (Dependency dependency : dependencies)
         {
            if (groupIds.contains(dependency.getGroupId())
                     && !(ScopeType.PROVIDED.equals(dependency.getScopeTypeEnum())
                     || ScopeType.TEST.equals(dependency.getScopeTypeEnum())))
            {
               ShellMessages.warn(out, "Dependency [" + dependency.toCoordinates()
                        + "] was not correctly marked as PROVIDED scope; this has been corrected.");
               deps.addDirectDependency(DependencyBuilder.create(dependency).setScopeType(ScopeType.PROVIDED));
            }
            else if (dependency.getGroupId().equals("org.jboss.forge")
                     && !providedDeps.contains(dependency.getArtifactId())
                     && !ScopeType.TEST.equals(deps.getEffectiveDependency(dependency).getScopeTypeEnum()))
            {
               ShellMessages.warn(writer,
                        "Plugin has a dependency on internal Forge API [" + dependency
                                 + "] - this is not allowed and may cause failures.");
            }
         }

         ShellMessages.info(out, "Invoking build with underlying build system.");
         Resource<?> artifact = project.getFacet(PackagingFacet.class).createBuilder().runTests(false).build();
         if ((artifact != null) && artifact.exists())
         {
            MetadataFacet meta = project.getFacet(MetadataFacet.class);
            Dependency dep = meta.getOutputDependency();

            ShellMessages.info(out, "Installing plugin artifact.");

            // TODO Figure out a better plugin versioning strategy than random numbers, also see if unloading is
            // possible to avoid this entirely.
            createModule(
                     project,
                     DependencyBuilder.create(dep).setVersion(
                              dep.getVersion() + "-" + UUID.randomUUID().toString()),
                     artifact, apiVersion);
         }
         else
         {
            throw new IllegalStateException("Build artifact [" + artifact
                     + "] is missing and cannot be installed. Please resolve build errors and try again.");
         }
      }
      finally
      {
         shell.setCurrentResource(savedLocation);
      }
   }

   private boolean needDependenciesAsResourceRoot(final Project project)
   {
      FileResource<?> forgeXml = (FileResource<?>) project.getProjectRoot().getChild(
               "src/main/resources/META-INF/forge.xml");
      if (forgeXml.exists())
      {
         try
         {
            Node node = XMLParser.parse(forgeXml.getResourceInputStream());
            return node.getSingle("dependencies-as-resource-root") != null;
         }
         catch (ParserException e)
         {
            return false;
         }
      }
      return false;
   }

   private DirectoryResource createModule(final Project project, final Dependency dep, final Resource<?> artifact,
            final String apiVersion)
   {

      boolean dependenciesAsResourceRoot = needDependenciesAsResourceRoot(project);

      DirectoryResource moduleDir = getOrCreatePluginModuleDirectory(dep);
      String pluginName = dep.getGroupId() + "." + dep.getArtifactId();
      String pluginSlot = dep.getVersion();

      FileResource<?> moduleXml = (FileResource<?>) moduleDir.getChild("module.xml");
      if (moduleXml.exists()
               && !prompt.promptBoolean(
                        "An existing installation for version [" + pluginSlot
                                 + "] of this plugin was found. Replace it?", true))
      {
         throw new RuntimeException("Aborted.");
      }

      moduleXml.delete();
      moduleXml.createNewFile();

      // <resource-root path="maven-dependency.jar" />
      Node module = XMLParser.parse(getClass().getResourceAsStream(MODULE_TEMPLATE_XML));
      module.attribute("name", pluginName);
      module.attribute("slot", pluginSlot);
      Node resources = module.getSingle("resources");

      resources.createChild("resource-root").attribute("path", dep.getArtifactId() + ".jar");

      if (dependenciesAsResourceRoot)
      {
         writeResourceRoots(project, module, moduleDir, resources);
      }

      // Copy the compiled JAR into the module directory
      FileResource<?> jar = moduleDir.getChild(dep.getArtifactId() + ".jar").reify(FileResource.class);
      jar.createNewFile();
      jar.setContents(artifact.getResourceInputStream());

      // <module name="org.jboss.forge:main" />
      Node dependencies = module.getSingle("dependencies");

      if (!dependenciesAsResourceRoot)
      {
         dependencies.createChild("module").attribute("name", pluginName + ".dependencies")
                  .attribute("slot", pluginSlot);
      }

      dependencies.createChild("module").attribute("name", "org.jboss.forge.javaee.api")
               .attribute("services", "import");
      dependencies.createChild("module").attribute("name", "org.jboss.forge.maven.api")
               .attribute("services", "import");
      dependencies.createChild("module").attribute("name", "org.jboss.forge.scaffold.api")
               .attribute("services", "import");
      dependencies.createChild("module").attribute("name", "org.jboss.forge.scaffoldx.api")
               .attribute("services", "import");
      dependencies.createChild("module").attribute("name", "org.jboss.forge.shell.api")
               .attribute("services", "import");
      dependencies.createChild("module").attribute("name", "org.jboss.seam.render").attribute("services", "import");
      dependencies.createChild("module").attribute("name", "javax.api");

      moduleXml.setContents(XMLParser.toXMLString(module));

      if (!dependenciesAsResourceRoot)
      {
         createDependenciesModule(project, dep);
      }

      // Add to list modules.
      registerPlugin(pluginName, pluginSlot, apiVersion);

      return moduleDir;
   }

   private List<DependencyResource> getPluginDependencies(final Project project, Node module)
   {
      DependencyFacet deps = project.getFacet(DependencyFacet.class);
      List<DependencyResource> pluginDependencies = new ArrayList<DependencyResource>();
      List<Dependency> effectiveDependenciesInScopes = deps.getEffectiveDependenciesInScopes(ScopeType.COMPILE,
               ScopeType.RUNTIME);
      for (Dependency d : effectiveDependenciesInScopes)
      {
         if (d.getPackagingTypeEnum().equals(PackagingType.JAR)
                  && !d.getGroupId().equals("org.jboss.forge"))
         {
            List<DependencyResource> artifacts = resolveArtifacts(project, d);
            pluginDependencies.addAll(artifacts);
         }
         // TODO encapsulate this?
         if (DependencyBuilder.areEquivalent(d, DependencyBuilder.create("org.jboss.forge:forge-javaee-api")))
         {
            module.getSingle("dependencies").createChild("module")
                     .attribute("name", "org.jboss.forge.javaee.api")
                     .attribute("services", "import");
         }
         else if (DependencyBuilder.areEquivalent(d, DependencyBuilder.create("org.jboss.forge:forge-scaffold-api")))
         {
            module.getSingle("dependencies").createChild("module")
                     .attribute("name", "org.jboss.forge.scaffold.api")
                     .attribute("services", "import");
         }
         else if (DependencyBuilder.areEquivalent(d, DependencyBuilder.create("org.jboss.forge:forge-scaffoldx-api")))
         {
            module.getSingle("dependencies").createChild("module")
                     .attribute("name", "org.jboss.forge.scaffoldx.api")
                     .attribute("services", "import");
         }
         else if (DependencyBuilder.areEquivalent(d, DependencyBuilder.create("org.jboss.forge:forge-maven-api")))
         {
            module.getSingle("dependencies").createChild("module")
                     .attribute("name", "org.jboss.forge.maven.api")
                     .attribute("services", "import");
         }
         else if (d.getGroupId().equals("org.jboss.forge"))
         {
            ShellMessages.error(writer,
                     "Plugin has a dependency on internal Forge API [" + d
                              + "] - this is not allowed and may cause failures.");
         }
      }
      return pluginDependencies;
   }

   private void writeResourceRoots(
            final Project project,
            final Node module,
            final DirectoryResource directory,
            final Node resources)
   {
      List<DependencyResource> pluginDependencies = getPluginDependencies(project, module);
      for (DependencyResource d : pluginDependencies)
      {
         String name = d.getName();
         Resource<?> child = directory.getChild(name);
         child.delete();
         FileResource<?> depJar = child.reify(FileResource.class);
         depJar.setContents(d.getResourceInputStream());
         resources.createChild("resource-root").attribute("path", name);
      }
   }

   private void createDependenciesModule(final Project project, final Dependency dep)
   {
      DirectoryResource dependencyDir = getOrCreatePluginDependenciesModuleDirectory(dep);
      String pluginName = dep.getGroupId() + "." + dep.getArtifactId();
      String pluginSlot = dep.getVersion();

      FileResource<?> moduleXml = (FileResource<?>) dependencyDir.getChild("module.xml");
      moduleXml.delete();
      moduleXml.createNewFile();

      // <resource-root path="maven-dependency.jar" />
      Node module = XMLParser.parse(getClass().getResourceAsStream(MODULE_TEMPLATE_XML));
      module.attribute("name", pluginName + ".dependencies");
      module.attribute("slot", pluginSlot);
      Node resources = module.getSingle("resources");

      // <module name="org.jboss.forge:main" />
      Node dependencies = module.getSingle("dependencies");
      dependencies.createChild("module").attribute("name", "javax.api");
      dependencies.createChild("module").attribute("name", "org.jboss.forge.shell.api");

      writeResourceRoots(project, module, dependencyDir, resources);

      // Write out the module XML file.
      moduleXml.setContents(XMLParser.toXMLString(module));
   }

   private List<DependencyResource> resolveArtifacts(final Project project, final Dependency dep)
   {
      Dependency d = dep;

      List<DependencyResource> artifacts = new ArrayList<DependencyResource>();
      DependencyFacet deps = project.getFacet(DependencyFacet.class);

      for (Dependency d2 : deps.getDependencies())
      {
         if (DependencyBuilder.areEquivalent(d, d2) && (d2.getVersion() != null))
         {
            d = d2;
            break;
         }
      }

      if (artifacts.size() != 1)
      {
         artifacts = resolver.resolveArtifacts(d, deps.getRepositories());
      }
      if (artifacts.size() != 1)
      {
         ShellMessages.warn(writer, "Could not resolve dependency [" + d.toCoordinates() + "]");
      }

      return artifacts;
   }

   public void registerPlugin(final String pluginName, final String pluginSlot, final String apiVersion)
   {
      String runtimeVersion = InstalledPluginRegistry.getRuntimeAPIVersion();
      if (InstalledPluginRegistry.isApiCompatible(runtimeVersion, apiVersion))
      {
         PluginEntry entry = InstalledPluginRegistry.install(pluginName, apiVersion, pluginSlot);
         pluginInstalledEvent.fire(new PluginInstalled(entry));
      }
      else
      {
         throw new RuntimeException(
                  "Could not install plugin ["
                           + pluginName
                           + "] because it references Forge API version ["
                           + apiVersion
                           + "] which may not be compatible with my current version ["
                           + runtimeVersion
                           + "]. Please consider upgrading forge, by typing 'forge update'. Otherwise, try installing an older version of the plugin.");
      }
   }

   public DirectoryResource getOrCreatePluginModuleDirectory(final Dependency dep)
   {
      DirectoryResource pluginDir = environment.getPluginDirectory();

      List<String> groupId = Arrays.asList(dep.getGroupId().split("\\."));
      List<String> artifactId = Arrays.asList(dep.getArtifactId().split("\\."));
      DirectoryResource dir = pluginDir;
      for (String segment : groupId)
      {
         dir = dir.getOrCreateChildDirectory(segment);
      }

      for (String segment : artifactId)
      {
         dir = dir.getOrCreateChildDirectory(segment);
      }

      dir = dir.getOrCreateChildDirectory(dep.getVersion());
      return dir;
   }

   public DirectoryResource getOrCreatePluginDependenciesModuleDirectory(final Dependency dep)
   {
      DirectoryResource pluginDir = environment.getPluginDirectory();

      List<String> groupId = Arrays.asList(dep.getGroupId().split("\\."));
      List<String> artifactId = Arrays.asList(dep.getArtifactId().split("\\."));
      DirectoryResource dir = pluginDir;
      for (String segment : groupId)
      {
         dir = dir.getOrCreateChildDirectory(segment);
      }

      for (String segment : artifactId)
      {
         dir = dir.getOrCreateChildDirectory(segment);
      }

      dir = dir.getOrCreateChildDirectory("dependencies");
      dir = dir.getOrCreateChildDirectory(dep.getVersion());
      return dir;
   }
}
TOP

Related Classes of org.jboss.forge.shell.plugins.builtin.ForgePlugin

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.