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 }