001package org.apache.maven.plugin;
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
022import java.io.ByteArrayOutputStream;
023import java.io.PrintStream;
024import java.util.List;
025
026import org.apache.maven.execution.MavenSession;
027import org.apache.maven.execution.MojoExecutionEvent;
028import org.apache.maven.execution.MojoExecutionListener;
029import org.apache.maven.execution.scope.internal.MojoExecutionScope;
030import org.apache.maven.model.Plugin;
031import org.apache.maven.plugin.descriptor.MojoDescriptor;
032import org.apache.maven.plugin.descriptor.PluginDescriptor;
033import org.apache.maven.project.MavenProject;
034import org.codehaus.plexus.classworlds.realm.ClassRealm;
035import org.codehaus.plexus.component.annotations.Component;
036import org.codehaus.plexus.component.annotations.Requirement;
037import org.eclipse.aether.RepositorySystemSession;
038import org.eclipse.aether.repository.RemoteRepository;
039
040// TODO: the antrun plugin has its own configurator, the only plugin that does. might need to think about how that works
041// TODO: remove the coreArtifactFilterManager
042
043@Component( role = BuildPluginManager.class )
044public class DefaultBuildPluginManager
045    implements BuildPluginManager
046{
047
048    @Requirement
049    private MavenPluginManager mavenPluginManager;
050
051    @Requirement
052    private LegacySupport legacySupport;
053
054    @Requirement
055    private MojoExecutionScope scope;
056
057    private MojoExecutionListener mojoExecutionListener;
058
059    // this tricks plexus-component-metadata generate required metadata
060    @Requirement( role = MojoExecutionListener.class )
061    private List<MojoExecutionListener> mojoExecutionListeners;
062
063    public void setMojoExecutionListeners( final List<MojoExecutionListener> listeners )
064    {
065        this.mojoExecutionListeners = listeners;
066        this.mojoExecutionListener = new CompoundMojoExecutionListener( listeners );
067    }
068
069    /**
070     * @param plugin
071     * @param repositories
072     * @param session
073     * @return PluginDescriptor The component descriptor for the Maven plugin.
074     * @throws PluginNotFoundException The plugin could not be found in any repositories.
075     * @throws PluginResolutionException The plugin could be found but could not be resolved.
076     * @throws InvalidPluginDescriptorException 
077     */
078    public PluginDescriptor loadPlugin( Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session )
079        throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException
080    {
081        return mavenPluginManager.getPluginDescriptor( plugin, repositories, session );
082    }
083
084    // ----------------------------------------------------------------------
085    // Mojo execution
086    // ----------------------------------------------------------------------
087
088    public void executeMojo( MavenSession session, MojoExecution mojoExecution )
089        throws MojoFailureException, MojoExecutionException, PluginConfigurationException, PluginManagerException
090    {
091        MavenProject project = session.getCurrentProject();
092
093        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
094
095        Mojo mojo = null;
096
097        ClassRealm pluginRealm;
098        try
099        {
100            pluginRealm = getPluginRealm( session, mojoDescriptor.getPluginDescriptor() );
101        }
102        catch ( PluginResolutionException e )
103        {
104            throw new PluginExecutionException( mojoExecution, project, e );
105        }
106
107        ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
108        Thread.currentThread().setContextClassLoader( pluginRealm );
109
110        MavenSession oldSession = legacySupport.getSession();
111
112        scope.enter();
113
114        try
115        {
116            scope.seed( MavenSession.class, session );
117            scope.seed( MavenProject.class, project );
118            scope.seed( MojoExecution.class, mojoExecution );
119            
120            mojo = mavenPluginManager.getConfiguredMojo( Mojo.class, session, mojoExecution );
121
122            legacySupport.setSession( session );
123
124            // NOTE: DuplicateArtifactAttachmentException is currently unchecked, so be careful removing this try/catch!
125            // This is necessary to avoid creating compatibility problems for existing plugins that use
126            // MavenProjectHelper.attachArtifact(..).
127            try
128            {
129                MojoExecutionEvent mojoExecutionEvent = new MojoExecutionEvent( session, project, mojoExecution, mojo );
130
131                mojoExecutionListener.beforeMojoExecution( mojoExecutionEvent );
132
133                mojo.execute();
134
135                mojoExecutionListener.afterMojoExecutionSuccess( mojoExecutionEvent );
136            }
137            catch ( ClassCastException e )
138            {
139                // to be processed in the outer catch block
140                throw e;
141            }
142            catch ( RuntimeException e )
143            {
144                throw new PluginExecutionException( mojoExecution, project, e );
145            }
146        }
147        catch ( PluginContainerException e )
148        {
149            mojoExecutionListener.afterExecutionFailure( new MojoExecutionEvent( session, project, mojoExecution, mojo,
150                                                                                 e ) );
151
152            throw new PluginExecutionException( mojoExecution, project, e );
153        }
154        catch ( NoClassDefFoundError e )
155        {
156            mojoExecutionListener.afterExecutionFailure( new MojoExecutionEvent( session, project, mojoExecution, mojo,
157                                                                                 e ) );
158
159            ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
160            PrintStream ps = new PrintStream( os );
161            ps.println( "A required class was missing while executing " + mojoDescriptor.getId() + ": "
162                + e.getMessage() );
163            pluginRealm.display( ps );
164
165            Exception wrapper = new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), e );
166
167            throw new PluginExecutionException( mojoExecution, project, wrapper );
168        }
169        catch ( LinkageError e )
170        {
171            mojoExecutionListener.afterExecutionFailure( new MojoExecutionEvent( session, project, mojoExecution, mojo,
172                                                                                 e ) );
173
174            ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
175            PrintStream ps = new PrintStream( os );
176            ps.println( "An API incompatibility was encountered while executing " + mojoDescriptor.getId() + ": "
177                + e.getClass().getName() + ": " + e.getMessage() );
178            pluginRealm.display( ps );
179
180            Exception wrapper = new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), e );
181
182            throw new PluginExecutionException( mojoExecution, project, wrapper );
183        }
184        catch ( ClassCastException e )
185        {
186            mojoExecutionListener.afterExecutionFailure( new MojoExecutionEvent( session, project, mojoExecution, mojo,
187                                                                                 e ) );
188
189            ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
190            PrintStream ps = new PrintStream( os );
191            ps.println( "A type incompatibility occured while executing " + mojoDescriptor.getId() + ": "
192                + e.getMessage() );
193            pluginRealm.display( ps );
194
195            throw new PluginExecutionException( mojoExecution, project, os.toString(), e );
196        }
197        catch ( RuntimeException e )
198        {
199            mojoExecutionListener.afterExecutionFailure( new MojoExecutionEvent( session, project, mojoExecution, mojo,
200                                                                                 e ) );
201
202            throw e;
203        }
204        finally
205        {
206            mavenPluginManager.releaseMojo( mojo, mojoExecution );
207
208            scope.exit();
209
210            Thread.currentThread().setContextClassLoader( oldClassLoader );
211
212            legacySupport.setSession( oldSession );
213        }
214    }
215
216    /**
217     * TODO pluginDescriptor classRealm and artifacts are set as a side effect of this
218     *      call, which is not nice.
219     * @throws PluginResolutionException 
220     */
221    public ClassRealm getPluginRealm( MavenSession session, PluginDescriptor pluginDescriptor ) 
222        throws PluginResolutionException, PluginManagerException
223    {
224        ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
225        if ( pluginRealm != null )
226        {
227            return pluginRealm;
228        }
229
230        mavenPluginManager.setupPluginRealm( pluginDescriptor, session, null, null, null );
231
232        return pluginDescriptor.getClassRealm();
233    }
234
235    public MojoDescriptor getMojoDescriptor( Plugin plugin, String goal, List<RemoteRepository> repositories,
236                                             RepositorySystemSession session )
237        throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
238        MojoNotFoundException, InvalidPluginDescriptorException
239    {
240        return mavenPluginManager.getMojoDescriptor( plugin, goal, repositories, session );
241    }
242
243}