Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ExecJavaMojo |
|
| 4.411764705882353;4.412 | ||||
ExecJavaMojo$1 |
|
| 4.411764705882353;4.412 | ||||
ExecJavaMojo$IsolatedThreadGroup |
|
| 4.411764705882353;4.412 |
1 | package org.codehaus.mojo.exec; | |
2 | ||
3 | /* | |
4 | * Licensed to the Apache Software Foundation (ASF) under one | |
5 | * or more contributor license agreements. See the NOTICE file | |
6 | * distributed with this work for additional information | |
7 | * regarding copyright ownership. The ASF licenses this file | |
8 | * to you under the Apache License, Version 2.0 (the | |
9 | * "License"); you may not use this file except in compliance | |
10 | * with the License. You may obtain a copy of the License at | |
11 | * | |
12 | * http://www.apache.org/licenses/LICENSE-2.0 | |
13 | * | |
14 | * Unless required by applicable law or agreed to in writing, | |
15 | * software distributed under the License is distributed on an | |
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
17 | * KIND, either express or implied. See the License for the | |
18 | * specific language governing permissions and limitations | |
19 | * under the License. | |
20 | */ | |
21 | ||
22 | import org.apache.maven.artifact.Artifact; | |
23 | import org.apache.maven.artifact.factory.ArtifactFactory; | |
24 | import org.apache.maven.artifact.metadata.ArtifactMetadataSource; | |
25 | import org.apache.maven.artifact.repository.ArtifactRepository; | |
26 | import org.apache.maven.artifact.resolver.ArtifactResolutionResult; | |
27 | import org.apache.maven.artifact.resolver.ArtifactResolver; | |
28 | import org.apache.maven.plugin.MojoExecutionException; | |
29 | import org.apache.maven.plugin.MojoFailureException; | |
30 | import org.apache.maven.project.MavenProject; | |
31 | import org.apache.maven.project.MavenProjectBuilder; | |
32 | import org.apache.maven.project.artifact.MavenMetadataSource; | |
33 | ||
34 | import java.io.File; | |
35 | import java.lang.reflect.Method; | |
36 | import java.net.MalformedURLException; | |
37 | import java.net.URL; | |
38 | import java.net.URLClassLoader; | |
39 | import java.util.ArrayList; | |
40 | import java.util.Collection; | |
41 | import java.util.Collections; | |
42 | import java.util.HashSet; | |
43 | import java.util.Iterator; | |
44 | import java.util.List; | |
45 | import java.util.Properties; | |
46 | import java.util.Set; | |
47 | ||
48 | /** | |
49 | * Executes the supplied java class in the current VM with the enclosing project's | |
50 | * dependencies as classpath. | |
51 | * | |
52 | * @author <a href="mailto:kaare.nilsen@gmail.com">Kaare Nilsen</a>, <a href="mailto:dsmiley@mitre.org">David Smiley</a> | |
53 | * @goal java | |
54 | * @requiresDependencyResolution test | |
55 | * @execute phase="validate" | |
56 | * @since 1.0 | |
57 | */ | |
58 | 0 | public class ExecJavaMojo |
59 | extends AbstractExecMojo | |
60 | { | |
61 | /** | |
62 | * @component | |
63 | */ | |
64 | private ArtifactResolver artifactResolver; | |
65 | ||
66 | /** | |
67 | * @component | |
68 | */ | |
69 | private ArtifactFactory artifactFactory; | |
70 | ||
71 | /** | |
72 | * @component | |
73 | */ | |
74 | private ArtifactMetadataSource metadataSource; | |
75 | ||
76 | /** | |
77 | * @parameter expression="${localRepository}" | |
78 | * @required | |
79 | * @readonly | |
80 | * @since 1.0 | |
81 | */ | |
82 | private ArtifactRepository localRepository; | |
83 | ||
84 | /** | |
85 | * @parameter expression="${project.remoteArtifactRepositories}" | |
86 | * @required | |
87 | * @readonly | |
88 | * @since 1.1-beta-1 | |
89 | */ | |
90 | private List remoteRepositories; | |
91 | ||
92 | /** | |
93 | * @component | |
94 | * @since 1.0 | |
95 | */ | |
96 | private MavenProjectBuilder projectBuilder; | |
97 | ||
98 | /** | |
99 | * @parameter expression="${plugin.artifacts}" | |
100 | * @readonly | |
101 | * @since 1.1-beta-1 | |
102 | */ | |
103 | private List pluginDependencies; | |
104 | ||
105 | /** | |
106 | * The main class to execute. | |
107 | * | |
108 | * @parameter expression="${exec.mainClass}" | |
109 | * @required | |
110 | * @since 1.0 | |
111 | */ | |
112 | private String mainClass; | |
113 | ||
114 | /** | |
115 | * The class arguments. | |
116 | * | |
117 | * @parameter expression="${exec.arguments}" | |
118 | * @since 1.0 | |
119 | */ | |
120 | private String[] arguments; | |
121 | ||
122 | /** | |
123 | * A list of system properties to be passed. Note: as the execution is not forked, some system properties | |
124 | * required by the JVM cannot be passed here. Use MAVEN_OPTS or the exec:exec instead. See the user guide for | |
125 | * more information. | |
126 | * | |
127 | * @parameter | |
128 | * @since 1.0 | |
129 | */ | |
130 | private Property[] systemProperties; | |
131 | ||
132 | /** | |
133 | * Indicates if mojo should be kept running after the mainclass terminates. | |
134 | * Usefull for serverlike apps with deamonthreads. | |
135 | * | |
136 | * @parameter expression="${exec.keepAlive}" default-value="false" | |
137 | * @deprecated since 1.1-alpha-1 | |
138 | * @since 1.0 | |
139 | */ | |
140 | private boolean keepAlive; | |
141 | ||
142 | /** | |
143 | * Indicates if the project dependencies should be used when executing | |
144 | * the main class. | |
145 | * | |
146 | * @parameter expression="${exec.includeProjectDependencies}" default-value="true" | |
147 | * @since 1.1-beta-1 | |
148 | */ | |
149 | private boolean includeProjectDependencies; | |
150 | ||
151 | /** | |
152 | * Indicates if this plugin's dependencies should be used when executing | |
153 | * the main class. | |
154 | * <p/> | |
155 | * This is useful when project dependencies are not appropriate. Using only | |
156 | * the plugin dependencies can be particularly useful when the project is | |
157 | * not a java project. For example a mvn project using the csharp plugins | |
158 | * only expects to see dotnet libraries as dependencies. | |
159 | * | |
160 | * @parameter expression="${exec.includePluginDependencies}" default-value="false" | |
161 | * @since 1.1-beta-1 | |
162 | */ | |
163 | private boolean includePluginDependencies; | |
164 | ||
165 | /** | |
166 | * If provided the ExecutableDependency identifies which of the plugin dependencies | |
167 | * contains the executable class. This will have the affect of only including | |
168 | * plugin dependencies required by the identified ExecutableDependency. | |
169 | * <p/> | |
170 | * If includeProjectDependencies is set to <code>true</code>, all of the project dependencies | |
171 | * will be included on the executable's classpath. Whether a particular project | |
172 | * dependency is a dependency of the identified ExecutableDependency will be | |
173 | * irrelevant to its inclusion in the classpath. | |
174 | * | |
175 | * @parameter | |
176 | * @optional | |
177 | * @since 1.1-beta-1 | |
178 | */ | |
179 | private ExecutableDependency executableDependency; | |
180 | ||
181 | /** | |
182 | * Wether to interrupt/join and possibly stop the daemon threads upon quitting. <br/> If this is <code>false</code>, | |
183 | * maven does nothing about the daemon threads. When maven has no more work to do, the VM will normally terminate | |
184 | * any remaining daemon threads. | |
185 | * <p> | |
186 | * In certain cases (in particular if maven is embedded), | |
187 | * you might need to keep this enabled to make sure threads are properly cleaned up to ensure they don't interfere | |
188 | * with subsequent activity. | |
189 | * In that case, see {@link #daemonThreadJoinTimeout} and | |
190 | * {@link #stopUnresponsiveDaemonThreads} for further tuning. | |
191 | * </p> | |
192 | * @parameter expression="${exec.cleanupDaemonThreads} default-value="true" | |
193 | * @since 1.1-beta-1 | |
194 | */ | |
195 | private boolean cleanupDaemonThreads; | |
196 | ||
197 | /** | |
198 | * This defines the number of milliseconds to wait for daemon threads to quit following their interruption.<br/> | |
199 | * This is only taken into account if {@link #cleanupDaemonThreads} is <code>true</code>. | |
200 | * A value <=0 means to not timeout (i.e. wait indefinitely for threads to finish). Following a timeout, a | |
201 | * warning will be logged. | |
202 | * <p>Note: properly coded threads <i>should</i> terminate upon interruption but some threads may prove | |
203 | * problematic: as the VM does interrupt daemon threads, some code may not have been written to handle | |
204 | * interruption properly. For example java.util.Timer is known to not handle interruptions in JDK <= 1.6. | |
205 | * So it is not possible for us to infinitely wait by default otherwise maven could hang. A sensible default | |
206 | * value has been chosen, but this default value <i>may change</i> in the future based on user feedback.</p> | |
207 | * @parameter expression="${exec.daemonThreadJoinTimeout}" default-value="15000" | |
208 | * @since 1.1-beta-1 | |
209 | */ | |
210 | private long daemonThreadJoinTimeout; | |
211 | ||
212 | /** | |
213 | * Wether to call {@link Thread#stop()} following a timing out of waiting for an interrupted thread to finish. | |
214 | * This is only taken into account if {@link #cleanupDaemonThreads} is <code>true</code> | |
215 | * and the {@link #daemonThreadJoinTimeout} threshold has been reached for an uncooperative thread. | |
216 | * If this is <code>false</code>, or if {@link Thread#stop()} fails to get the thread to stop, then | |
217 | * a warning is logged and Maven will continue on while the affected threads (and related objects in memory) | |
218 | * linger on. Consider setting this to <code>true</code> if you are invoking problematic code that you can't fix. | |
219 | * An example is {@link java.util.Timer} which doesn't respond to interruption. To have <code>Timer</code> | |
220 | * fixed, vote for <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6336543">this bug</a>. | |
221 | * @parameter expression="${exec.stopUnresponsiveDaemonThreads} default-value="false" | |
222 | * @since 1.1-beta-1 | |
223 | */ | |
224 | private boolean stopUnresponsiveDaemonThreads; | |
225 | ||
226 | /** | |
227 | * Deprecated this is not needed anymore. | |
228 | * | |
229 | * @parameter expression="${exec.killAfter}" default-value="-1" | |
230 | * @deprecated since 1.1-alpha-1 | |
231 | * @since 1.0 | |
232 | */ | |
233 | private long killAfter; | |
234 | ||
235 | private Properties originalSystemProperties; | |
236 | ||
237 | /** | |
238 | * Execute goal. | |
239 | * @throws MojoExecutionException execution of the main class or one of the threads it generated failed. | |
240 | * @throws MojoFailureException something bad happened... | |
241 | */ | |
242 | public void execute() | |
243 | throws MojoExecutionException, MojoFailureException | |
244 | { | |
245 | 0 | if ( isSkip() ) |
246 | { | |
247 | 0 | getLog().info( "skipping execute as per configuraion" ); |
248 | 0 | return; |
249 | } | |
250 | 0 | if ( killAfter != -1 ) |
251 | { | |
252 | 0 | getLog().warn( "Warning: killAfter is now deprecated. Do you need it ? Please comment on MEXEC-6." ); |
253 | } | |
254 | ||
255 | 0 | if ( null == arguments ) |
256 | { | |
257 | 0 | arguments = new String[0]; |
258 | } | |
259 | ||
260 | 0 | if ( getLog().isDebugEnabled() ) |
261 | { | |
262 | 0 | StringBuffer msg = new StringBuffer( "Invoking : " ); |
263 | 0 | msg.append( mainClass ); |
264 | 0 | msg.append( ".main(" ); |
265 | 0 | for ( int i = 0; i < arguments.length; i++ ) |
266 | { | |
267 | 0 | if ( i > 0 ) |
268 | { | |
269 | 0 | msg.append( ", " ); |
270 | } | |
271 | 0 | msg.append( arguments[i] ); |
272 | } | |
273 | 0 | msg.append( ")" ); |
274 | 0 | getLog().debug( msg ); |
275 | } | |
276 | ||
277 | 0 | IsolatedThreadGroup threadGroup = new IsolatedThreadGroup( mainClass /*name*/ ); |
278 | 0 | Thread bootstrapThread = new Thread( threadGroup, new Runnable() |
279 | 0 | { |
280 | public void run() | |
281 | { | |
282 | try | |
283 | { | |
284 | 0 | Method main = Thread.currentThread().getContextClassLoader().loadClass( mainClass ) |
285 | .getMethod( "main", new Class[]{ String[].class } ); | |
286 | 0 | if ( ! main.isAccessible() ) |
287 | { | |
288 | 0 | getLog().debug( "Setting accessibility to true in order to invoke main()." ); |
289 | 0 | main.setAccessible( true ); |
290 | } | |
291 | 0 | main.invoke( main, new Object[]{arguments} ); |
292 | } | |
293 | 0 | catch ( NoSuchMethodException e ) |
294 | { // just pass it on | |
295 | 0 | Thread.currentThread().getThreadGroup().uncaughtException( Thread.currentThread(), |
296 | new Exception( | |
297 | "The specified mainClass doesn't contain a main method with appropriate signature.", e | |
298 | ) | |
299 | ); | |
300 | } | |
301 | 0 | catch ( Exception e ) |
302 | { // just pass it on | |
303 | 0 | Thread.currentThread().getThreadGroup().uncaughtException( Thread.currentThread(), e ); |
304 | 0 | } |
305 | 0 | } |
306 | }, mainClass + ".main()" ); | |
307 | 0 | bootstrapThread.setContextClassLoader( getClassLoader() ); |
308 | 0 | setSystemProperties(); |
309 | ||
310 | 0 | bootstrapThread.start(); |
311 | 0 | joinNonDaemonThreads( threadGroup ); |
312 | // It's plausible that spontaneously a non-daemon thread might be created as we try and shut down, | |
313 | // but it's too late since the termination condition (only daemon threads) has been triggered. | |
314 | 0 | if ( keepAlive ) |
315 | { | |
316 | 0 | getLog().warn( |
317 | "Warning: keepAlive is now deprecated and obsolete. Do you need it? Please comment on MEXEC-6." ); | |
318 | 0 | waitFor( 0 ); |
319 | } | |
320 | ||
321 | 0 | if ( cleanupDaemonThreads ) |
322 | { | |
323 | ||
324 | 0 | terminateThreads( threadGroup ); |
325 | ||
326 | try | |
327 | { | |
328 | 0 | threadGroup.destroy(); |
329 | } | |
330 | 0 | catch ( IllegalThreadStateException e ) |
331 | { | |
332 | 0 | getLog().warn( "Couldn't destroy threadgroup " + threadGroup, e ); |
333 | 0 | } |
334 | } | |
335 | ||
336 | ||
337 | 0 | if ( originalSystemProperties != null ) |
338 | { | |
339 | 0 | System.setProperties( originalSystemProperties ); |
340 | } | |
341 | ||
342 | 0 | synchronized ( threadGroup ) |
343 | { | |
344 | 0 | if ( threadGroup.uncaughtException != null ) |
345 | { | |
346 | 0 | throw new MojoExecutionException( "An exception occured while executing the Java class. " |
347 | + threadGroup.uncaughtException.getMessage(), | |
348 | threadGroup.uncaughtException ); | |
349 | } | |
350 | 0 | } |
351 | ||
352 | 0 | registerSourceRoots(); |
353 | 0 | } |
354 | ||
355 | /** | |
356 | * a ThreadGroup to isolate execution and collect exceptions. | |
357 | */ | |
358 | 0 | class IsolatedThreadGroup extends ThreadGroup |
359 | { | |
360 | private Throwable uncaughtException; // synchronize access to this | |
361 | ||
362 | public IsolatedThreadGroup( String name ) | |
363 | 0 | { |
364 | 0 | super( name ); |
365 | 0 | } |
366 | ||
367 | public void uncaughtException( Thread thread, Throwable throwable ) | |
368 | { | |
369 | 0 | if ( throwable instanceof ThreadDeath ) |
370 | { | |
371 | 0 | return; //harmless |
372 | } | |
373 | 0 | boolean doLog = false; |
374 | 0 | synchronized ( this ) |
375 | { | |
376 | 0 | if ( uncaughtException == null ) // only remember the first one |
377 | { | |
378 | 0 | uncaughtException = throwable; // will be reported eventually |
379 | } | |
380 | else | |
381 | { | |
382 | 0 | doLog = true; |
383 | } | |
384 | 0 | } |
385 | 0 | if ( doLog ) |
386 | { | |
387 | 0 | getLog().warn( "an additional exception was thrown", throwable ); |
388 | } | |
389 | 0 | } |
390 | } | |
391 | ||
392 | private void joinNonDaemonThreads( ThreadGroup threadGroup ) | |
393 | { | |
394 | boolean foundNonDaemon; | |
395 | do | |
396 | { | |
397 | 0 | foundNonDaemon = false; |
398 | 0 | Collection threads = getActiveThreads( threadGroup ); |
399 | 0 | for ( Iterator iter = threads.iterator(); iter.hasNext(); ) |
400 | { | |
401 | 0 | Thread thread = (Thread) iter.next(); |
402 | 0 | if ( thread.isDaemon() ) |
403 | { | |
404 | 0 | continue; |
405 | } | |
406 | 0 | foundNonDaemon = true; //try again; maybe more threads were created while we were busy |
407 | 0 | joinThread( thread, 0 ); |
408 | 0 | } |
409 | 0 | } while ( foundNonDaemon ); |
410 | 0 | } |
411 | ||
412 | private void joinThread( Thread thread, long timeoutMsecs ) | |
413 | { | |
414 | try | |
415 | { | |
416 | 0 | getLog().debug( "joining on thread " + thread ); |
417 | 0 | thread.join( timeoutMsecs ); |
418 | } | |
419 | 0 | catch ( InterruptedException e ) |
420 | { | |
421 | 0 | Thread.currentThread().interrupt(); // good practice if don't throw |
422 | 0 | getLog().warn( "interrupted while joining against thread " + thread, e ); // not expected! |
423 | 0 | } |
424 | 0 | if ( thread.isAlive() ) //generally abnormal |
425 | { | |
426 | 0 | getLog().warn( "thread " + thread + " was interrupted but is still alive after waiting at least " |
427 | + timeoutMsecs + "msecs" ); | |
428 | } | |
429 | 0 | } |
430 | ||
431 | private void terminateThreads( ThreadGroup threadGroup ) | |
432 | { | |
433 | 0 | long startTime = System.currentTimeMillis(); |
434 | 0 | Set uncooperativeThreads = new HashSet(); // these were not responsive to interruption |
435 | 0 | for ( Collection threads = getActiveThreads( threadGroup ); !threads.isEmpty(); |
436 | 0 | threads = getActiveThreads( threadGroup ), threads.removeAll( uncooperativeThreads ) ) |
437 | { | |
438 | // Interrupt all threads we know about as of this instant (harmless if spuriously went dead (! isAlive()) | |
439 | // or if something else interrupted it ( isInterrupted() ). | |
440 | 0 | for ( Iterator iter = threads.iterator(); iter.hasNext(); ) |
441 | { | |
442 | 0 | Thread thread = (Thread) iter.next(); |
443 | 0 | getLog().debug( "interrupting thread " + thread ); |
444 | 0 | thread.interrupt(); |
445 | 0 | } |
446 | // Now join with a timeout and call stop() (assuming flags are set right) | |
447 | 0 | for ( Iterator iter = threads.iterator(); iter.hasNext(); ) |
448 | { | |
449 | 0 | Thread thread = (Thread) iter.next(); |
450 | 0 | if ( ! thread.isAlive() ) |
451 | { | |
452 | 0 | continue; //and, presumably it won't show up in getActiveThreads() next iteration |
453 | } | |
454 | 0 | if ( daemonThreadJoinTimeout <= 0 ) |
455 | { | |
456 | 0 | joinThread( thread, 0 ); //waits until not alive; no timeout |
457 | 0 | continue; |
458 | } | |
459 | 0 | long timeout = daemonThreadJoinTimeout |
460 | - ( System.currentTimeMillis() - startTime ); | |
461 | 0 | if ( timeout > 0 ) |
462 | { | |
463 | 0 | joinThread( thread, timeout ); |
464 | } | |
465 | 0 | if ( ! thread.isAlive() ) |
466 | { | |
467 | 0 | continue; |
468 | } | |
469 | 0 | uncooperativeThreads.add( thread ); // ensure we don't process again |
470 | 0 | if ( stopUnresponsiveDaemonThreads ) |
471 | { | |
472 | 0 | getLog().warn( "thread " + thread + " will be Thread.stop()'ed" ); |
473 | 0 | thread.stop(); |
474 | } | |
475 | else | |
476 | { | |
477 | 0 | getLog().warn( "thread " + thread + " will linger despite being asked to die via interruption" ); |
478 | } | |
479 | 0 | } |
480 | } | |
481 | 0 | if ( ! uncooperativeThreads.isEmpty() ) |
482 | { | |
483 | 0 | getLog().warn( "NOTE: " + uncooperativeThreads.size() + " thread(s) did not finish despite being asked to " |
484 | + " via interruption. This is not a problem with exec:java, it is a problem with the running code." | |
485 | + " Although not serious, it should be remedied." ); | |
486 | } | |
487 | else | |
488 | { | |
489 | 0 | int activeCount = threadGroup.activeCount(); |
490 | 0 | if ( activeCount != 0 ) |
491 | { | |
492 | // TODO this may be nothing; continue on anyway; perhaps don't even log in future | |
493 | 0 | Thread[] threadsArray = new Thread[1]; |
494 | 0 | threadGroup.enumerate( threadsArray ); |
495 | 0 | getLog().debug( "strange; " + activeCount |
496 | + " thread(s) still active in the group " + threadGroup + " such as " + threadsArray[0] ); | |
497 | } | |
498 | } | |
499 | 0 | } |
500 | ||
501 | private Collection getActiveThreads( ThreadGroup threadGroup ) | |
502 | { | |
503 | 0 | Thread[] threads = new Thread[ threadGroup.activeCount() ]; |
504 | 0 | int numThreads = threadGroup.enumerate( threads ); |
505 | 0 | Collection result = new ArrayList( numThreads ); |
506 | 0 | for ( int i = 0; i < threads.length && threads[i] != null; i++ ) |
507 | { | |
508 | 0 | result.add( threads[i] ); |
509 | } | |
510 | 0 | return result; //note: result should be modifiable |
511 | } | |
512 | ||
513 | /** | |
514 | * Pass any given system properties to the java system properties. | |
515 | */ | |
516 | private void setSystemProperties() | |
517 | { | |
518 | 0 | if ( systemProperties != null ) |
519 | { | |
520 | 0 | originalSystemProperties = System.getProperties(); |
521 | 0 | for ( int i = 0; i < systemProperties.length; i++ ) |
522 | { | |
523 | 0 | Property systemProperty = systemProperties[i]; |
524 | 0 | String value = systemProperty.getValue(); |
525 | 0 | System.setProperty( systemProperty.getKey(), value == null ? "" : value ); |
526 | } | |
527 | } | |
528 | 0 | } |
529 | ||
530 | /** | |
531 | * Set up a classloader for the execution of the main class. | |
532 | * | |
533 | * @return the classloader | |
534 | * @throws MojoExecutionException if a problem happens | |
535 | */ | |
536 | private ClassLoader getClassLoader() | |
537 | throws MojoExecutionException | |
538 | { | |
539 | 0 | List classpathURLs = new ArrayList(); |
540 | 0 | this.addRelevantPluginDependenciesToClasspath( classpathURLs ); |
541 | 0 | this.addRelevantProjectDependenciesToClasspath( classpathURLs ); |
542 | 0 | return new URLClassLoader( ( URL[] ) classpathURLs.toArray( new URL[ classpathURLs.size() ] ) ); |
543 | } | |
544 | ||
545 | /** | |
546 | * Add any relevant project dependencies to the classpath. | |
547 | * Indirectly takes includePluginDependencies and ExecutableDependency into consideration. | |
548 | * | |
549 | * @param path classpath of {@link java.net.URL} objects | |
550 | * @throws MojoExecutionException if a problem happens | |
551 | */ | |
552 | private void addRelevantPluginDependenciesToClasspath( List path ) | |
553 | throws MojoExecutionException | |
554 | { | |
555 | 0 | if ( hasCommandlineArgs() ) |
556 | { | |
557 | 0 | arguments = parseCommandlineArgs(); |
558 | } | |
559 | ||
560 | try | |
561 | { | |
562 | 0 | Iterator iter = this.determineRelevantPluginDependencies().iterator(); |
563 | 0 | while ( iter.hasNext() ) |
564 | { | |
565 | 0 | Artifact classPathElement = (Artifact) iter.next(); |
566 | 0 | getLog().debug( |
567 | "Adding plugin dependency artifact: " + classPathElement.getArtifactId() + " to classpath" ); | |
568 | 0 | path.add( classPathElement.getFile().toURI().toURL() ); |
569 | 0 | } |
570 | } | |
571 | 0 | catch ( MalformedURLException e ) |
572 | { | |
573 | 0 | throw new MojoExecutionException( "Error during setting up classpath", e ); |
574 | 0 | } |
575 | ||
576 | 0 | } |
577 | ||
578 | /** | |
579 | * Add any relevant project dependencies to the classpath. | |
580 | * Takes includeProjectDependencies into consideration. | |
581 | * | |
582 | * @param path classpath of {@link java.net.URL} objects | |
583 | * @throws MojoExecutionException if a problem happens | |
584 | */ | |
585 | private void addRelevantProjectDependenciesToClasspath( List path ) | |
586 | throws MojoExecutionException | |
587 | { | |
588 | 0 | if ( this.includeProjectDependencies ) |
589 | { | |
590 | try | |
591 | { | |
592 | 0 | getLog().debug( "Project Dependencies will be included." ); |
593 | ||
594 | 0 | List artifacts = new ArrayList(); |
595 | 0 | List theClasspathFiles = new ArrayList(); |
596 | ||
597 | 0 | collectProjectArtifactsAndClasspath( artifacts, theClasspathFiles ); |
598 | ||
599 | 0 | for ( Iterator it = theClasspathFiles.iterator(); it.hasNext(); ) |
600 | { | |
601 | 0 | URL url = ( (File) it.next() ).toURI().toURL(); |
602 | 0 | getLog().debug( "Adding to classpath : " + url ); |
603 | 0 | path.add( url ); |
604 | 0 | } |
605 | ||
606 | 0 | Iterator iter = artifacts.iterator(); |
607 | 0 | while ( iter.hasNext() ) |
608 | { | |
609 | 0 | Artifact classPathElement = (Artifact) iter.next(); |
610 | 0 | getLog().debug( |
611 | "Adding project dependency artifact: " + classPathElement.getArtifactId() + " to classpath" ); | |
612 | 0 | path.add( classPathElement.getFile().toURI().toURL() ); |
613 | 0 | } |
614 | ||
615 | } | |
616 | 0 | catch ( MalformedURLException e ) |
617 | { | |
618 | 0 | throw new MojoExecutionException( "Error during setting up classpath", e ); |
619 | 0 | } |
620 | } | |
621 | else | |
622 | { | |
623 | 0 | getLog().debug( "Project Dependencies will be excluded." ); |
624 | } | |
625 | ||
626 | 0 | } |
627 | ||
628 | /** | |
629 | * Determine all plugin dependencies relevant to the executable. | |
630 | * Takes includePlugins, and the executableDependency into consideration. | |
631 | * | |
632 | * @return a set of Artifact objects. | |
633 | * (Empty set is returned if there are no relevant plugin dependencies.) | |
634 | * @throws MojoExecutionException if a problem happens resolving the plufin dependencies | |
635 | */ | |
636 | private Set determineRelevantPluginDependencies() | |
637 | throws MojoExecutionException | |
638 | { | |
639 | Set relevantDependencies; | |
640 | 0 | if ( this.includePluginDependencies ) |
641 | { | |
642 | 0 | if ( this.executableDependency == null ) |
643 | { | |
644 | 0 | getLog().debug( "All Plugin Dependencies will be included." ); |
645 | 0 | relevantDependencies = new HashSet( this.pluginDependencies ); |
646 | } | |
647 | else | |
648 | { | |
649 | 0 | getLog().debug( "Selected plugin Dependencies will be included." ); |
650 | 0 | Artifact executableArtifact = this.findExecutableArtifact(); |
651 | 0 | Artifact executablePomArtifact = this.getExecutablePomArtifact( executableArtifact ); |
652 | 0 | relevantDependencies = this.resolveExecutableDependencies( executablePomArtifact ); |
653 | 0 | } |
654 | } | |
655 | else | |
656 | { | |
657 | 0 | relevantDependencies = Collections.EMPTY_SET; |
658 | 0 | getLog().debug( "Plugin Dependencies will be excluded." ); |
659 | } | |
660 | 0 | return relevantDependencies; |
661 | } | |
662 | ||
663 | /** | |
664 | * Get the artifact which refers to the POM of the executable artifact. | |
665 | * | |
666 | * @param executableArtifact this artifact refers to the actual assembly. | |
667 | * @return an artifact which refers to the POM of the executable artifact. | |
668 | */ | |
669 | private Artifact getExecutablePomArtifact( Artifact executableArtifact ) | |
670 | { | |
671 | 0 | return this.artifactFactory.createBuildArtifact( executableArtifact.getGroupId(), |
672 | executableArtifact.getArtifactId(), | |
673 | executableArtifact.getVersion(), "pom" ); | |
674 | } | |
675 | ||
676 | /** | |
677 | * Examine the plugin dependencies to find the executable artifact. | |
678 | * | |
679 | * @return an artifact which refers to the actual executable tool (not a POM) | |
680 | * @throws MojoExecutionException if no executable artifact was found | |
681 | */ | |
682 | private Artifact findExecutableArtifact() | |
683 | throws MojoExecutionException | |
684 | { | |
685 | //ILimitedArtifactIdentifier execToolAssembly = this.getExecutableToolAssembly(); | |
686 | ||
687 | 0 | Artifact executableTool = null; |
688 | 0 | for ( Iterator iter = this.pluginDependencies.iterator(); iter.hasNext(); ) |
689 | { | |
690 | 0 | Artifact pluginDep = (Artifact) iter.next(); |
691 | 0 | if ( this.executableDependency.matches( pluginDep ) ) |
692 | { | |
693 | 0 | executableTool = pluginDep; |
694 | 0 | break; |
695 | } | |
696 | 0 | } |
697 | ||
698 | 0 | if ( executableTool == null ) |
699 | { | |
700 | 0 | throw new MojoExecutionException( |
701 | "No dependency of the plugin matches the specified executableDependency." | |
702 | + " Specified executableToolAssembly is: " + executableDependency.toString() ); | |
703 | } | |
704 | ||
705 | 0 | return executableTool; |
706 | } | |
707 | ||
708 | /** | |
709 | * Resolve the executable dependencies for the specified project | |
710 | * @param executablePomArtifact the project's POM | |
711 | * @return a set of Artifacts | |
712 | * @throws MojoExecutionException if a failure happens | |
713 | */ | |
714 | private Set resolveExecutableDependencies( Artifact executablePomArtifact ) | |
715 | throws MojoExecutionException | |
716 | { | |
717 | ||
718 | Set executableDependencies; | |
719 | try | |
720 | { | |
721 | 0 | MavenProject executableProject = this.projectBuilder.buildFromRepository( executablePomArtifact, |
722 | this.remoteRepositories, | |
723 | this.localRepository ); | |
724 | ||
725 | //get all of the dependencies for the executable project | |
726 | 0 | List dependencies = executableProject.getDependencies(); |
727 | ||
728 | //make Artifacts of all the dependencies | |
729 | 0 | Set dependencyArtifacts = |
730 | MavenMetadataSource.createArtifacts( this.artifactFactory, dependencies, null, null, null ); | |
731 | ||
732 | //not forgetting the Artifact of the project itself | |
733 | 0 | dependencyArtifacts.add( executableProject.getArtifact() ); |
734 | ||
735 | //resolve all dependencies transitively to obtain a comprehensive list of assemblies | |
736 | 0 | ArtifactResolutionResult result = artifactResolver.resolveTransitively( dependencyArtifacts, |
737 | executablePomArtifact, | |
738 | Collections.EMPTY_MAP, | |
739 | this.localRepository, | |
740 | this.remoteRepositories, | |
741 | metadataSource, null, | |
742 | Collections.EMPTY_LIST ); | |
743 | 0 | executableDependencies = result.getArtifacts(); |
744 | ||
745 | } | |
746 | 0 | catch ( Exception ex ) |
747 | { | |
748 | 0 | throw new MojoExecutionException( |
749 | "Encountered problems resolving dependencies of the executable " + "in preparation for its execution.", | |
750 | ex ); | |
751 | 0 | } |
752 | ||
753 | 0 | return executableDependencies; |
754 | } | |
755 | ||
756 | /** | |
757 | * Stop program execution for nn millis. | |
758 | * | |
759 | * @param millis the number of millis-seconds to wait for, | |
760 | * <code>0</code> stops program forever. | |
761 | */ | |
762 | private void waitFor( long millis ) | |
763 | { | |
764 | 0 | Object lock = new Object(); |
765 | 0 | synchronized ( lock ) |
766 | { | |
767 | try | |
768 | { | |
769 | 0 | lock.wait( millis ); |
770 | } | |
771 | 0 | catch ( InterruptedException e ) |
772 | { | |
773 | 0 | Thread.currentThread().interrupt(); // good practice if don't throw |
774 | 0 | getLog().warn( "Spuriously interrupted while waiting for " + millis + "ms", e ); |
775 | 0 | } |
776 | 0 | } |
777 | 0 | } |
778 | ||
779 | } |