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