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