001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.guice.maven;
018
019 import java.io.File;
020 import java.lang.reflect.Method;
021 import java.net.MalformedURLException;
022 import java.net.URL;
023 import java.net.URLClassLoader;
024 import java.util.ArrayList;
025 import java.util.Arrays;
026 import java.util.Collection;
027 import java.util.Collections;
028 import java.util.HashSet;
029 import java.util.Iterator;
030 import java.util.List;
031 import java.util.Properties;
032 import java.util.Set;
033
034 import org.apache.maven.artifact.Artifact;
035 import org.apache.maven.artifact.factory.ArtifactFactory;
036 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
037 import org.apache.maven.artifact.repository.ArtifactRepository;
038 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
039 import org.apache.maven.artifact.resolver.ArtifactResolver;
040 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
041 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
042 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
043 import org.apache.maven.artifact.versioning.VersionRange;
044 import org.apache.maven.model.Dependency;
045 import org.apache.maven.model.Exclusion;
046 import org.apache.maven.plugin.MojoExecutionException;
047 import org.apache.maven.plugin.MojoFailureException;
048 import org.apache.maven.project.MavenProject;
049 import org.apache.maven.project.MavenProjectBuilder;
050 import org.apache.maven.project.artifact.MavenMetadataSource;
051 import org.codehaus.mojo.exec.AbstractExecMojo;
052 import org.codehaus.mojo.exec.ExecutableDependency;
053 import org.codehaus.mojo.exec.Property;
054
055 /**
056 * Runs a Camel using the
057 * <code>jndi.properties</code> file on the classpath as the
058 * way to <a href="http://camel.apache.org/guice.html">bootstrap Camel using Guice</a>
059 *
060 * @goal run
061 * @requiresDependencyResolution runtime
062 * @execute phase="test-compile"
063 */
064 public class RunMojo extends AbstractExecMojo {
065
066 // TODO
067 // this code is based on a copy-and-paste of maven-exec-plugin
068 //
069 // If we could avoid the mega-cut-n-paste it would really really help!
070 // ideally all I wanna do is auto-default 2 values!
071 // namely the main and the command line arguments..
072
073 /**
074 * The maven project.
075 *
076 * @parameter expression="${project}"
077 * @required
078 * @readonly
079 */
080 protected MavenProject project;
081
082 /**
083 * The duration to run the application for which by default is in
084 * milliseconds. A value <= 0 will run forever.
085 * Adding a s indicates seconds - eg "5s" means 5 seconds.
086 *
087 * @parameter expression="-1"
088 *
089 */
090 protected String duration;
091
092 /**
093 * The DOT output directory name used to generate the DOT diagram of the
094 * route definitions
095 *
096 * @parameter expression="${project.build.directory}/site/cameldoc"
097 * @readonly
098 */
099 protected String dotDir;
100
101 /**
102 * Allows the DOT file generation to be disabled
103 *
104 * @parameter expression="true"
105 * @readonly
106 */
107 protected boolean dotEnabled;
108
109 /**
110 * @component
111 */
112 private ArtifactResolver artifactResolver;
113
114 /**
115 * @component
116 */
117 private ArtifactFactory artifactFactory;
118
119 /**
120 * @component
121 */
122 private ArtifactMetadataSource metadataSource;
123
124 /**
125 * @parameter expression="${localRepository}"
126 * @required
127 * @readonly
128 */
129 private ArtifactRepository localRepository;
130
131 /**
132 * @parameter expression="${project.remoteArtifactRepositories}"
133 */
134 private List remoteRepositories;
135
136 /**
137 * @component
138 */
139 private MavenProjectBuilder projectBuilder;
140
141 /**
142 * @parameter expression="${plugin.artifacts}"
143 * @readonly
144 */
145 private List pluginDependencies;
146
147 /**
148 * Whether to enable the debugger or not
149 *
150 * @parameter expression="${camel.debug}"
151 * default-value="false"
152 * @required
153 */
154 private boolean debug;
155
156 /**
157 * Whether to enable the tracer or not
158 *
159 * @parameter expression="${camel.trace}"
160 * default-value="false"
161 * @required
162 */
163 private boolean trace;
164
165 /**
166 * Output all routes to the specified XML file
167 *
168 * @parameter expression="${camel.routesOutputFile}"
169 */
170 private String routesOutputFile;
171
172 /**
173 * The main class to execute.
174 *
175 * @parameter expression="${camel.mainClass}"
176 * default-value="org.apache.camel.guice.Main"
177 * @required
178 */
179 private String mainClass;
180
181 /**
182 * The class arguments.
183 *
184 * @parameter expression="${camel.arguments}"
185 */
186 private String[] arguments;
187
188 /**
189 * A list of system properties to be passed. Note: as the execution is not
190 * forked, some system properties required by the JVM cannot be passed here.
191 * Use MAVEN_OPTS or the exec:exec instead. See the user guide for more
192 * information.
193 *
194 * @parameter
195 */
196 private Property[] systemProperties;
197
198 /**
199 * Deprecated; this is not needed anymore. Indicates if mojo should be kept
200 * running after the mainclass terminates. Usefull for serverlike apps with
201 * deamonthreads.
202 *
203 * @parameter expression="${camel.keepAlive}" default-value="false"
204 */
205 private boolean keepAlive;
206
207 /**
208 * Indicates if the project dependencies should be used when executing the
209 * main class.
210 *
211 * @parameter expression="${camel.includeProjectDependencies}"
212 * default-value="true"
213 */
214 private boolean includeProjectDependencies;
215
216 /**
217 * Indicates if this plugin's dependencies should be used when executing the
218 * main class. <p/> This is useful when project dependencies are not
219 * appropriate. Using only the plugin dependencies can be particularly
220 * useful when the project is not a java project. For example a mvn project
221 * using the csharp plugins only expects to see dotnet libraries as
222 * dependencies.
223 *
224 * @parameter expression="${camel.includePluginDependencies}"
225 * default-value="false"
226 */
227 private boolean includePluginDependencies;
228
229 /**
230 * If provided the ExecutableDependency identifies which of the plugin
231 * dependencies contains the executable class. This will have the affect of
232 * only including plugin dependencies required by the identified
233 * ExecutableDependency. <p/> If includeProjectDependencies is set to
234 * <code>true</code>, all of the project dependencies will be included on
235 * the executable's classpath. Whether a particular project dependency is a
236 * dependency of the identified ExecutableDependency will be irrelevant to
237 * its inclusion in the classpath.
238 *
239 * @parameter
240 * @optional
241 */
242 private ExecutableDependency executableDependency;
243
244 /**
245 * Wether to interrupt/join and possibly stop the daemon threads upon
246 * quitting. <br/> If this is <code>false</code>, maven does nothing
247 * about the daemon threads. When maven has no more work to do, the VM will
248 * normally terminate any remaining daemon threads.
249 * <p>
250 * In certain cases (in particular if maven is embedded), you might need to
251 * keep this enabled to make sure threads are properly cleaned up to ensure
252 * they don't interfere with subsequent activity. In that case, see
253 * {@link #daemonThreadJoinTimeout} and
254 * {@link #stopUnresponsiveDaemonThreads} for further tuning.
255 * </p>
256 *
257 * @parameter expression="${camel.cleanupDaemonThreads} default-value="true"
258 */
259 private boolean cleanupDaemonThreads;
260
261 /**
262 * This defines the number of milliseconds to wait for daemon threads to
263 * quit following their interruption.<br/> This is only taken into account
264 * if {@link #cleanupDaemonThreads} is <code>true</code>. A value <=0
265 * means to not timeout (i.e. wait indefinitely for threads to finish).
266 * Following a timeout, a warning will be logged.
267 * <p>
268 * Note: properly coded threads <i>should</i> terminate upon interruption
269 * but some threads may prove problematic: as the VM does interrupt daemon
270 * threads, some code may not have been written to handle interruption
271 * properly. For example java.util.Timer is known to not handle
272 * interruptions in JDK <= 1.6. So it is not possible for us to
273 * infinitely wait by default otherwise maven could hang. A sensible default
274 * value has been chosen, but this default value <i>may change</i> in the
275 * future based on user feedback.
276 * </p>
277 *
278 * @parameter expression="${camel.daemonThreadJoinTimeout}"
279 * default-value="15000"
280 */
281 private long daemonThreadJoinTimeout;
282
283 /**
284 * Wether to call {@link Thread#stop()} following a timing out of waiting
285 * for an interrupted thread to finish. This is only taken into account if
286 * {@link #cleanupDaemonThreads} is <code>true</code> and the
287 * {@link #daemonThreadJoinTimeout} threshold has been reached for an
288 * uncooperative thread. If this is <code>false</code>, or if
289 * {@link Thread#stop()} fails to get the thread to stop, then a warning is
290 * logged and Maven will continue on while the affected threads (and related
291 * objects in memory) linger on. Consider setting this to <code>true</code>
292 * if you are invoking problematic code that you can't fix. An example is
293 * {@link java.util.Timer} which doesn't respond to interruption. To have
294 * <code>Timer</code> fixed, vote for <a
295 * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6336543">this
296 * bug</a>.
297 *
298 * @parameter expression="${camel.stopUnresponsiveDaemonThreads}
299 * default-value="false"
300 */
301 private boolean stopUnresponsiveDaemonThreads;
302
303 /**
304 * Deprecated this is not needed anymore.
305 *
306 * @parameter expression="${camel.killAfter}" default-value="-1"
307 */
308 private long killAfter;
309
310 private Properties originalSystemProperties;
311
312 /**
313 * Execute goal.
314 *
315 * @throws org.apache.maven.plugin.MojoExecutionException execution of the main class or one of the
316 * threads it generated failed.
317 * @throws org.apache.maven.plugin.MojoFailureException something bad happened...
318 */
319 public void execute() throws MojoExecutionException, MojoFailureException {
320 if (killAfter != -1) {
321 getLog().warn("Warning: killAfter is now deprecated. Do you need it ? Please comment on MEXEC-6.");
322 }
323
324 // lets create the command line arguments to pass in...
325 List<String> args = new ArrayList<String>();
326 if (dotDir != null && dotEnabled) {
327 args.add("-o");
328 args.add(dotDir);
329 }
330 if (debug) {
331 args.add("-x");
332 }
333 if (trace) {
334 args.add("-t");
335 }
336
337 if (routesOutputFile != null) {
338 args.add("-output");
339 args.add(routesOutputFile);
340 }
341
342 args.add("-d");
343 args.add(duration);
344 if (arguments != null) {
345 args.addAll(Arrays.asList(arguments));
346 }
347 arguments = new String[args.size()];
348 args.toArray(arguments);
349
350 if (getLog().isDebugEnabled()) {
351 StringBuffer msg = new StringBuffer("Invoking : ");
352 msg.append(mainClass);
353 msg.append(".main(");
354 for (int i = 0; i < arguments.length; i++) {
355 if (i > 0) {
356 msg.append(", ");
357 }
358 msg.append(arguments[i]);
359 }
360 msg.append(")");
361 getLog().debug(msg);
362 }
363
364 IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(mainClass /* name */);
365 Thread bootstrapThread = new Thread(threadGroup, new Runnable() {
366 public void run() {
367 try {
368 Method main = Thread.currentThread().getContextClassLoader().loadClass(mainClass)
369 .getMethod("main", new Class[] {String[].class});
370 if (!main.isAccessible()) {
371 getLog().debug("Setting accessibility to true in order to invoke main().");
372 main.setAccessible(true);
373 }
374 main.invoke(main, new Object[] {arguments});
375 } catch (Exception e) { // just pass it on
376 Thread.currentThread().getThreadGroup().uncaughtException(Thread.currentThread(), e);
377 }
378 }
379 }, mainClass + ".main()");
380 bootstrapThread.setContextClassLoader(getClassLoader());
381 setSystemProperties();
382
383 bootstrapThread.start();
384 joinNonDaemonThreads(threadGroup);
385 // It's plausible that spontaneously a non-daemon thread might be
386 // created as we try and shut down,
387 // but it's too late since the termination condition (only daemon
388 // threads) has been triggered.
389 if (keepAlive) {
390 getLog().warn("Warning: keepAlive is now deprecated and obsolete. Do you need it? Please comment on MEXEC-6.");
391 waitFor(0);
392 }
393
394 if (cleanupDaemonThreads) {
395
396 terminateThreads(threadGroup);
397
398 try {
399 threadGroup.destroy();
400 } catch (IllegalThreadStateException e) {
401 getLog().warn("Couldn't destroy threadgroup " + threadGroup, e);
402 }
403 }
404
405 if (originalSystemProperties != null) {
406 System.setProperties(originalSystemProperties);
407 }
408
409 synchronized (threadGroup) {
410 if (threadGroup.uncaughtException != null) {
411 throw new MojoExecutionException(null, threadGroup.uncaughtException);
412 }
413 }
414
415 registerSourceRoots();
416 }
417
418 class IsolatedThreadGroup extends ThreadGroup {
419 Throwable uncaughtException; // synchronize access to this
420
421 public IsolatedThreadGroup(String name) {
422 super(name);
423 }
424
425 public void uncaughtException(Thread thread, Throwable throwable) {
426 if (throwable instanceof ThreadDeath) {
427 return; // harmless
428 }
429 boolean doLog = false;
430 synchronized (this) {
431 // only remember the first one
432 if (uncaughtException == null) {
433 uncaughtException = throwable; // will be reported
434 // eventually
435 } else {
436 doLog = true;
437 }
438 }
439 if (doLog) {
440 getLog().warn("an additional exception was thrown", throwable);
441 }
442 }
443 }
444
445 private void joinNonDaemonThreads(ThreadGroup threadGroup) {
446 boolean foundNonDaemon;
447 do {
448 foundNonDaemon = false;
449 Collection threads = getActiveThreads(threadGroup);
450 for (Iterator iter = threads.iterator(); iter.hasNext();) {
451 Thread thread = (Thread)iter.next();
452 if (thread.isDaemon()) {
453 continue;
454 }
455 foundNonDaemon = true; // try again; maybe more threads were
456 // created while we were busy
457 joinThread(thread, 0);
458 }
459 } while (foundNonDaemon);
460 }
461
462 private void joinThread(Thread thread, long timeoutMsecs) {
463 try {
464 getLog().debug("joining on thread " + thread);
465 thread.join(timeoutMsecs);
466 } catch (InterruptedException e) {
467 Thread.currentThread().interrupt(); // good practice if don't throw
468 getLog().warn("interrupted while joining against thread " + thread, e); // not
469 // expected!
470 }
471 // generally abnormal
472 if (thread.isAlive()) {
473 getLog().warn("thread " + thread + " was interrupted but is still alive after waiting at least "
474 + timeoutMsecs + "msecs");
475 }
476 }
477
478 private void terminateThreads(ThreadGroup threadGroup) {
479 long startTime = System.currentTimeMillis();
480 Set uncooperativeThreads = new HashSet(); // these were not responsive
481 // to interruption
482 for (Collection threads = getActiveThreads(threadGroup); !threads.isEmpty(); threads = getActiveThreads(threadGroup), threads
483 .removeAll(uncooperativeThreads)) {
484 // Interrupt all threads we know about as of this instant (harmless
485 // if spuriously went dead (! isAlive())
486 // or if something else interrupted it ( isInterrupted() ).
487 for (Iterator iter = threads.iterator(); iter.hasNext();) {
488 Thread thread = (Thread)iter.next();
489 getLog().debug("interrupting thread " + thread);
490 thread.interrupt();
491 }
492 // Now join with a timeout and call stop() (assuming flags are set
493 // right)
494 for (Iterator iter = threads.iterator(); iter.hasNext();) {
495 Thread thread = (Thread)iter.next();
496 if (!thread.isAlive()) {
497 continue; // and, presumably it won't show up in
498 // getActiveThreads() next iteration
499 }
500 if (daemonThreadJoinTimeout <= 0) {
501 joinThread(thread, 0); // waits until not alive; no timeout
502 continue;
503 }
504 long timeout = daemonThreadJoinTimeout - (System.currentTimeMillis() - startTime);
505 if (timeout > 0) {
506 joinThread(thread, timeout);
507 }
508 if (!thread.isAlive()) {
509 continue;
510 }
511 uncooperativeThreads.add(thread); // ensure we don't process
512 // again
513 if (stopUnresponsiveDaemonThreads) {
514 getLog().warn("thread " + thread + " will be Thread.stop()'ed");
515 thread.stop();
516 } else {
517 getLog().warn("thread " + thread
518 + " will linger despite being asked to die via interruption");
519 }
520 }
521 }
522 if (!uncooperativeThreads.isEmpty()) {
523 getLog().warn("NOTE: "
524 + uncooperativeThreads.size()
525 + " thread(s) did not finish despite being asked to "
526 + " via interruption. This is not a problem with exec:java, it is a problem with the running code."
527 + " Although not serious, it should be remedied.");
528 } else {
529 int activeCount = threadGroup.activeCount();
530 if (activeCount != 0) {
531 // TODO this may be nothing; continue on anyway; perhaps don't
532 // even log in future
533 Thread[] threadsArray = new Thread[1];
534 threadGroup.enumerate(threadsArray);
535 getLog().debug("strange; " + activeCount + " thread(s) still active in the group "
536 + threadGroup + " such as " + threadsArray[0]);
537 }
538 }
539 }
540
541 private Collection getActiveThreads(ThreadGroup threadGroup) {
542 Thread[] threads = new Thread[threadGroup.activeCount()];
543 int numThreads = threadGroup.enumerate(threads);
544 Collection result = new ArrayList(numThreads);
545 for (int i = 0; i < threads.length && threads[i] != null; i++) {
546 result.add(threads[i]);
547 }
548 // note: result should be modifiable
549 return result;
550 }
551
552 /**
553 * Pass any given system properties to the java system properties.
554 */
555 private void setSystemProperties() {
556 if (systemProperties != null) {
557 originalSystemProperties = System.getProperties();
558 for (int i = 0; i < systemProperties.length; i++) {
559 Property systemProperty = systemProperties[i];
560 String value = systemProperty.getValue();
561 System.setProperty(systemProperty.getKey(), value == null ? "" : value);
562 }
563 }
564 }
565
566 /**
567 * Set up a classloader for the execution of the main class.
568 *
569 * @return the classloader
570 * @throws org.apache.maven.plugin.MojoExecutionException
571 */
572 private ClassLoader getClassLoader() throws MojoExecutionException {
573 List classpathURLs = new ArrayList();
574 this.addRelevantPluginDependenciesToClasspath(classpathURLs);
575 this.addRelevantProjectDependenciesToClasspath(classpathURLs);
576
577 getLog().info("Classpath = " + classpathURLs);
578 return new URLClassLoader((URL[])classpathURLs.toArray(new URL[classpathURLs.size()]));
579 }
580
581 /**
582 * Add any relevant project dependencies to the classpath. Indirectly takes
583 * includePluginDependencies and ExecutableDependency into consideration.
584 *
585 * @param path classpath of {@link java.net.URL} objects
586 * @throws org.apache.maven.plugin.MojoExecutionException
587 */
588 private void addRelevantPluginDependenciesToClasspath(List path) throws MojoExecutionException {
589 if (hasCommandlineArgs()) {
590 arguments = parseCommandlineArgs();
591 }
592
593 try {
594 Iterator iter = this.determineRelevantPluginDependencies().iterator();
595 while (iter.hasNext()) {
596 Artifact classPathElement = (Artifact)iter.next();
597 getLog().debug("Adding plugin dependency artifact: " + classPathElement.getArtifactId()
598 + " to classpath");
599 path.add(classPathElement.getFile().toURL());
600 }
601 } catch (MalformedURLException e) {
602 throw new MojoExecutionException("Error during setting up classpath", e);
603 }
604
605 }
606
607 /**
608 * Add any relevant project dependencies to the classpath. Takes
609 * includeProjectDependencies into consideration.
610 *
611 * @param path classpath of {@link java.net.URL} objects
612 * @throws org.apache.maven.plugin.MojoExecutionException
613 */
614 private void addRelevantProjectDependenciesToClasspath(List path) throws MojoExecutionException {
615 if (this.includeProjectDependencies) {
616 try {
617 getLog().debug("Project Dependencies will be included.");
618
619 URL mainClasses = new File(project.getBuild().getOutputDirectory()).toURL();
620 getLog().debug("Adding to classpath : " + mainClasses);
621 path.add(mainClasses);
622
623 Set dependencies = project.getArtifacts();
624
625 // system scope dependencies are not returned by maven 2.0. See
626 // MEXEC-17
627 dependencies.addAll(getAllNonTestScopedDependencies());
628
629 Iterator iter = dependencies.iterator();
630 while (iter.hasNext()) {
631 Artifact classPathElement = (Artifact)iter.next();
632 getLog().debug("Adding project dependency artifact: " + classPathElement.getArtifactId()
633 + " to classpath");
634 File file = classPathElement.getFile();
635 if (file != null) {
636 path.add(file.toURL());
637 }
638 }
639
640 } catch (MalformedURLException e) {
641 throw new MojoExecutionException("Error during setting up classpath", e);
642 }
643 } else {
644 getLog().debug("Project Dependencies will be excluded.");
645 }
646
647 }
648
649 private Collection getAllNonTestScopedDependencies() throws MojoExecutionException {
650 List answer = new ArrayList();
651
652 for (Iterator artifacts = getAllDependencies().iterator(); artifacts.hasNext();) {
653 Artifact artifact = (Artifact)artifacts.next();
654
655 // do not add test artifacts
656 if (!artifact.getScope().equals(Artifact.SCOPE_TEST)) {
657 answer.add(artifact);
658 }
659 }
660 return answer;
661 }
662
663 // generic method to retrieve all the transitive dependencies
664 private Collection getAllDependencies() throws MojoExecutionException {
665 List artifacts = new ArrayList();
666
667 for (Iterator dependencies = project.getDependencies().iterator(); dependencies.hasNext();) {
668 Dependency dependency = (Dependency)dependencies.next();
669
670 String groupId = dependency.getGroupId();
671 String artifactId = dependency.getArtifactId();
672
673 VersionRange versionRange;
674 try {
675 versionRange = VersionRange.createFromVersionSpec(dependency.getVersion());
676 } catch (InvalidVersionSpecificationException e) {
677 throw new MojoExecutionException("unable to parse version", e);
678 }
679
680 String type = dependency.getType();
681 if (type == null) {
682 type = "jar";
683 }
684 String classifier = dependency.getClassifier();
685 boolean optional = dependency.isOptional();
686 String scope = dependency.getScope();
687 if (scope == null) {
688 scope = Artifact.SCOPE_COMPILE;
689 }
690
691 Artifact art = this.artifactFactory.createDependencyArtifact(groupId, artifactId, versionRange,
692 type, classifier, scope, optional);
693
694 if (scope.equalsIgnoreCase(Artifact.SCOPE_SYSTEM)) {
695 art.setFile(new File(dependency.getSystemPath()));
696 }
697
698 List exclusions = new ArrayList();
699 for (Iterator j = dependency.getExclusions().iterator(); j.hasNext();) {
700 Exclusion e = (Exclusion)j.next();
701 exclusions.add(e.getGroupId() + ":" + e.getArtifactId());
702 }
703
704 ArtifactFilter newFilter = new ExcludesArtifactFilter(exclusions);
705
706 art.setDependencyFilter(newFilter);
707
708 artifacts.add(art);
709 }
710
711 return artifacts;
712 }
713
714 /**
715 * Determine all plugin dependencies relevant to the executable. Takes
716 * includePlugins, and the executableDependency into consideration.
717 *
718 * @return a set of Artifact objects. (Empty set is returned if there are no
719 * relevant plugin dependencies.)
720 * @throws org.apache.maven.plugin.MojoExecutionException
721 */
722 private Set determineRelevantPluginDependencies() throws MojoExecutionException {
723 Set relevantDependencies;
724 if (this.includePluginDependencies) {
725 if (this.executableDependency == null) {
726 getLog().debug("All Plugin Dependencies will be included.");
727 relevantDependencies = new HashSet(this.pluginDependencies);
728 } else {
729 getLog().debug("Selected plugin Dependencies will be included.");
730 Artifact executableArtifact = this.findExecutableArtifact();
731 Artifact executablePomArtifact = this.getExecutablePomArtifact(executableArtifact);
732 relevantDependencies = this.resolveExecutableDependencies(executablePomArtifact);
733 }
734 } else {
735 relevantDependencies = Collections.EMPTY_SET;
736 getLog().debug("Plugin Dependencies will be excluded.");
737 }
738 return relevantDependencies;
739 }
740
741 /**
742 * Get the artifact which refers to the POM of the executable artifact.
743 *
744 * @param executableArtifact this artifact refers to the actual assembly.
745 * @return an artifact which refers to the POM of the executable artifact.
746 */
747 private Artifact getExecutablePomArtifact(Artifact executableArtifact) {
748 return this.artifactFactory.createBuildArtifact(executableArtifact.getGroupId(), executableArtifact
749 .getArtifactId(), executableArtifact.getVersion(), "pom");
750 }
751
752 /**
753 * Examine the plugin dependencies to find the executable artifact.
754 *
755 * @return an artifact which refers to the actual executable tool (not a POM)
756 * @throws org.apache.maven.plugin.MojoExecutionException
757 */
758 private Artifact findExecutableArtifact() throws MojoExecutionException {
759 // ILimitedArtifactIdentifier execToolAssembly =
760 // this.getExecutableToolAssembly();
761
762 Artifact executableTool = null;
763 for (Iterator iter = this.pluginDependencies.iterator(); iter.hasNext();) {
764 Artifact pluginDep = (Artifact)iter.next();
765 if (this.executableDependency.matches(pluginDep)) {
766 executableTool = pluginDep;
767 break;
768 }
769 }
770
771 if (executableTool == null) {
772 throw new MojoExecutionException("No dependency of the plugin matches the specified executableDependency."
773 + " Specified executableToolAssembly is: "
774 + executableDependency.toString());
775 }
776
777 return executableTool;
778 }
779
780 private Set resolveExecutableDependencies(Artifact executablePomArtifact) throws MojoExecutionException {
781
782 Set executableDependencies;
783 try {
784 MavenProject executableProject = this.projectBuilder.buildFromRepository(executablePomArtifact,
785 this.remoteRepositories,
786 this.localRepository);
787
788 // get all of the dependencies for the executable project
789 List dependencies = executableProject.getDependencies();
790
791 // make Artifacts of all the dependencies
792 Set dependencyArtifacts = MavenMetadataSource.createArtifacts(this.artifactFactory, dependencies,
793 null, null, null);
794
795 // not forgetting the Artifact of the project itself
796 dependencyArtifacts.add(executableProject.getArtifact());
797
798 // resolve all dependencies transitively to obtain a comprehensive
799 // list of assemblies
800 ArtifactResolutionResult result = artifactResolver.resolveTransitively(dependencyArtifacts,
801 executablePomArtifact,
802 Collections.EMPTY_MAP,
803 this.localRepository,
804 this.remoteRepositories,
805 metadataSource, null,
806 Collections.EMPTY_LIST);
807 executableDependencies = result.getArtifacts();
808
809 } catch (Exception ex) {
810 throw new MojoExecutionException("Encountered problems resolving dependencies of the executable "
811 + "in preparation for its execution.", ex);
812 }
813
814 return executableDependencies;
815 }
816
817 /**
818 * Stop program execution for nn millis.
819 *
820 * @param millis the number of millis-seconds to wait for, <code>0</code>
821 * stops program forever.
822 */
823 private void waitFor(long millis) {
824 Object lock = new Object();
825 synchronized (lock) {
826 try {
827 lock.wait(millis);
828 } catch (InterruptedException e) {
829 Thread.currentThread().interrupt(); // good practice if don't throw
830 getLog().warn("Spuriously interrupted while waiting for " + millis + "ms", e);
831 }
832 }
833 }
834
835 }