1 package org.codehaus.gmavenplus.mojo;
2
3 import org.apache.maven.artifact.DependencyResolutionRequiredException;
4 import org.apache.maven.plugin.MojoExecutionException;
5 import org.apache.maven.plugin.MojoFailureException;
6 import org.apache.maven.plugins.annotations.Mojo;
7 import org.apache.maven.plugins.annotations.Parameter;
8 import org.apache.maven.plugins.annotations.ResolutionScope;
9 import org.codehaus.gmavenplus.util.NoExitSecurityManager;
10
11 import java.io.File;
12 import java.lang.reflect.InvocationTargetException;
13 import java.lang.reflect.Method;
14 import java.net.MalformedURLException;
15 import java.util.Set;
16
17 import static org.codehaus.gmavenplus.mojo.ExecuteMojo.GROOVY_4_0_0_RC_1;
18 import static org.codehaus.gmavenplus.util.ReflectionUtils.findConstructor;
19 import static org.codehaus.gmavenplus.util.ReflectionUtils.findField;
20 import static org.codehaus.gmavenplus.util.ReflectionUtils.findMethod;
21 import static org.codehaus.gmavenplus.util.ReflectionUtils.getField;
22 import static org.codehaus.gmavenplus.util.ReflectionUtils.invokeConstructor;
23 import static org.codehaus.gmavenplus.util.ReflectionUtils.invokeMethod;
24
25
26
27
28
29
30
31
32
33
34
35
36 @Mojo(name = "console", requiresDependencyResolution = ResolutionScope.TEST)
37 public class ConsoleMojo extends AbstractToolsMojo {
38
39
40
41
42
43
44 @Parameter(property = "consoleScript")
45 protected String consoleScript;
46
47
48
49
50
51
52
53 @Override
54 public void execute() throws MojoExecutionException, MojoFailureException {
55 try {
56 setupClassWrangler(project.getTestClasspathElements(), includeClasspath);
57 } catch (MalformedURLException e) {
58 throw new MojoExecutionException("Unable to add project test dependencies to classpath.", e);
59 } catch (DependencyResolutionRequiredException e) {
60 throw new MojoExecutionException("Test dependencies weren't resolved.", e);
61 }
62
63 logPluginClasspath();
64 classWrangler.logGroovyVersion(mojoExecution.getMojoDescriptor().getGoal());
65
66 try {
67 getLog().debug("Project test classpath:\n" + project.getTestClasspathElements());
68 } catch (DependencyResolutionRequiredException e) {
69 getLog().debug("Unable to log project test classpath");
70 }
71
72 if (!groovyVersionSupportsAction()) {
73 getLog().error("Your Groovy version (" + classWrangler.getGroovyVersionString() + ") doesn't support running a console. The minimum version of Groovy required is " + minGroovyVersion + ". Skipping console startup.");
74 return;
75 }
76
77 final SecurityManager defaultSecurityManager = System.getSecurityManager();
78 try {
79 if (!allowSystemExits) {
80 getLog().warn("JEP 411 deprecated Security Manager in Java 17 for removal. Therefore `allowSystemExits` is also deprecated for removal.");
81 try {
82 System.setSecurityManager(new NoExitSecurityManager());
83 } catch (UnsupportedOperationException e) {
84 getLog().warn("Attempted to use Security Manager in a JVM where it's disabled by default. You might try `-Djava.security.manager=allow` to override this.");
85 }
86 }
87
88
89 Class<?> consoleClass;
90 try {
91 consoleClass = classWrangler.getClass("groovy.console.ui.Console");
92 } catch (ClassNotFoundException e) {
93 consoleClass = classWrangler.getClass("groovy.ui.Console");
94 }
95 Class<?> bindingClass = classWrangler.getClass("groovy.lang.Binding");
96
97
98 Object console = setupConsole(consoleClass, bindingClass);
99
100
101 invokeMethod(findMethod(consoleClass, "run"), console);
102
103
104 bindAntBuilder(consoleClass, bindingClass, console);
105
106
107 loadScript(consoleClass, console);
108
109
110 waitForConsoleClose();
111 } catch (ClassNotFoundException e) {
112 throw new MojoExecutionException("Unable to get a Groovy class from classpath (" + e.getMessage() + "). Do you have Groovy as a compile dependency in your project or the plugin?", e);
113 } catch (InvocationTargetException e) {
114 if (e.getCause() instanceof NoClassDefFoundError && "org/apache/ivy/core/report/ResolveReport".equals(e.getCause().getMessage())) {
115 throw new MojoExecutionException("Groovy 1.7.6 and 1.7.7 have a dependency on Ivy to run the console. Either change your Groovy version or add Ivy as a project or plugin dependency.", e);
116 } else {
117 throw new MojoExecutionException("Error occurred while calling a method on a Groovy class from classpath.", e);
118 }
119 } catch (IllegalAccessException e) {
120 throw new MojoExecutionException("Unable to access a method on a Groovy class from classpath.", e);
121 } catch (InstantiationException e) {
122 throw new MojoExecutionException("Error occurred while instantiating a Groovy class from classpath.", e);
123 } finally {
124 if (!allowSystemExits) {
125 try {
126 System.setSecurityManager(defaultSecurityManager);
127 } catch (UnsupportedOperationException e) {
128 getLog().warn("Attempted to use Security Manager in a JVM where it's disabled by default. You might try `-Djava.security.manager=allow` to override this.");
129 }
130 }
131 }
132 }
133
134 protected void loadScript(Class<?> consoleClass, Object console) throws InvocationTargetException, IllegalAccessException {
135 if (consoleScript != null) {
136 Method loadScriptFile = findMethod(consoleClass, "loadScriptFile", File.class);
137 File consoleScriptFile = new File(consoleScript);
138 if (consoleScriptFile.isFile()) {
139 invokeMethod(loadScriptFile, console, consoleScriptFile);
140 } else if (project.getProperties().containsKey(consoleScript)) {
141 consoleScriptFile = new File(project.getProperties().getProperty(consoleScript));
142 if (consoleScriptFile.isFile()) {
143 invokeMethod(loadScriptFile, console, consoleScriptFile);
144 } else {
145 getLog().warn("consoleScript ('" + consoleScript + "') doesn't exist in project properties or as a file.");
146 }
147 } else {
148 getLog().warn("consoleScript ('" + consoleScript + "') doesn't exist in project properties or as a file.");
149 }
150 }
151 }
152
153
154
155
156
157
158
159
160
161
162
163 protected Object setupConsole(final Class<?> consoleClass, final Class<?> bindingClass) throws InvocationTargetException, IllegalAccessException, InstantiationException {
164 Object binding = invokeConstructor(findConstructor(bindingClass));
165 initializeProperties();
166 Method setVariable = findMethod(bindingClass, "setVariable", String.class, Object.class);
167 if (bindPropertiesToSeparateVariables) {
168 for (Object k : properties.keySet()) {
169 invokeMethod(setVariable, binding, k, properties.get(k));
170 }
171 } else {
172 if (groovyOlderThan(GROOVY_4_0_0_RC_1)) {
173 invokeMethod(setVariable, binding, "properties", properties);
174 } else {
175 throw new IllegalArgumentException("properties is a read-only property in Groovy " + GROOVY_4_0_0_RC_1 + " and later.");
176 }
177 }
178
179 return invokeConstructor(findConstructor(consoleClass, ClassLoader.class, bindingClass), classWrangler.getClassLoader(), binding);
180 }
181
182
183
184
185
186
187
188
189
190
191
192 protected void bindAntBuilder(Class<?> consoleClass, Class<?> bindingClass, Object console) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException {
193 if (properties.containsKey("ant")) {
194 Class<?> groovyShellClass = classWrangler.getClass("groovy.lang.GroovyShell");
195 Object shell = getField(findField(consoleClass, "shell", groovyShellClass), console);
196 Object binding = invokeMethod(findMethod(groovyShellClass, "getContext"), shell);
197 Object antBuilder = null;
198 try {
199 antBuilder = invokeConstructor(findConstructor(classWrangler.getClass("groovy.ant.AntBuilder")));
200 } catch (ClassNotFoundException e1) {
201 getLog().debug("groovy.ant.AntBuilder not available, trying groovy.util.AntBuilder.");
202 try {
203 antBuilder = invokeConstructor(findConstructor(classWrangler.getClass("groovy.util.AntBuilder")));
204 } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | InstantiationException e2) {
205 logUnableToInitializeAntBuilder(e2);
206 }
207 } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
208 logUnableToInitializeAntBuilder(e);
209 }
210 if (antBuilder != null) {
211 if (bindPropertiesToSeparateVariables) {
212 invokeMethod(findMethod(bindingClass, "setVariable", String.class, Object.class), binding, "ant", antBuilder);
213 } else {
214 properties.put("ant", antBuilder);
215 }
216 }
217 }
218 }
219
220
221
222
223
224
225 protected void waitForConsoleClose() throws MojoFailureException {
226 Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
227 Thread[] threadArray = threadSet.toArray(new Thread[0]);
228 Thread consoleThread = null;
229 for (Thread thread : threadArray) {
230 if ("AWT-Shutdown".equals(thread.getName())) {
231 consoleThread = thread;
232 break;
233 }
234 }
235 if (consoleThread != null) {
236 try {
237 consoleThread.join();
238 } catch (InterruptedException e) {
239 throw new MojoFailureException("Mojo interrupted while waiting for Console thread to end.", e);
240 }
241 } else {
242 throw new MojoFailureException("Unable to locate Console thread to wait on.");
243 }
244 }
245
246 }