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