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 Set<Artifact> filterArtifacts(Set<Artifact> resolvedArtifacts, String... scopes) { HashSet<Artifact> artifacts = new HashSet<Artifact>(); List<String> checkScopes = Arrays.asList(scopes); for (Artifact artifact : resolvedArtifacts) { if (checkScopes.contains(artifact.getScope())) artifacts.add(artifact); } return artifacts; }
@NotNull @Override public MavenServerExecutionResult resolveProject( @NotNull File file, @NotNull Collection<String> activeProfiles) throws RemoteException, MavenServerProcessCanceledException { DependencyTreeResolutionListener listener = new DependencyTreeResolutionListener(myConsoleWrapper); MavenExecutionResult result = doResolveProject( file, new ArrayList<String>(activeProfiles), Arrays.<ResolutionListener>asList(listener)); return createExecutionResult(file, result, listener.getRootNode()); }
/** * Gets around an issue where a binary plugin's resource refers to a source plugins's resource and * the binary plugin is subsequently requested in a binary or source artifact. * * @param existing - the existing decoded classpath * @return - the new classpath with the extra directories in it where source plugins will be * stored * @throws MalformedURLException */ protected URL[] addBinaryPluginWorkaround(URL[] existing) throws MalformedURLException { List<URL> classpath = new ArrayList<URL>(); classpath.add(new File(basedir, "target/plugin-build-classes").toURI().toURL()); classpath.add(new File(basedir, "target/plugin-provided-classes").toURI().toURL()); classpath.add(new File(basedir, "target/plugin-classes").toURI().toURL()); classpath.add(new File(basedir, "target/resources").toURI().toURL()); classpath.addAll(Arrays.asList(existing)); URL[] workaround = new URL[classpath.size()]; return classpath.toArray(workaround); }
/** * Common services for all Mojos using Grails. * * <p>This should be re-written in Groovy (static) * * @author <a href="mailto:[email protected]">Arnaud HERITIER</a> * @author Peter Ledbrook * @author Jonathan Pearlin * @version $Id$ */ public abstract class AbstractGrailsMojo extends AbstractMojo { public static final String PLUGIN_PREFIX = "grails-"; private static final String GRAILS_PLUGIN_NAME_FORMAT = "plugins.%s:%s"; private static final String GRAILS_PLUGIN_VERSION_PATTERN = "((def|String)\\s*version\\s*=\\s*(\"|'))(.*)(\"|')"; public static final String APP_GRAILS_VERSION = "app.grails.version"; public static final String APP_VERSION = "app.version"; /** The directory where is launched the mvn command. */ @Parameter(defaultValue = "${basedir}", required = true) protected File basedir; /** The Grails environment to use. */ @Parameter(property = "grails.env") protected String env; /** * Whether to run Grails in non-interactive mode or not. The default is to run interactively, just * like the Grails command-line. */ @Parameter(property = "nonInteractive", defaultValue = "true", required = true) protected boolean nonInteractive; /** The directory where plugins are stored. */ @Parameter(property = "pluginsDirectory", defaultValue = "${basedir}/plugins", required = true) protected File pluginsDir; /** The path to the Grails installation. */ @Parameter(property = "grailsHome") protected File grailsHome; /** The Maven settings reference. */ @Parameter(property = "settings", required = true, readonly = true) protected Settings settings; /** POM */ @Parameter(property = "project", readonly = true, required = true) protected MavenProject project; @Component private ArtifactResolver artifactResolver; /** */ @Component private ArtifactFactory artifactFactory; /** */ @Component private ArtifactMetadataSource artifactMetadataSource; @Parameter(property = "localRepository") private ArtifactRepository localRepository; /** The artifact collector to use. */ @Component private ArtifactCollector artifactCollector; /** The dependency tree builder to use. */ @Component private DependencyTreeBuilder dependencyTreeBuilder; /** */ @Parameter(property = "project.remoteArtifactRepositories", readonly = true, required = true) private List<ArtifactRepository> remoteRepositories; /** */ @Component private MavenProjectBuilder projectBuilder; /** */ @Component private GrailsServices grailsServices; /** */ @Parameter(defaultValue = "${user.home}/.grails/maven") private File centralPluginInstallDir; /** If this is passed, it will set the grails.server.factory property. */ @Parameter private String servletServerFactory; /** * We want this to come from the main pom so we can re-write the dependencies of the plugin to * match */ @Parameter(property = "grails.version") private String grailsVersion; @Parameter(property = "run.useTransitives") private boolean useTransitives = true; // by default use Maven 2.x to resolve transitives /** Whether we want the test dependencies to be used as run dependencies */ @Parameter(property = "run.includeTestDependencies") protected boolean runWithTestDependencies = false; /** Whether we want the execute the unit tests */ @Parameter(property = "run.unitTest") protected boolean runUnitTests = true; /** Whether we want the execute the unit tests */ @Parameter(property = "run.integrationTest") protected boolean runIntegrationTests = true; /** Whether we want the execute the unit tests */ @Parameter(property = "run.functionalTest") protected boolean runFunctionalTests = true; /** * When running using this plugin ONLY, what jars should be inserted into the front of the * classpath to ensure they get loaded first. */ @Parameter(property = "run.patchArtifacts") protected String patchArtifacts = null; protected List<String> artifactIdsToInsertAtStartOfClasspath = new ArrayList<String>(); protected void parsePatchArtifacts() { if (patchArtifacts != null) { StringTokenizer st = new StringTokenizer(patchArtifacts, ","); while (st.hasMoreTokens()) { artifactIdsToInsertAtStartOfClasspath.add(st.nextToken()); } } } /** * Returns the configured base directory for this execution of the plugin. * * @return The base directory. */ protected File getBasedir() { if (basedir == null) { throw new RuntimeException( "Your subclass have a field called 'basedir'. Remove it and use getBasedir() " + "instead."); } return this.basedir; } /** * Returns the {@code GrailsServices} instance used by the plugin with the base directory of the * services object set to the configured base directory. * * @return The underlying {@code GrailsServices} instance. */ protected GrailsServices getGrailsServices() { grailsServices.setBasedir(basedir); return grailsServices; } private String readFileAsString(File file) throws java.io.IOException { byte[] buffer = new byte[(int) file.length()]; BufferedInputStream bis = null; try { bis = new BufferedInputStream(new FileInputStream(file)); bis.read(buffer); } finally { if (bis != null) try { bis.close(); } catch (IOException ignored) { } } return new String(buffer); } protected String getFullGrailsPluginName() { String pluginName = getGrailsPluginFileName(); return pluginName != null ? this.getBasedir() + File.separator + pluginName : null; } protected String getGrailsPluginFileName() { String artifactId = project.getArtifactId(); String pluginName = GrailsNameUtils.getNameFromScript(project.getArtifactId()) + "GrailsPlugin.groovy"; String name = this.getBasedir() + File.separator + pluginName; if (new File(name).exists()) { return pluginName; } if (artifactId.startsWith("grails-")) { artifactId = artifactId.substring("grails-".length()); pluginName = GrailsNameUtils.getNameFromScript(artifactId) + "GrailsPlugin.groovy"; name = this.getBasedir() + File.separator + pluginName; if (new File(name).exists()) { return pluginName; } } return null; } protected void syncAppVersion() { final Metadata metadata = Metadata.getInstance(new File(getBasedir(), "application.properties")); if (syncVersion(metadata)) metadata.persist(); String artifactId = project.getArtifactId(); if (artifactId.startsWith("grails-")) artifactId = artifactId.substring("grails-".length()); final String fName = getFullGrailsPluginName(); if (fName != null) { File gpFile = new File(fName); String text = null; String mod = null; try { text = readFileAsString(gpFile); mod = text.replaceFirst(GRAILS_PLUGIN_VERSION_PATTERN, "$1" + project.getVersion() + "$5"); } catch (IOException e) { // ignore } if (text != null && !mod.equalsIgnoreCase(text)) { BufferedOutputStream out = null; try { out = new BufferedOutputStream(new FileOutputStream(gpFile)); out.write(mod.getBytes()); } catch (IOException e) { // do nuffink } finally { if (out != null) { try { out.close(); } catch (Exception ignored) { // ignored } } } } } } private boolean isWindows() { return System.getProperty("os.name").toLowerCase().contains("windows"); } // we are getting a situation where the same goal is running twice - two compiles, two test apps. // This is a hack fix until the real cause is discovered. private static String lastTargetName; private static String lastArgs; private static String lastArtifactId; private static String lastGroupId; private static Set<Artifact> resolvedArtifacts; private static Set<Artifact> pluginArtifacts; private static List<File> pluginDirectories; private static URL[] classpath; private static String grailsHomePath; 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 boolean alreadyLoaderClasspathForArtifact() { return project.getArtifactId().equalsIgnoreCase(lastArtifactId) && project.getGroupId().equalsIgnoreCase(lastGroupId); } // 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. * @throws MojoExecutionException if an error occurs while attempting to execute the target. */ protected void runGrails(final String targetName) throws MojoExecutionException { runGrails(targetName, null); } // void printClasspath(String name, List<URL> cp) { // StringBuilder sb = new StringBuilder(); // // for(URL c : cp) // sb.append(c.toExternalForm() + "\r\n"); // // getLog().info("name : " + sb.toString()); // } /** * 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 } /** * Gets around an issue where a binary plugin's resource refers to a source plugins's resource and * the binary plugin is subsequently requested in a binary or source artifact. * * @param existing - the existing decoded classpath * @return - the new classpath with the extra directories in it where source plugins will be * stored * @throws MalformedURLException */ protected URL[] addBinaryPluginWorkaround(URL[] existing) throws MalformedURLException { List<URL> classpath = new ArrayList<URL>(); classpath.add(new File(basedir, "target/plugin-build-classes").toURI().toURL()); classpath.add(new File(basedir, "target/plugin-provided-classes").toURI().toURL()); classpath.add(new File(basedir, "target/plugin-classes").toURI().toURL()); classpath.add(new File(basedir, "target/resources").toURI().toURL()); classpath.addAll(Arrays.asList(existing)); URL[] workaround = new URL[classpath.size()]; return classpath.toArray(workaround); } public static final String SETTINGS_START_MARKER = "---=== IDEA Grails build settings ===---"; public static final String SETTINGS_END_MARKER = "---=== End IDEA Grails build settings ===---"; private static Set GRAILS_PROPERTY_LIST = new HashSet( Arrays.asList( new String[] { "grails.work.dir", "grails.project.work.dir", "grails.project.target.dir", "grails.project.war.file", "grails.project.war.exploded.dir", "grails.project.class.dir", "grails.project.test.class.dir", "grails.project.resource.dir", "grails.project.source.dir", "grails.project.web.xml", "grails.project.plugins.dir", "grails.global.plugins.dir", "grails.project.test.reports.dir", "grails.project.test.source.dir" })); 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); } } private boolean syncVersion(Metadata metadata) { boolean changed = false; Object apGrailsVersion = metadata.get(APP_GRAILS_VERSION); Artifact grailsDependency = findGrailsDependency(project); if (grailsDependency != null) { if (!grailsDependency.getVersion().equals(apGrailsVersion)) { metadata.put(APP_GRAILS_VERSION, grailsDependency.getVersion()); changed = true; } } else if (grailsVersion != null && !grailsVersion.equals(apGrailsVersion)) { metadata.put(APP_GRAILS_VERSION, grailsVersion); changed = true; } if (!project.getVersion().equals(metadata.get(APP_VERSION))) { metadata.put(APP_VERSION, project.getVersion()); changed = true; } return changed; } private Artifact findGrailsDependency(MavenProject project) { Set dependencyArtifacts = project.getDependencyArtifacts(); for (Object o : dependencyArtifacts) { Artifact artifact = (Artifact) o; if (artifact.getArtifactId().equals("grails-dependencies")) { return artifact; } } return null; } 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); } } } } /** * Invokes the named method on a target object using reflection. The method signature is * determined by the classes of each argument. * * @param target The object to call the method on. * @param name The name of the method to call. * @param args The arguments to pass to the method (may be an empty array). * @return The value returned by the method. */ private Object invokeStaticMethod(Class target, String name, Object... args) { Class<?>[] argTypes = new Class[args.length]; for (int i = 0; i < args.length; i++) { argTypes[i] = args[i].getClass(); } try { return target.getMethod(name, argTypes).invoke(target, args); } catch (Exception ex) { throw new RuntimeException(ex); } } private String artifactToKey(Artifact artifact) { return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getClassifier(); } /* taken from the grails launcher as the plugins setup is broken */ private Object invokeMethod(Object target, String name, Class<?>[] argTypes, Object[] args) { try { return target.getClass().getMethod(name, argTypes).invoke(target, args); } catch (Exception ex) { throw new RuntimeException(ex); } } private Set<Artifact> collectAllProjectArtifacts() throws MojoExecutionException { final Set<Artifact> resolvedArtifacts = new HashSet<Artifact>(); /* * Get the Grails dependencies from the plugin's POM file first. */ final MavenProject pluginProject; try { pluginProject = getPluginProject(); } catch (ProjectBuildingException e) { throw new MojoExecutionException("Unable to get plugin project", e); } /* * Add the plugin's dependencies and the project using the plugin's dependencies to the list * of unresolved dependencies. This is done so they can all be resolved at the same time so * that we get the benefit of Maven's conflict resolution. */ Set<Artifact> uncheckedArtifacts = useTransitives ? resolveFromTree() : getResolvedArtifactsFromUnresolvedDependencies(project.getDependencies(), false); Map<String, Artifact> checklist = new HashMap<String, Artifact>(); for (Artifact artifact : uncheckedArtifacts) { // resolvedArtifacts.add(artifact); checklist.put(artifactToKey(artifact), artifact); // resolvedArtifacts.add(artifact); } // major breaking change, no dependencies from plugin /* for( Artifact artifact : getResolvedArtifactsFromUnresolvedDependencies(replaceVersion(filterGrailsDependencies(pluginProject.getDependencies())), true) ) { // resolvedArtifacts.add(artifact); String key = artifactToKey(artifact); Artifact existing = checklist.get(key); if (existing == null) checklist.put(key, artifact); } */ resolvedArtifacts.addAll(checklist.values()); return resolvedArtifacts; } private Set<Artifact> resolveFromTree() { final Set<Artifact> resolvedArtifacts = new HashSet<Artifact>(); try { // we have to do this because Aether does not work. dependencyTreeBuilder .buildDependencyTree( project, localRepository, artifactFactory, artifactMetadataSource, artifactCollector) .getRootNode() .accept( new DependencyNodeVisitor() { @Override public boolean visit(DependencyNode dependencyNode) { Artifact artifact = dependencyNode.getArtifact(); if (dependencyNode.getState() != DependencyNode.INCLUDED) return true; if (artifact.getArtifactId().equals(project.getArtifactId()) && artifact.getGroupId().equals(project.getGroupId())) return true; try { artifactResolver.resolve(artifact, remoteRepositories, localRepository); } catch (ArtifactResolutionException e) { throw new RuntimeException(e); } catch (ArtifactNotFoundException e) { throw new RuntimeException(e); } resolvedArtifacts.add(artifact); return true; } @Override public boolean endVisit(DependencyNode dependencyNode) { return true; } }); } catch (DependencyTreeBuilderException e) { throw new RuntimeException(e); } return resolvedArtifacts; } private List<Dependency> replaceVersion(List<Dependency> dependencies) { if (grailsVersion != null) { for (Dependency d : dependencies) { if ("org.grails".equals(d.getGroupId()) && !grailsVersion.equals(d.getVersion()) && grailsVersion.charAt(0) == d.getVersion().charAt(0)) { d.setVersion(grailsVersion); } } } return dependencies; } 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; } /** * 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 MavenProject getPluginProject() throws ProjectBuildingException { Artifact pluginArtifact = findArtifact( this.project.getPluginArtifacts(), "com.bluetrainsoftware.bluegrails", "grails-maven-plugin"); if (pluginArtifact == null) pluginArtifact = findArtifact(this.project.getPluginArtifacts(), "org.grails", "grails-dependencies"); return this.projectBuilder.buildFromRepository( pluginArtifact, this.remoteRepositories, this.localRepository); } /** * Returns only the dependencies matching the supplied group ID value, filtering out all others. * * @param dependencies A list of dependencies to be filtered. * @return The filtered list of dependencies. */ private List<Dependency> filterGrailsDependencies(final List<Dependency> dependencies) { final List<Dependency> filteredDependencies = new ArrayList<Dependency>(); for (final Dependency dependency : dependencies) { if (dependency.getArtifactId().equals("grails-scripts") || dependency.getArtifactId().equals("grails-bootstrap") || dependency.getArtifactId().equals("grails-launcher")) { filteredDependencies.add(dependency); } } return filteredDependencies; } Set<Artifact> getResolvedArtifactsFromUnresolvedDependencies( List<Dependency> unresolvedDependencies, boolean resolveTransitively) throws MojoExecutionException { final Set<Artifact> resolvedArtifacts = new HashSet<Artifact>(); // Artifact mojoArtifact = this.artifactFactory.createBuildArtifact(project.getGroupId(), // project.getArtifactId(), project.getVersion(), "pom"); /* * Resolve each artifact. This will get all transitive artifacts AND eliminate conflicts. */ final Set<Artifact> unresolvedArtifacts; // for (Dependency d : unresolvedDependencies) // System.out.println("dependency: " + d.toString()); try { unresolvedArtifacts = MavenMetadataSource.createArtifacts( this.artifactFactory, unresolvedDependencies, null, null, null); // for (Artifact artifact : unresolvedArtifacts) { // System.out.println("unresolved " + artifact.toString()); // } if (resolveTransitively) { ArtifactResolutionResult artifacts = artifactResolver.resolveTransitively( unresolvedArtifacts, project.getArtifact(), remoteRepositories, localRepository, artifactMetadataSource); resolvedArtifacts.addAll(artifacts.getArtifacts()); } else { // resolve each artifact individually for (Artifact artifact : unresolvedArtifacts) { artifactResolver.resolve(artifact, remoteRepositories, localRepository); resolvedArtifacts.add(artifact); } } } catch (Exception e) { throw new MojoExecutionException("Unable to complete configuring the build settings", e); } for (Artifact artifact : resolvedArtifacts) { System.out.println("matched " + artifact.toString()); } return resolvedArtifacts; } private boolean logDependencies = false; /** * 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; } private Set<Artifact> filterArtifacts(Set<Artifact> resolvedArtifacts, String... scopes) { HashSet<Artifact> artifacts = new HashSet<Artifact>(); List<String> checkScopes = Arrays.asList(scopes); for (Artifact artifact : resolvedArtifacts) { if (checkScopes.contains(artifact.getScope())) artifacts.add(artifact); } return artifacts; } private String getPluginName(Artifact plugin) { String pluginName = plugin.getArtifactId(); if (pluginName.startsWith(PLUGIN_PREFIX)) { return pluginName.substring(PLUGIN_PREFIX.length()); } else { return pluginName; } } 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; } private File getPluginTargetDirCentral(Artifact plugin) { return new File( this.centralPluginInstallDir, getPluginName(plugin) + "-" + plugin.getVersion()); } private File getPluginTargetDir(Artifact plugin) { File targetDir = getPluginTargetDirOverride(plugin); if (targetDir == null) { // The directory the plugin will be unzipped to. targetDir = getPluginTargetDirCentral(plugin); } return targetDir; } 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; } /** * Installs a Grails plugin into the current project if it isn't already installed. It works by * simply unpacking the plugin artifact (a ZIP file) into the appropriate location and adding the * plugin to the application's metadata. * * @param plugins The plugin artifact to install. * @param launcher The launcher instance that contains information about the various project * directories. In particular, this is where the method gets the location of the project's * "plugins" directory from. * @return <code>true</code> if the plugin is installed and the metadata updated, otherwise <code> * false</code>. * @throws IOException * @throws ArchiverException */ private boolean installGrailsPlugins( List<File> plugins, final DecentGrailsLauncher launcher, Field settingsField, Class clazz) throws MojoExecutionException { Object settings = null; try { settings = settingsField.get(launcher); Method m = clazz.getDeclaredMethod("addPluginDirectory", new Class[] {File.class, boolean.class}); for (File targetDir : plugins) m.invoke(settings, targetDir, true); } catch (Exception e) { throw new MojoExecutionException("Unable to install plugins", e); } return false; } /** * Converts a collection of Maven artifacts to files. For this method to function properly, the * artifacts MUST be resolved first. * * @param artifacts A collection of artifacts. * @return The list of files pointed to by the artifacts. */ private List<File> artifactsToFiles(final Collection<Artifact> artifacts) { final List<File> files = new ArrayList<File>(artifacts.size()); StringBuilder sb = new StringBuilder(); for (Artifact artifact : artifacts) { sb.append("\natof " + artifact.getFile().getAbsolutePath()); files.add(artifact.getFile()); } if (logDependencies) getLog().info(sb.toString()); return files; } /** * Finds the requested artifact in the supplied artifact collection. * * @param artifacts A collection of artifacts. * @param groupId The group ID of the artifact to be found. * @param artifactId The artifact ID of the artifact to be found. * @return The artifact from the collection that matches the group ID and artifact ID value or * {@code null} if no match is found. */ private Artifact findArtifact( final Collection<Artifact> artifacts, final String groupId, final String artifactId) { for (final Artifact artifact : artifacts) { if (artifact.getGroupId().equals(groupId) && artifact.getArtifactId().equals(artifactId)) { return artifact; } } return null; } /** * Converts a collection of Dependency objects to a list of corresponding Artifact objects. * * @param deps The collection of dependencies to convert. * @return A list of Artifact instances. */ private List<Artifact> dependenciesToArtifacts(final Collection<Dependency> deps) { final List<Artifact> artifacts = new ArrayList<Artifact>(deps.size()); for (Dependency dep : deps) { artifacts.add(dependencyToArtifact(dep)); } return artifacts; } /** * Uses the injected artifact factory to convert a single Dependency object into an Artifact * instance. * * @param dep The dependency to convert. * @return The resulting Artifact. */ private Artifact dependencyToArtifact(final Dependency dep) { // return this.artifactFactory.createBuildArtifact( // dep.getGroupId(), // dep.getArtifactId(), // dep.getVersion(), // "pom"); return this.artifactFactory.createDependencyArtifact( dep.getGroupId(), dep.getArtifactId(), VersionRange.createFromVersion(dep.getVersion()), dep.getType(), dep.getClassifier(), dep.getScope()); } /** * Removes any Grails plugin artifacts from the supplied list of dependencies. A Grails plugin is * any artifact whose type is equal to "grails-plugin" or "zip" * * @param artifact The list of artifacts to be cleansed. * @return list of plugins */ private Set<Artifact> removePluginArtifacts(final Set<Artifact> artifact) { final Set<Artifact> pluginArtifacts = new HashSet<Artifact>(); if (artifact != null) { for (final Iterator<Artifact> iter = artifact.iterator(); iter.hasNext(); ) { final Artifact dep = iter.next(); if (dep.getType() != null && (dep.getType().equals("grails-plugin") || dep.getType().equals("zip") || (dep.getType().equals("grails-plugin2") && "plugin".equals(dep.getClassifier())))) { pluginArtifacts.add(dep); // System.out.println("removing " + dep.toString()); iter.remove(); } } } return pluginArtifacts; } }