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