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 }