/**
   * Generates the classpath to be used by the launcher to execute the requested Grails script.
   *
   * @return An array of {@code URL} objects representing the dependencies required on the classpath
   *     to execute the selected Grails script.
   * @throws MojoExecutionException if an error occurs while attempting to resolve the dependencies
   *     and generate the classpath array.
   */
  @SuppressWarnings("unchecked")
  private URL[] generateGrailsExecutionClasspath(Set<Artifact> resolvedArtifacts)
      throws MojoExecutionException {
    try {

      final List<URL> classpath = generateExecutionClasspath(resolvedArtifacts);

      // check to see if someone is adding build listeners on the classpath, and if so, bring in the
      // system classpath and add it to our urls
      // IDEA for example does this
      if (System.getProperty("grails.build.listeners") != null) {
        String cp = System.getProperty("java.class.path");
        for (String c : cp.split(":")) {
          File f = new File(c);
          if (f.exists()) classpath.add(f.toURI().toURL());
        }
      }

      if (System.getProperty("grails.debug.classpath") != null) {
        for (URL url : classpath) {
          getLog().info("classpath " + url.toString());
        }
      }

      /*
       * Add the "tools.jar" to the classpath so that the Grails scripts can run native2ascii.
       * First assume that "java.home" points to a JRE within a JDK.  NOTE that this will not
       * provide a valid path on Mac OSX.  This is not a big deal, as the JDK on Mac OSX already
       * adds the required JAR's to the classpath.  This logic is really only for Windows/*Unix.
       */
      final String javaHome = System.getProperty("java.home");
      File toolsJar = new File(javaHome, "../lib/tools.jar");
      if (!toolsJar.exists()) {
        // The "tools.jar" cannot be found with that path, so
        // now try with the assumption that "java.home" points
        // to a JDK.
        toolsJar = new File(javaHome, "tools.jar");
      }

      if (toolsJar.exists()) {
        java.net.URL url = toolsJar.toURI().toURL();
        if (url != null) {
          classpath.add(url);
        }
      }

      return classpath.toArray(new URL[classpath.size()]);
    } catch (final Exception e) {
      throw new MojoExecutionException("Failed to create classpath for Grails execution.", e);
    }
  }
  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 configureMavenProxy() {
    if (settings != null) {
      Proxy activeProxy = settings.getActiveProxy();
      if (activeProxy != null) {
        String host = activeProxy.getHost();
        int port = activeProxy.getPort();
        String username = activeProxy.getUsername();
        String password = activeProxy.getPassword();

        System.setProperty("http.proxyHost", host);
        System.setProperty("http.proxyPort", String.valueOf(port));
        if (username != null) {
          System.setProperty("http.proxyUser", username);
        }
        if (password != null) {
          System.setProperty("http.proxyPassword", password);
        }
      }
    }
  }
  private File getPluginTargetDirOverride(Artifact plugin) {
    String pluginLocationOverride =
        System.getProperty(plugin.getGroupId() + ":" + plugin.getArtifactId());

    File targetDir = null;

    if (pluginLocationOverride != null && pluginLocationOverride.length() > 0) {
      targetDir = new File(pluginLocationOverride);
      if (!targetDir.exists()) {
        getLog()
            .error(
                String.format(
                    "Specified directory (%s) for plugin %s:%s:%s could not be found",
                    pluginLocationOverride,
                    plugin.getGroupId(),
                    plugin.getArtifactId(),
                    plugin.getVersion()));
        targetDir = null;
      }
    }
    return targetDir;
  }
  // 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);
    //    }

  }
  public Maven3ServerEmbedderImpl(MavenServerSettings settings) throws RemoteException {
    File mavenHome = settings.getMavenHome();
    if (mavenHome != null) {
      System.setProperty("maven.home", mavenHome.getPath());
    }

    myConsoleWrapper = new Maven3ServerConsoleLogger();
    myConsoleWrapper.setThreshold(settings.getLoggingLevel());

    ClassWorld classWorld =
        new ClassWorld("plexus.core", Thread.currentThread().getContextClassLoader());
    MavenCli cli =
        new MavenCli(classWorld) {
          @Override
          protected void customizeContainer(PlexusContainer container) {
            ((DefaultPlexusContainer) container)
                .setLoggerManager(
                    new BaseLoggerManager() {
                      @Override
                      protected Logger createLogger(String s) {
                        return myConsoleWrapper;
                      }
                    });
          }
        };

    Class cliRequestClass;
    try {
      cliRequestClass =
          MavenCli.class.getClassLoader().loadClass("org.apache.maven.cli.MavenCli$CliRequest");
    } catch (ClassNotFoundException e) {
      throw new RuntimeException("Class \"org.apache.maven.cli.MavenCli$CliRequest\" not found");
    }

    Object cliRequest;
    try {
      String[] commandLineOptions = new String[settings.getUserProperties().size()];
      int idx = 0;
      for (Map.Entry<Object, Object> each : settings.getUserProperties().entrySet()) {
        commandLineOptions[idx++] = "-D" + each.getKey() + "=" + each.getValue();
      }

      Constructor constructor =
          cliRequestClass.getDeclaredConstructor(String[].class, ClassWorld.class);
      constructor.setAccessible(true);
      cliRequest = constructor.newInstance(commandLineOptions, classWorld);

      for (String each : new String[] {"initialize", "cli", "properties", "container"}) {
        Method m = MavenCli.class.getDeclaredMethod(each, cliRequestClass);
        m.setAccessible(true);
        m.invoke(cli, cliRequest);
      }
    } catch (InstantiationException e) {
      throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
      throw new RuntimeException(e);
    }

    // reset threshold
    myContainer = FieldAccessor.get(MavenCli.class, cli, "container");
    myContainer.getLoggerManager().setThreshold(settings.getLoggingLevel());

    mySystemProperties =
        FieldAccessor.<Properties>get(cliRequestClass, cliRequest, "systemProperties");

    myMavenSettings =
        buildSettings(
            FieldAccessor.<SettingsBuilder>get(MavenCli.class, cli, "settingsBuilder"),
            settings,
            mySystemProperties,
            FieldAccessor.<Properties>get(cliRequestClass, cliRequest, "userProperties"));

    myLocalRepository = createLocalRepository(settings.getSnapshotUpdatePolicy());
  }
  /**
   * 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 boolean isWindows() {
   return System.getProperty("os.name").toLowerCase().contains("windows");
 }
  /**
   * Configures the launcher for execution.
   *
   * @param launcher The {@code GrailsLauncher} instance to be configured.
   */
  @SuppressWarnings("unchecked")
  private Set<Artifact> configureBuildSettings(
      final DecentGrailsLauncher launcher,
      Set<Artifact> resolvedArtifacts,
      Field settingsField,
      Class clazz,
      String args)
      throws ProjectBuildingException, MojoExecutionException {
    final String targetDir = this.project.getBuild().getDirectory();
    launcher.setDependenciesExternallyConfigured(true);

    // allow plugins that are being developed with fake api implementations to include the test
    // artifacts in the runtime
    if ((args != null && args.contains("--run-with-test-dependencies"))
        || runWithTestDependencies) {
      getLog().warn("grails-maven: Running with test dependencies");
      List<File> artifacts =
          artifactsToFiles(filterArtifacts(resolvedArtifacts, "compile", "runtime", "test"));
      launcher.setCompileDependencies(artifacts);
      launcher.setRuntimeDependencies(artifacts);
      launcher.setTestDependencies(artifacts);
    } else {
      // getCompileArtifacts, getRuntimeArtifacts and getTestArticats on the project are not
      // reliable
      logDependencies = "true".equals(System.getProperty("grails.maven.dependencies.compile"));
      launcher.setCompileDependencies(
          artifactsToFiles(filterArtifacts(resolvedArtifacts, "compile")));
      logDependencies = "true".equals(System.getProperty("grails.maven.dependencies.runtime"));
      launcher.setRuntimeDependencies(
          artifactsToFiles(filterArtifacts(resolvedArtifacts, "compile", "runtime")));
      logDependencies = "true".equals(System.getProperty("grails.maven.dependencies.test"));
      launcher.setTestDependencies(
          artifactsToFiles(filterArtifacts(resolvedArtifacts, "compile", "runtime", "test")));
      logDependencies = false;
    }

    launcher.setProjectWorkDir(new File(targetDir));
    launcher.setClassesDir(new File(targetDir, "classes"));
    launcher.setTestClassesDir(new File(targetDir, "test-classes"));
    launcher.setResourcesDir(new File(targetDir, "resources"));
    launcher.setProjectPluginsDir(this.pluginsDir);

    logDependencies = "true".equals(System.getProperty("grails.maven.dependencies.build"));
    List<File> files = artifactsToFiles(resolvedArtifacts);
    logDependencies = false;

    launcher.setBuildDependencies(files);

    Object settings = null;
    try {
      settings = settingsField.get(launcher);

      Field f = settings.getClass().getDeclaredField("defaultPluginSet");
      f.setAccessible(true);
      f.set(settings, new HashSet());
      f = settings.getClass().getDeclaredField("defaultPluginMap");
      f.setAccessible(true);
      f.set(settings, new LinkedHashMap());
      f = settings.getClass().getDeclaredField("enableResolve");
      f.setAccessible(true);
      f.set(settings, false);

    } catch (Exception e) {
      getLog().error("Unable to set default plugin set to empty ", e);
    }

    return resolvedArtifacts;
  }