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 &lt;=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 &lt;= 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    }