View Javadoc
1   /*
2    * Copyright (C) 2011 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.codehaus.gmavenplus.mojo;
18  
19  import org.apache.maven.artifact.Artifact;
20  import org.apache.maven.execution.MavenSession;
21  import org.apache.maven.plugin.AbstractMojo;
22  import org.apache.maven.plugin.MojoExecution;
23  import org.apache.maven.plugins.annotations.Parameter;
24  import org.apache.maven.project.MavenProject;
25  import org.apache.maven.plugin.descriptor.PluginDescriptor;
26  import org.codehaus.gmavenplus.model.IncludeClasspath;
27  import org.codehaus.gmavenplus.model.internal.Version;
28  import org.codehaus.gmavenplus.util.ClassWrangler;
29  
30  import java.io.File;
31  import java.net.MalformedURLException;
32  import java.util.List;
33  
34  import static java.util.Collections.emptyList;
35  
36  
37  /**
38   * The base mojo class, which all other mojos extend.
39   *
40   * @author Keegan Witt
41   */
42  public abstract class AbstractGroovyMojo extends AbstractMojo {
43  
44      /**
45       * The pattern defining Groovy files.
46       */
47      protected static final String GROOVY_SOURCES_PATTERN = "**" + File.separator + "*.groovy";
48  
49      /**
50       * The pattern defining Java stub files.
51       */
52      protected static final String JAVA_SOURCES_PATTERN = "**" + File.separator + "*.java";
53  
54      /**
55       * Java 1.7 version.
56       */
57      protected static final Version JAVA_1_7 = new Version(1, 7);
58  
59      /**
60       * Java 1.8 version.
61       */
62      protected static final Version JAVA_1_8 = new Version(1, 8);
63  
64      /**
65       * Java 1.8 version.
66       */
67      protected static final Version JAVA_12 = new Version(12);
68  
69      /**
70       * Groovy 1.5.0 version.
71       */
72      protected static final Version GROOVY_1_5_0 = new Version(1, 5, 0);
73  
74      /**
75       * The wrangler to use to work with Groovy classes, classpaths, classLoaders, and versions.
76       */
77      protected ClassWrangler classWrangler;
78  
79      // note that all supported parameter expressions can be found here: https://git-wip-us.apache.org/repos/asf?p=maven.git;a=blob;f=maven-core/src/main/java/org/apache/maven/plugin/PluginParameterExpressionEvaluator.java;hb=HEAD
80  
81      /**
82       * The Maven project this plugin is being used on.
83       */
84      @Parameter(property = "project", required = true, readonly = true)
85      protected MavenProject project;
86  
87      /**
88       * The Maven Session this plugin is being used on.
89       */
90      @Parameter(property = "session", required = true, readonly = true)
91      protected MavenSession session;
92  
93      /**
94       * The plugin dependencies.
95       */
96      @Parameter(property = "plugin.artifacts", required = true, readonly = true)
97      protected List<Artifact> pluginArtifacts;
98  
99      /**
100      * The plugin's mojo execution.
101      */
102     @Parameter(property = "mojoExecution", required = true, readonly = true)
103     protected MojoExecution mojoExecution;
104 
105     /**
106      * The plugin descriptor.
107      */
108     @Parameter(defaultValue = "${plugin}", readonly = true)
109     protected PluginDescriptor pluginDescriptor;
110 
111     /**
112      * The minimum version of Groovy that this mojo supports (1.5.0 by
113      * default, but other mojos can override).
114      */
115     protected Version minGroovyVersion = GROOVY_1_5_0;
116 
117     /**
118      * Logs the plugin classpath.
119      */
120     protected void logPluginClasspath() {
121         if (getLog().isDebugEnabled()) {
122             StringBuilder sb = new StringBuilder();
123             for (int i = 0; i < pluginArtifacts.size(); i++) {
124                 sb.append(pluginArtifacts.get(i).getFile());
125                 if (i < pluginArtifacts.size() - 1) {
126                     sb.append(", ");
127                 }
128             }
129             getLog().debug("Plugin classpath:\n" + sb);
130         }
131     }
132 
133     /**
134      * Determines whether the version of Java executing this mojo supports invokedynamic (is at least 1.7).
135      *
136      * @return <code>true</code> if the running Java supports invokedynamic, <code>false</code> otherwise
137      */
138     protected boolean isJavaSupportIndy() {
139         return getJavaVersion().compareTo(JAVA_1_7, false) >= 0;
140     }
141 
142     /**
143      * Determines whether the version of Java executing this mojo supports preview features (is at least 12).
144      *
145      * @return <code>true</code> if the running Java supports preview features, <code>false</code> otherwise
146      */
147     protected boolean isJavaSupportPreviewFeatures() {
148         return getJavaVersion().compareTo(JAVA_12, false) >= 0;
149     }
150 
151     /**
152      * Determines whether the version of Java executing this mojo supports JEP 118 (is at least 1.8).
153      *
154      * @return <code>true</code> if the running Java supports parameters, <code>false</code> otherwise
155      */
156     protected boolean isJavaSupportParameters() {
157         return getJavaVersion().compareTo(JAVA_1_8, false) >= 0;
158     }
159 
160     /**
161      * Gets the version of Java executing this mojo as a Version object.
162      *
163      * @return a Version object of the running Java version
164      */
165     protected Version getJavaVersion() {
166         return Version.parseFromString(getJavaVersionString());
167     }
168 
169     /**
170      * Gets the version of Java executing this mojo as a String.
171      *
172      * @return a String of the running Java version
173      */
174     protected String getJavaVersionString() {
175         return System.getProperty("java.version");
176     }
177 
178     /**
179      * Determines whether this mojo can be run with the version of Groovy supplied.
180      *
181      * @return <code>true</code> only if the version of Groovy supports this mojo
182      */
183     protected boolean groovyVersionSupportsAction() {
184         return classWrangler.getGroovyVersion() != null && groovyAtLeast(minGroovyVersion);
185     }
186 
187     /**
188      * Determines whether the detected Groovy version is the specified version or newer.
189      *
190      * @param version the version to compare the detected Groovy version to
191      * @return <code>true</code> if the detected Groovy version is the specified version or newer, <code>false</code> otherwise
192      */
193     protected boolean groovyAtLeast(Version version) {
194         return ClassWrangler.groovyAtLeast(classWrangler.getGroovyVersion(), version);
195     }
196 
197     /**
198      * Determines whether the detected Groovy version is the specified version.
199      *
200      * @param version the version to compare the detected Groovy version to
201      * @return <code>true</code> if the detected Groovy version is the specified version, <code>false</code> otherwise
202      */
203     protected boolean groovyIs(Version version) {
204         return ClassWrangler.groovyIs(classWrangler.getGroovyVersion(), version);
205     }
206 
207     /**
208      * Determines whether the detected Groovy version is newer than the specified version.
209      *
210      * @param version the version to compare the detected Groovy version to
211      * @return <code>true</code> if the detected Groovy version is newer than the specified version, <code>false</code> otherwise
212      */
213     protected boolean groovyNewerThan(Version version) {
214         return ClassWrangler.groovyNewerThan(classWrangler.getGroovyVersion(), version);
215     }
216 
217     /**
218      * Determines whether the detected Groovy version is older than the specified version.
219      *
220      * @param version the version to compare the detected Groovy version to
221      * @return <code>true</code> if the detected Groovy version is older than the specified version, <code>false</code> otherwise
222      */
223     protected boolean groovyOlderThan(Version version) {
224         return ClassWrangler.groovyOlderThan(classWrangler.getGroovyVersion(), version);
225     }
226 
227     /**
228      * Gets whether the version of Groovy on the classpath supports invokedynamic.
229      *
230      * @return <code>true</code> if the version of Groovy uses invokedynamic,
231      * <code>false</code> if not or Groovy dependency cannot be found
232      */
233     protected boolean isGroovyIndy() {
234         return classWrangler.isGroovyIndy();
235     }
236 
237     /**
238      * Instantiate a ClassWrangler.
239      *
240      * @param classpath        the classpath to load onto a new classloader (if includeClasspath is <code>PROJECT_ONLY</code>)
241      * @param includeClasspath whether to use a shared classloader that includes both the project classpath and plugin classpath.
242      * @throws MalformedURLException when a classpath element provides a malformed URL
243      */
244     protected void setupClassWrangler(List<?> classpath, IncludeClasspath includeClasspath) throws MalformedURLException {
245         if (IncludeClasspath.PROJECT_ONLY.equals(includeClasspath)) {
246             getLog().info("Using isolated classloader, without GMavenPlus classpath.");
247             classWrangler = new ClassWrangler(classpath, ClassLoader.getSystemClassLoader(), getLog());
248         } else if (IncludeClasspath.PROJECT_AND_PLUGIN.equals(includeClasspath)) {
249             getLog().info("Using plugin classloader, includes GMavenPlus and project classpath.");
250             classWrangler = new ClassWrangler(classpath, getClass().getClassLoader(), getLog());
251         } else {
252             getLog().info("Using plugin classloader, includes GMavenPlus classpath, but not project classpath.");
253             classWrangler = new ClassWrangler(emptyList(), getClass().getClassLoader(), getLog());
254         }
255     }
256 
257     /**
258      * Gets the Java executable to use for forked execution.
259      *
260      * @return the Java executable path
261      */
262     protected String getJavaExecutable() {
263         // Try to get operation system process via JDK 9+ ProcessHandle
264         try {
265             Class<?> processHandleClass = Class.forName("java.lang.ProcessHandle");
266             // ProcessHandle.current()
267             java.lang.reflect.Method currentMethod = processHandleClass.getMethod("current");
268             Object currentProcess = currentMethod.invoke(null);
269 
270             // ProcessHandle.info()
271             java.lang.reflect.Method infoMethod = processHandleClass.getMethod("info");
272             Object info = infoMethod.invoke(currentProcess);
273 
274             // ProcessHandle.Info.command()
275             Class<?> infoClass = Class.forName("java.lang.ProcessHandle$Info");
276             java.lang.reflect.Method commandMethod = infoClass.getMethod("command");
277             @SuppressWarnings("unchecked")
278             java.util.Optional<String> commandConfig = (java.util.Optional<String>) commandMethod.invoke(info);
279 
280             if (commandConfig.isPresent()) {
281                 return commandConfig.get();
282             }
283         } catch (Exception e) {
284             // ignore, we are probably on Java 8 or the OS doesn't support this
285         }
286 
287         String javaHome = System.getProperty("java.home");
288         String javaExecutable = javaHome + File.separator + "bin" + File.separator + "java";
289         if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
290             javaExecutable += ".exe";
291         }
292         return javaExecutable;
293     }
294 }