private List<URL> generateExecutionClasspath(
      Set<Artifact> resolvedArtifacts, String... excludeGroups) throws MojoExecutionException {
    /*
     * Convert each resolved artifact into a URL/classpath element.
     */
    final ArrayList<URL> classpath = new ArrayList<URL>();

    final List<String> excludes = Arrays.asList(excludeGroups);

    try {

      for (Artifact resolvedArtifact : resolvedArtifacts) {
        if (excludes.contains(resolvedArtifact.getGroupId())) continue;
        final File file = resolvedArtifact.getFile();
        //        System.out.println("artifact " + resolvedArtifact.toString());
        if (file != null) {
          if (artifactIdsToInsertAtStartOfClasspath.contains(resolvedArtifact.getArtifactId())) {
            getLog().info("adding at the start" + file.getAbsolutePath());
            // a patch? grails is full of them, insert it at the start
            classpath.add(0, file.toURI().toURL());
          } else { // insert it at the end
            classpath.add(file.toURI().toURL());
          }
        }
      }
    } catch (MalformedURLException murle) {
      throw new MojoExecutionException("Unable to find files", murle);
    }

    return classpath;
  }
  private void resolveClasspath() throws MojoExecutionException {
    parsePatchArtifacts();

    getLog()
        .info(
            "Resolving dependencies"
                + (useTransitives
                    ? ""
                    : " - warning! we are not using transitive dependencies, only those directly in the pom.xml"));

    resolvedArtifacts = collectAllProjectArtifacts();

    /*
     * Remove any Grails plugins that may be in the resolved artifact set.  This is because we
     * do not need them on the classpath, as they will be handled later on by a separate call to
     * "install" them.
     */
    pluginArtifacts = removePluginArtifacts(resolvedArtifacts);

    pluginDirectories = new ArrayList<File>();

    for (Artifact artifact : pluginArtifacts)
      pluginDirectories.add(getPluginDirAndInstallIfNecessary(artifact));

    if (getLog().isInfoEnabled()) {
      for (File f : pluginDirectories) {
        getLog().info("plugin: " + f.getAbsolutePath());
      }
    }

    classpath = generateGrailsExecutionClasspath(resolvedArtifacts);

    System.gc();
  }
  private void printIntellijIDEASettings(
      DecentGrailsLauncher launcher, Field settingsField, Set<Artifact> pluginArtifacts) {
    try {
      Object settings = settingsField.get(launcher);
      Field configField = settings.getClass().getSuperclass().getDeclaredField("config");
      configField.setAccessible(true);
      Object config = configField.get(settings);
      Map flatten = (Map) config.getClass().getDeclaredMethod("flatten").invoke(config);

      System.out.println();
      System.out.println(SETTINGS_START_MARKER);

      for (Object key : flatten.keySet()) {
        Object value = flatten.get(key);
        if (value instanceof String || value instanceof GString) {
          String realKey = key.toString();
          if (GRAILS_PROPERTY_LIST.contains(realKey)) {
            System.out.println(realKey + "=" + value.toString().replace('\\', '/'));
          }
        }
      }

      for (Artifact plugin : pluginArtifacts) {
        File targetDir = getPluginTargetDir(plugin);
        System.out.println(
            "grails.plugin.location."
                + getPluginName(plugin)
                + "="
                + targetDir.getAbsolutePath().replace('\\', '/'));
      }

      System.out.println();
      System.out.println(SETTINGS_END_MARKER);
    } catch (Exception ex) {
      getLog().error("Unable to get flattened configuration data", ex);
    }
  }
  // we only need to do these once as they don't change
  private void doOncePerArtifact() throws MojoExecutionException {
    lastArtifactId = project.getArtifactId();
    lastGroupId = project.getGroupId();

    configureMavenProxy();

    resolveClasspath();
    //    printClasspath("main", Arrays.asList(classpath));

    grailsHomePath = (grailsHome != null) ? grailsHome.getAbsolutePath() : null;

    if (isWindows()) { // force console and interactive on to get around _GrailsRun.groovy windows
      // bug where attaches to grailsConsole.reader.add...
      System.setProperty("grails.console.enable.terminal", "true");
      System.setProperty("grails.console.enable.interactive", "true");
    } else {
      if (System.getProperty("grails.console.enable.terminal") == null)
        System.setProperty("grails.console.enable.terminal", "true");
      if (System.getProperty("grails.console.enable.interactive") == null)
        System.setProperty("grails.console.enable.interactive", "true");
    }

    // override the servlet factory if it is specified
    if (this.servletServerFactory != null) {
      System.setProperty("grails.server.factory", this.servletServerFactory);
    }

    // see if we are using logback and not log4j
    //    final String logbackFilename = this.getBasedir() + "/logback.xml";
    //
    //    if (new File(logbackFilename).exists()) {
    //      getLog().info("Found logback configuration, setting logback.xml to " + logbackFilename);
    //
    //      System.setProperty("logback.configurationFile", logbackFilename);
    //    }

  }
  /**
   * Executes the requested Grails target. The "targetName" must match a known Grails script
   * provided by grails-scripts.
   *
   * @param targetName The name of the Grails target to execute.
   * @param args String of arguments to be passed to the executed Grails target.
   * @throws MojoExecutionException if an error occurs while attempting to execute the target.
   */
  protected void runGrails(final String targetName, String args) throws MojoExecutionException {
    if (((lastArgs != null && lastArgs.equals(args)) || (lastArgs == null && args == null))
        && lastTargetName != null
        && lastTargetName.equals(targetName)) return;

    lastArgs = args;
    lastTargetName = targetName;

    if (!alreadyLoaderClasspathForArtifact()) doOncePerArtifact();
    else if (targetName.equals("War")) resolveClasspath(); // we have to get rid of the test rubbish

    getLog()
        .info(
            "Grails target: "
                + targetName
                + " raw args:"
                + args
                + " (pom says Grails Version is "
                + grailsVersion
                + ")");

    InputStream currentIn = System.in;
    PrintStream currentOutput = System.out;

    try {
      RootLoader rootLoader = new RootLoader(addBinaryPluginWorkaround(classpath));

      // see if log4j is there and if so, initialize it
      try {
        Class cls = rootLoader.loadClass("org.springframework.util.Log4jConfigurer");
        invokeStaticMethod(
            cls, "initLogging", new Object[] {"classpath:grails-maven/log4j.properties"});
      } catch (Exception ex) {
        getLog().info("No log4j available, good!");
      }

      try {
        final DecentGrailsLauncher launcher =
            new DecentGrailsLauncher(rootLoader, grailsHomePath, basedir.getAbsolutePath());
        launcher.setPlainOutput(true);

        /**
         * this collects the different dependency levels (compile, runtime, test) and puts them into
         * the correct arrays to pass through to the Grails script launcher. If using Maven, you
         * should *never* see an Ivy message and if you do, immediately stop your build, figure out
         * the incorrect dependency, delete the ~/.ivy2 directory and try again.
         */
        Field settingsField = launcher.getClass().getDeclaredField("settings");
        settingsField.setAccessible(true);

        configureBuildSettings(
            launcher,
            resolvedArtifacts,
            settingsField,
            rootLoader.loadClass("grails.util.BuildSettings"),
            args);

        syncAppVersion();

        installGrailsPlugins(
            pluginDirectories,
            launcher,
            settingsField,
            rootLoader.loadClass("grails.util.AbstractBuildSettings"));

        // If the command is running in non-interactive mode, we
        // need to pass on the relevant argument.
        if (this.nonInteractive) {
          args = (args != null) ? "--non-interactive " + args : "--non-interactive ";
        }

        // consuming the standard output after execution via Maven.
        args = (args != null) ? "--plain-output " + args : "--plain-output";
        args = (args != null) ? "--stacktrace " + args : "--stacktrace";
        args = (args != null) ? "--verboseCompile " + args : "--verboseCompile";

        if (env == null) System.clearProperty("grails.env");
        else System.setProperty("grails.env", env);

        getLog()
            .info(
                "grails -Dgrails.env="
                    + (env == null ? "dev" : env)
                    + " "
                    + targetName.toLowerCase()
                    + " "
                    + args);
        int retval;

        if ("true".equals(System.getProperty("print.grails.settings"))
            || "ideaprintprojectsettings".equalsIgnoreCase(targetName)) {
          printIntellijIDEASettings(launcher, settingsField, pluginArtifacts);
        } else {

          if ("interactive".equals(targetName)) retval = launcher.launch("", "", env);
          else retval = launcher.launch(targetName, args, env);

          if (retval != 0) {
            throw new MojoExecutionException("Grails returned non-zero value: " + retval);
          }
        }
      } catch (final MojoExecutionException ex) {
        // Simply rethrow it.
        throw ex;
      } catch (final Exception ex) {
        getLog().error(ex);

        throw new MojoExecutionException("Unable to start Grails", ex);
      }

      rootLoader = null;
    } catch (MalformedURLException mfe) {
      throw new MojoExecutionException("Unable to start Grails", mfe);
    } finally {
      System.setIn(currentIn);
      System.setOut(currentOutput);
    }

    System.gc(); // try and help with memory issues
  }
  private File getPluginDirAndInstallIfNecessary(final Artifact plugin)
      throws MojoExecutionException {
    boolean targetDirOverridden = true;
    File targetDir = getPluginTargetDirOverride(plugin);
    if (targetDir == null) {
      targetDirOverridden = false;
      targetDir = getPluginTargetDirCentral(plugin);
    }

    String pluginName = getPluginName(plugin);
    final String pluginVersion = plugin.getVersion();
    boolean snapshot = pluginVersion.endsWith("-SNAPSHOT");

    if (snapshot
        && plugin
            .getFile()
            .getAbsolutePath()
            .endsWith("target" + File.separator + "classes")) { // multi module build

      targetDir = plugin.getFile().getParentFile().getParentFile();
      getLog()
          .info(
              String.format(
                  "Plugin %s:%s is coming from a multi-module dependency (%s)",
                  pluginName, pluginVersion, targetDir.getAbsolutePath()));

    } else if ((!snapshot && !targetDir.exists()) || (snapshot && !targetDirOverridden)) {
      // Unpack the plugin if it hasn't already been or if its a SNAPSHOT and not overridden by
      // -Dflag

      // Ideally we need to now do two things (a) see if we are running JDK7
      // and (b) determine if -Dplugin.groupId.artifactId has been set - if this is so, we want to
      // do a Files.createLink
      // to the directory specified by  the -D flag. We should probably also check if the targetDir
      // is a link and
      // the -Dflag hasn't been set, in which case we'd want to remove the link and install the
      // plugin (and let the user
      // know this has happened.
      // We wouldn't actually want this to be allowed when doing a release however.... So people
      // should make sure they don't
      // specify them, they they'll be installed.

      getLog()
          .info(
              String.format(
                  "Installing Plugin %s:%s into (%s)",
                  pluginName, pluginVersion, targetDir.getAbsolutePath()));
      targetDir.mkdirs();

      final ZipUnArchiver unzipper = new ZipUnArchiver();
      unzipper.enableLogging(new ConsoleLogger(Logger.LEVEL_ERROR, "zip-unarchiver"));
      unzipper.setSourceFile(plugin.getFile());
      unzipper.setDestDirectory(targetDir);
      unzipper.setOverwrite(true);
      try {
        unzipper.extract();
      } catch (ArchiverException e) {
        throw new MojoExecutionException("Unable to extract zip", e);
      }
      try {
        File inputPom =
            new File(plugin.getFile().getParentFile(), pluginName + "-" + pluginVersion + ".pom");
        File outputPom = new File(targetDir, "pom.xml");
        getLog()
            .info(
                String.format(
                    "copying %s to %s", inputPom.getAbsolutePath(), outputPom.getAbsolutePath()));
        FileReader fr = new FileReader(inputPom);
        FileWriter fw = new FileWriter(outputPom);
        IOUtils.copy(fr, fw);
        fw.flush();
        fw.close();
        fr.close();
      } catch (IOException e) {
        throw new MojoExecutionException("Unable to copy pom.xml file");
      }
    } else {
      getLog()
          .info(
              String.format(
                  "Plugin %s:%s already installed (%s)",
                  pluginName, pluginVersion, targetDir.getAbsolutePath()));
    }

    return targetDir;
  }