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