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