View Javadoc

1   package org.codehaus.mojo.exec;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.factory.ArtifactFactory;
24  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
25  import org.apache.maven.artifact.repository.ArtifactRepository;
26  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
27  import org.apache.maven.artifact.resolver.ArtifactResolver;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.plugin.MojoFailureException;
30  import org.apache.maven.project.MavenProject;
31  import org.apache.maven.project.MavenProjectBuilder;
32  import org.apache.maven.project.artifact.MavenMetadataSource;
33  
34  import java.io.File;
35  import java.lang.reflect.Method;
36  import java.lang.reflect.Modifier;
37  import java.net.MalformedURLException;
38  import java.net.URL;
39  import java.net.URLClassLoader;
40  import java.util.ArrayList;
41  import java.util.Collection;
42  import java.util.Collections;
43  import java.util.HashSet;
44  import java.util.Iterator;
45  import java.util.List;
46  import java.util.Properties;
47  import java.util.Set;
48  
49  /**
50   * Executes the supplied java class in the current VM with the enclosing project's
51   * dependencies as classpath.
52   *
53   * @author <a href="mailto:kaare.nilsen@gmail.com">Kaare Nilsen</a>, <a href="mailto:dsmiley@mitre.org">David Smiley</a>
54   * @goal java
55   * @requiresDependencyResolution test
56   * @execute phase="validate"
57   * @since 1.0
58   */
59  public class ExecJavaMojo
60      extends AbstractExecMojo
61  {
62      /**
63       * @component
64       */
65      private ArtifactResolver artifactResolver;
66  
67      /**
68       * @component
69       */
70      private ArtifactFactory artifactFactory;
71  
72      /**
73       * @component
74       */
75      private ArtifactMetadataSource metadataSource;
76  
77      /**
78       * @parameter default-value="${localRepository}"
79       * @required
80       * @readonly
81       * @since 1.0
82       */
83      private ArtifactRepository localRepository;
84  
85      /**
86       * @parameter default-value="${project.remoteArtifactRepositories}"
87       * @required
88       * @readonly
89       * @since 1.1-beta-1
90       */
91      private List remoteRepositories;
92  
93      /**
94       * @component
95       * @since 1.0
96       */
97      private MavenProjectBuilder projectBuilder;
98  
99      /**
100      * @parameter default-value="${plugin.artifacts}"
101      * @readonly
102      * @since 1.1-beta-1
103      */
104     private List pluginDependencies;
105 
106     /**
107      * The main class to execute.
108      *
109      * @parameter expression="${exec.mainClass}"
110      * @required
111      * @since 1.0
112      */
113     private String mainClass;
114 
115     /**
116      * The class arguments.
117      *
118      * @parameter expression="${exec.arguments}"
119      * @since 1.0
120      */
121     private String[] arguments;
122 
123     /**
124      * A list of system properties to be passed. Note: as the execution is not forked, some system properties
125      * required by the JVM cannot be passed here. Use MAVEN_OPTS or the exec:exec instead. See the user guide for
126      * more information.
127      *
128      * @parameter
129      * @since 1.0
130      */
131     private Property[] systemProperties;
132 
133     /**
134      * Indicates if mojo should be kept running after the mainclass terminates.
135      * Usefull for serverlike apps with deamonthreads.
136      *
137      * @parameter expression="${exec.keepAlive}" default-value="false"
138      * @deprecated since 1.1-alpha-1
139      * @since 1.0
140      */
141     private boolean keepAlive;
142 
143     /**
144      * Indicates if the project dependencies should be used when executing
145      * the main class.
146      *
147      * @parameter expression="${exec.includeProjectDependencies}" default-value="true"
148      * @since 1.1-beta-1
149      */
150     private boolean includeProjectDependencies;
151 
152     /**
153      * Indicates if this plugin's dependencies should be used when executing
154      * the main class.
155      * <p/>
156      * This is useful when project dependencies are not appropriate.  Using only
157      * the plugin dependencies can be particularly useful when the project is
158      * not a java project.  For example a mvn project using the csharp plugins
159      * only expects to see dotnet libraries as dependencies.
160      *
161      * @parameter expression="${exec.includePluginDependencies}" default-value="false"
162      * @since 1.1-beta-1
163      */
164     private boolean includePluginDependencies;
165 
166     /**
167      * If provided the ExecutableDependency identifies which of the plugin dependencies
168      * contains the executable class.  This will have the affect of only including
169      * plugin dependencies required by the identified ExecutableDependency.
170      * <p/>
171      * If includeProjectDependencies is set to <code>true</code>, all of the project dependencies
172      * will be included on the executable's classpath.  Whether a particular project
173      * dependency is a dependency of the identified ExecutableDependency will be
174      * irrelevant to its inclusion in the classpath.
175      *
176      * @parameter
177      * @optional
178      * @since 1.1-beta-1
179      */
180     private ExecutableDependency executableDependency;
181 
182     /**
183      * Wether to interrupt/join and possibly stop the daemon threads upon quitting. <br/> If this is <code>false</code>,
184      *  maven does nothing about the daemon threads.  When maven has no more work to do, the VM will normally terminate
185      *  any remaining daemon threads.
186      * <p>
187      * In certain cases (in particular if maven is embedded),
188      *  you might need to keep this enabled to make sure threads are properly cleaned up to ensure they don't interfere
189      * with subsequent activity.
190      * In that case, see {@link #daemonThreadJoinTimeout} and
191      * {@link #stopUnresponsiveDaemonThreads} for further tuning.
192      * </p>
193      * @parameter expression="${exec.cleanupDaemonThreads} default-value="true"
194      * @since 1.1-beta-1
195      */
196      private boolean cleanupDaemonThreads;
197 
198      /**
199      * This defines the number of milliseconds to wait for daemon threads to quit following their interruption.<br/>
200      * This is only taken into account if {@link #cleanupDaemonThreads} is <code>true</code>.
201      * A value &lt;=0 means to not timeout (i.e. wait indefinitely for threads to finish). Following a timeout, a
202      * warning will be logged.
203      * <p>Note: properly coded threads <i>should</i> terminate upon interruption but some threads may prove
204      * problematic:  as the VM does interrupt daemon threads, some code may not have been written to handle
205      * interruption properly. For example java.util.Timer is known to not handle interruptions in JDK &lt;= 1.6.
206      * So it is not possible for us to infinitely wait by default otherwise maven could hang. A  sensible default 
207      * value has been chosen, but this default value <i>may change</i> in the future based on user feedback.</p>
208      * @parameter expression="${exec.daemonThreadJoinTimeout}" default-value="15000"
209      * @since 1.1-beta-1
210      */
211     private long daemonThreadJoinTimeout;
212 
213     /**
214      * Wether to call {@link Thread#stop()} following a timing out of waiting for an interrupted thread to finish.
215      * This is only taken into account if {@link #cleanupDaemonThreads} is <code>true</code>
216      * and the {@link #daemonThreadJoinTimeout} threshold has been reached for an uncooperative thread.
217      * If this is <code>false</code>, or if {@link Thread#stop()} fails to get the thread to stop, then
218      * a warning is logged and Maven will continue on while the affected threads (and related objects in memory)
219      * linger on.  Consider setting this to <code>true</code> if you are invoking problematic code that you can't fix. 
220      * An example is {@link java.util.Timer} which doesn't respond to interruption.  To have <code>Timer</code>
221      * fixed, vote for <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6336543">this bug</a>.
222      * @parameter expression="${exec.stopUnresponsiveDaemonThreads} default-value="false"
223      * @since 1.1-beta-1
224      */
225     private boolean stopUnresponsiveDaemonThreads;
226 
227     /**
228      * Deprecated this is not needed anymore.
229      *
230      * @parameter expression="${exec.killAfter}" default-value="-1"
231      * @deprecated since 1.1-alpha-1
232      * @since 1.0
233      */
234     private long killAfter;
235         
236     private Properties originalSystemProperties;
237 
238     /**
239      * Execute goal.
240      * @throws MojoExecutionException execution of the main class or one of the threads it generated failed.
241      * @throws MojoFailureException something bad happened...
242      */
243     public void execute()
244         throws MojoExecutionException, MojoFailureException
245     {
246         if ( isSkip() )
247         {
248             getLog().info( "skipping execute as per configuraion" );
249             return;
250         }
251         if ( killAfter != -1 )
252         {
253             getLog().warn( "Warning: killAfter is now deprecated. Do you need it ? Please comment on MEXEC-6." );
254         }
255 
256         if ( null == arguments )
257         {
258             arguments = new String[0];
259         }
260 
261         if ( getLog().isDebugEnabled() )
262         {
263             StringBuffer msg = new StringBuffer( "Invoking : " );
264             msg.append( mainClass );
265             msg.append( ".main(" );
266             for ( int i = 0; i < arguments.length; i++ )
267             {
268                 if ( i > 0 )
269                 {
270                     msg.append( ", " );
271                 }
272                 msg.append( arguments[i] );
273             }
274             msg.append( ")" );
275             getLog().debug(  msg );
276         }
277 
278         IsolatedThreadGroup threadGroup = new IsolatedThreadGroup( mainClass /*name*/ );
279         Thread bootstrapThread = new Thread( threadGroup, new Runnable()
280         {
281             public void run()
282             {
283                 try
284                 {
285                     Method main = Thread.currentThread().getContextClassLoader().loadClass( mainClass )
286                         .getMethod( "main", new Class[]{ String[].class } );
287                     if ( !main.isAccessible() )
288                     {
289                         getLog().debug( "Setting accessibility to true in order to invoke main()." );
290                         main.setAccessible( true );
291                     }
292                     if ( !Modifier.isStatic( main.getModifiers() ) )
293                     {
294                         throw new MojoExecutionException( 
295                                  "Can't call main(String[])-method because it is not static." );
296                     }
297                     main.invoke( null, new Object[] { arguments } );
298                 }
299                 catch ( NoSuchMethodException e )
300                 {   // just pass it on
301                     Thread.currentThread().getThreadGroup().uncaughtException( Thread.currentThread(), 
302                           new Exception( 
303                                "The specified mainClass doesn't contain a main method with appropriate signature.", e
304                           )
305                        );
306                 }
307                 catch ( Exception e )
308                 {   // just pass it on
309                     Thread.currentThread().getThreadGroup().uncaughtException( Thread.currentThread(), e );
310                 }
311             }
312         }, mainClass + ".main()" );
313         bootstrapThread.setContextClassLoader( getClassLoader() );
314         setSystemProperties();
315 
316         bootstrapThread.start();
317         joinNonDaemonThreads( threadGroup );
318         // It's plausible that spontaneously a non-daemon thread might be created as we try and shut down,
319         // but it's too late since the termination condition (only daemon threads) has been triggered.
320         if ( keepAlive )
321         {
322             getLog().warn(
323                 "Warning: keepAlive is now deprecated and obsolete. Do you need it? Please comment on MEXEC-6." );
324             waitFor( 0 );
325         }
326 
327         if ( cleanupDaemonThreads )
328         {
329         
330             terminateThreads( threadGroup );
331             
332             try
333             {
334                 threadGroup.destroy();
335             }
336             catch ( IllegalThreadStateException e )
337             {
338                 getLog().warn( "Couldn't destroy threadgroup " + threadGroup, e );
339             }
340         }
341         
342 
343         if ( originalSystemProperties != null )
344         {
345             System.setProperties( originalSystemProperties );
346         }
347 
348         synchronized ( threadGroup )
349         {
350             if ( threadGroup.uncaughtException != null )
351             {
352                 throw new MojoExecutionException( "An exception occured while executing the Java class. " 
353                                                   + threadGroup.uncaughtException.getMessage(),
354                                                   threadGroup.uncaughtException );
355             }
356         }
357 
358         registerSourceRoots();
359     }
360 
361     /**
362      * a ThreadGroup to isolate execution and collect exceptions.
363      */
364     class IsolatedThreadGroup extends ThreadGroup
365     {
366         private Throwable uncaughtException; // synchronize access to this
367 
368         public IsolatedThreadGroup( String name )
369         {
370             super( name );
371         }
372 
373         public void uncaughtException( Thread thread, Throwable throwable )
374         {
375             if ( throwable instanceof ThreadDeath )
376             {
377                 return; //harmless
378             }
379             synchronized ( this )
380             {
381                 if ( uncaughtException == null ) // only remember the first one
382                 {
383                     uncaughtException = throwable; // will be reported eventually
384                 }
385             }
386             getLog().warn( throwable );
387         }
388     }
389 
390     private void joinNonDaemonThreads( ThreadGroup threadGroup )
391     {
392         boolean foundNonDaemon;
393         do
394         {
395             foundNonDaemon = false;
396             Collection threads = getActiveThreads( threadGroup );
397             for ( Iterator iter = threads.iterator(); iter.hasNext(); )
398             {
399                 Thread thread = (Thread) iter.next();
400                 if ( thread.isDaemon() )
401                 {
402                     continue;
403                 }
404                 foundNonDaemon = true;   //try again; maybe more threads were created while we were busy
405                 joinThread( thread, 0 );
406             }
407         } while ( foundNonDaemon );
408     }
409 
410     private void joinThread( Thread thread, long timeoutMsecs )
411     {
412         try
413         {
414             getLog().debug( "joining on thread " + thread );
415             thread.join( timeoutMsecs );
416         }
417         catch ( InterruptedException e )
418         {
419             Thread.currentThread().interrupt();   // good practice if don't throw
420             getLog().warn( "interrupted while joining against thread " + thread, e );   // not expected!
421         }
422         if ( thread.isAlive() ) //generally abnormal
423         {
424             getLog().warn( "thread " + thread + " was interrupted but is still alive after waiting at least "
425                 + timeoutMsecs + "msecs" );
426         }
427     }
428 
429     private void terminateThreads( ThreadGroup threadGroup )
430     {
431         long startTime = System.currentTimeMillis();
432         Set uncooperativeThreads = new HashSet(); // these were not responsive to interruption
433         for ( Collection threads = getActiveThreads( threadGroup ); !threads.isEmpty();
434               threads = getActiveThreads( threadGroup ), threads.removeAll( uncooperativeThreads ) )
435         {
436             // Interrupt all threads we know about as of this instant (harmless if spuriously went dead (! isAlive())
437             //   or if something else interrupted it ( isInterrupted() ).
438             for ( Iterator iter = threads.iterator(); iter.hasNext(); )
439             {
440                 Thread thread = (Thread) iter.next();
441                 getLog().debug( "interrupting thread " + thread );
442                 thread.interrupt();
443             }
444             // Now join with a timeout and call stop() (assuming flags are set right)
445             for ( Iterator iter = threads.iterator(); iter.hasNext(); )
446             {
447                 Thread thread = (Thread) iter.next();
448                 if ( ! thread.isAlive() )
449                 {
450                     continue; //and, presumably it won't show up in getActiveThreads() next iteration
451                 }
452                 if ( daemonThreadJoinTimeout <= 0 )
453                 {
454                     joinThread( thread, 0 ); //waits until not alive; no timeout
455                     continue;
456                 }
457                 long timeout = daemonThreadJoinTimeout 
458                                - ( System.currentTimeMillis() - startTime );
459                 if ( timeout > 0 )
460                 {
461                     joinThread( thread, timeout );
462                 }
463                 if ( ! thread.isAlive() )
464                 {
465                     continue;
466                 }
467                 uncooperativeThreads.add( thread ); // ensure we don't process again
468                 if ( stopUnresponsiveDaemonThreads )
469                 {
470                     getLog().warn( "thread " + thread + " will be Thread.stop()'ed" );
471                     thread.stop();
472                 }
473                 else
474                 {
475                     getLog().warn( "thread " + thread + " will linger despite being asked to die via interruption" );
476                 }
477             }
478         }
479         if ( ! uncooperativeThreads.isEmpty() )
480         {
481             getLog().warn( "NOTE: " + uncooperativeThreads.size() + " thread(s) did not finish despite being asked to "
482                 + " via interruption. This is not a problem with exec:java, it is a problem with the running code."
483                 + " Although not serious, it should be remedied." );
484         }
485         else
486         {
487             int activeCount = threadGroup.activeCount();
488             if ( activeCount != 0 )
489             {
490                 // TODO this may be nothing; continue on anyway; perhaps don't even log in future
491                 Thread[] threadsArray = new Thread[1];
492                 threadGroup.enumerate( threadsArray );
493                 getLog().debug( "strange; " + activeCount
494                         + " thread(s) still active in the group " + threadGroup + " such as " + threadsArray[0] );
495             }
496         }
497     }
498 
499     private Collection getActiveThreads( ThreadGroup threadGroup )
500     {
501         Thread[] threads = new Thread[ threadGroup.activeCount() ];
502         int numThreads = threadGroup.enumerate( threads );
503         Collection result = new ArrayList( numThreads );
504         for ( int i = 0; i < threads.length && threads[i] != null; i++ )
505         {
506             result.add( threads[i] );
507         }
508         return result; //note: result should be modifiable
509     }
510 
511     /**
512      * Pass any given system properties to the java system properties.
513      */
514     private void setSystemProperties()
515     {
516         if ( systemProperties != null )
517         {
518             originalSystemProperties = System.getProperties();
519             for ( int i = 0; i < systemProperties.length; i++ )
520             {
521                 Property systemProperty = systemProperties[i];
522                 String value = systemProperty.getValue();
523                 System.setProperty( systemProperty.getKey(), value == null ? "" : value );
524             }
525         }
526     }
527 
528     /**
529      * Set up a classloader for the execution of the main class.
530      *
531      * @return the classloader
532      * @throws MojoExecutionException if a problem happens
533      */
534     private ClassLoader getClassLoader()
535         throws MojoExecutionException
536     {
537         List classpathURLs = new ArrayList();
538         this.addRelevantPluginDependenciesToClasspath( classpathURLs );
539         this.addRelevantProjectDependenciesToClasspath( classpathURLs );
540         return new URLClassLoader( ( URL[] ) classpathURLs.toArray( new URL[ classpathURLs.size() ] ) );
541     }
542 
543     /**
544      * Add any relevant project dependencies to the classpath.
545      * Indirectly takes includePluginDependencies and ExecutableDependency into consideration.
546      *
547      * @param path classpath of {@link java.net.URL} objects
548      * @throws MojoExecutionException if a problem happens
549      */
550     private void addRelevantPluginDependenciesToClasspath( List path )
551         throws MojoExecutionException
552     {
553         if ( hasCommandlineArgs() )
554         {
555             arguments = parseCommandlineArgs();
556         }
557 
558         try
559         {
560             Iterator iter = this.determineRelevantPluginDependencies().iterator();
561             while ( iter.hasNext() )
562             {
563                 Artifact classPathElement = (Artifact) iter.next();
564                 getLog().debug(
565                     "Adding plugin dependency artifact: " + classPathElement.getArtifactId() + " to classpath" );
566                 path.add( classPathElement.getFile().toURI().toURL() );
567             }
568         }
569         catch ( MalformedURLException e )
570         {
571             throw new MojoExecutionException( "Error during setting up classpath", e );
572         }
573 
574     }
575 
576     /**
577      * Add any relevant project dependencies to the classpath.
578      * Takes includeProjectDependencies into consideration.
579      *
580      * @param path classpath of {@link java.net.URL} objects
581      * @throws MojoExecutionException if a problem happens
582      */
583     private void addRelevantProjectDependenciesToClasspath( List path )
584         throws MojoExecutionException
585     {
586         if ( this.includeProjectDependencies )
587         {
588             try
589             {
590                 getLog().debug( "Project Dependencies will be included." );
591 
592                 List artifacts = new ArrayList();
593                 List theClasspathFiles = new ArrayList();
594  
595                 collectProjectArtifactsAndClasspath( artifacts, theClasspathFiles );
596 
597                 for ( Iterator it = theClasspathFiles.iterator(); it.hasNext(); )
598                 {
599                      URL url = ( (File) it.next() ).toURI().toURL();
600                      getLog().debug( "Adding to classpath : " + url );
601                      path.add( url );
602                 }
603 
604                 Iterator iter = artifacts.iterator();
605                 while ( iter.hasNext() )
606                 {
607                     Artifact classPathElement = (Artifact) iter.next();
608                     getLog().debug(
609                         "Adding project dependency artifact: " + classPathElement.getArtifactId() + " to classpath" );
610                     path.add( classPathElement.getFile().toURI().toURL() );
611                 }
612 
613             }
614             catch ( MalformedURLException e )
615             {
616                 throw new MojoExecutionException( "Error during setting up classpath", e );
617             }
618         }
619         else
620         {
621             getLog().debug( "Project Dependencies will be excluded." );
622         }
623 
624     }
625 
626     /**
627      * Determine all plugin dependencies relevant to the executable.
628      * Takes includePlugins, and the executableDependency into consideration.
629      *
630      * @return a set of Artifact objects.
631      *         (Empty set is returned if there are no relevant plugin dependencies.)
632      * @throws MojoExecutionException if a problem happens resolving the plufin dependencies
633      */
634     private Set determineRelevantPluginDependencies()
635         throws MojoExecutionException
636     {
637         Set relevantDependencies;
638         if ( this.includePluginDependencies )
639         {
640             if ( this.executableDependency == null )
641             {
642                 getLog().debug( "All Plugin Dependencies will be included." );
643                 relevantDependencies = new HashSet( this.pluginDependencies );
644             }
645             else
646             {
647                 getLog().debug( "Selected plugin Dependencies will be included." );
648                 Artifact executableArtifact = this.findExecutableArtifact();
649                 Artifact executablePomArtifact = this.getExecutablePomArtifact( executableArtifact );
650                 relevantDependencies = this.resolveExecutableDependencies( executablePomArtifact );
651             }
652         }
653         else
654         {
655             relevantDependencies = Collections.EMPTY_SET;
656             getLog().debug( "Plugin Dependencies will be excluded." );
657         }
658         return relevantDependencies;
659     }
660 
661     /**
662      * Get the artifact which refers to the POM of the executable artifact.
663      *
664      * @param executableArtifact this artifact refers to the actual assembly.
665      * @return an artifact which refers to the POM of the executable artifact.
666      */
667     private Artifact getExecutablePomArtifact( Artifact executableArtifact )
668     {
669         return this.artifactFactory.createBuildArtifact( executableArtifact.getGroupId(),
670                                                          executableArtifact.getArtifactId(),
671                                                          executableArtifact.getVersion(), "pom" );
672     }
673 
674     /**
675      * Examine the plugin dependencies to find the executable artifact.
676      *
677      * @return an artifact which refers to the actual executable tool (not a POM)
678      * @throws MojoExecutionException if no executable artifact was found
679      */
680     private Artifact findExecutableArtifact()
681         throws MojoExecutionException
682     {
683         //ILimitedArtifactIdentifier execToolAssembly = this.getExecutableToolAssembly();
684 
685         Artifact executableTool = null;
686         for ( Iterator iter = this.pluginDependencies.iterator(); iter.hasNext(); )
687         {
688             Artifact pluginDep = (Artifact) iter.next();
689             if ( this.executableDependency.matches( pluginDep ) )
690             {
691                 executableTool = pluginDep;
692                 break;
693             }
694         }
695 
696         if ( executableTool == null )
697         {
698             throw new MojoExecutionException(
699                 "No dependency of the plugin matches the specified executableDependency."
700                 + "  Specified executableToolAssembly is: " + executableDependency.toString() );
701         }
702 
703         return executableTool;
704     }
705 
706     /**
707      * Resolve the executable dependencies for the specified project
708      * @param executablePomArtifact the project's POM
709      * @return a set of Artifacts
710      * @throws MojoExecutionException if a failure happens
711      */
712     private Set resolveExecutableDependencies( Artifact executablePomArtifact )
713         throws MojoExecutionException
714     {
715 
716         Set executableDependencies;
717         try
718         {
719             MavenProject executableProject = this.projectBuilder.buildFromRepository( executablePomArtifact,
720                                                                                       this.remoteRepositories,
721                                                                                       this.localRepository );
722 
723             //get all of the dependencies for the executable project
724             List dependencies = executableProject.getDependencies();
725 
726             //make Artifacts of all the dependencies
727             Set dependencyArtifacts =
728                 MavenMetadataSource.createArtifacts( this.artifactFactory, dependencies, null, null, null );
729 
730             //not forgetting the Artifact of the project itself
731             dependencyArtifacts.add( executableProject.getArtifact() );
732 
733             //resolve all dependencies transitively to obtain a comprehensive list of assemblies
734             ArtifactResolutionResult result = artifactResolver.resolveTransitively( dependencyArtifacts,
735                                                                                     executablePomArtifact,
736                                                                                     Collections.EMPTY_MAP,
737                                                                                     this.localRepository,
738                                                                                     this.remoteRepositories,
739                                                                                     metadataSource, null,
740                                                                                     Collections.EMPTY_LIST );
741             executableDependencies = result.getArtifacts();
742 
743         }
744         catch ( Exception ex )
745         {
746             throw new MojoExecutionException(
747                 "Encountered problems resolving dependencies of the executable " + "in preparation for its execution.",
748                 ex );
749         }
750 
751         return executableDependencies;
752     }
753 
754     /**
755      * Stop program execution for nn millis.
756      *
757      * @param millis the number of millis-seconds to wait for,
758      *               <code>0</code> stops program forever.
759      */
760     private void waitFor( long millis )
761     {
762         Object lock = new Object();
763         synchronized ( lock )
764         {
765             try
766             {
767                 lock.wait( millis );
768             }
769             catch ( InterruptedException e )
770             {
771                 Thread.currentThread().interrupt(); // good practice if don't throw
772                 getLog().warn( "Spuriously interrupted while waiting for " + millis + "ms", e );
773             }
774         }
775     }
776 
777 }