View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugin;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.PrintStream;
23  import java.util.List;
24  import javax.inject.Inject;
25  import javax.inject.Named;
26  import javax.inject.Singleton;
27  import org.apache.maven.api.Project;
28  import org.apache.maven.api.plugin.MojoException;
29  import org.apache.maven.execution.MavenSession;
30  import org.apache.maven.execution.MojoExecutionEvent;
31  import org.apache.maven.execution.MojoExecutionListener;
32  import org.apache.maven.execution.scope.internal.MojoExecutionScope;
33  import org.apache.maven.internal.impl.DefaultLog;
34  import org.apache.maven.internal.impl.DefaultMojoExecution;
35  import org.apache.maven.internal.impl.DefaultSession;
36  import org.apache.maven.model.Plugin;
37  import org.apache.maven.plugin.descriptor.MojoDescriptor;
38  import org.apache.maven.plugin.descriptor.PluginDescriptor;
39  import org.apache.maven.plugin.logging.Log;
40  import org.apache.maven.project.MavenProject;
41  import org.codehaus.plexus.classworlds.realm.ClassRealm;
42  import org.eclipse.aether.RepositorySystemSession;
43  import org.eclipse.aether.repository.RemoteRepository;
44  import org.slf4j.LoggerFactory;
45  
46  // TODO the antrun plugin has its own configurator, the only plugin that does. might need to think about how that works
47  // TODO remove the coreArtifactFilterManager
48  
49  /**
50   * DefaultBuildPluginManager
51   */
52  @Named
53  @Singleton
54  public class DefaultBuildPluginManager implements BuildPluginManager {
55  
56      private final MavenPluginManager mavenPluginManager;
57      private final LegacySupport legacySupport;
58      private final MojoExecutionScope scope;
59      private final MojoExecutionListener mojoExecutionListener;
60  
61      @Inject
62      public DefaultBuildPluginManager(
63              MavenPluginManager mavenPluginManager,
64              LegacySupport legacySupport,
65              MojoExecutionScope scope,
66              List<MojoExecutionListener> mojoExecutionListeners) {
67          this.mavenPluginManager = mavenPluginManager;
68          this.legacySupport = legacySupport;
69          this.scope = scope;
70          this.mojoExecutionListener = new CompoundMojoExecutionListener(mojoExecutionListeners);
71      }
72  
73      /**
74       * @param plugin
75       * @param repositories
76       * @param session
77       * @return PluginDescriptor The component descriptor for the Maven plugin.
78       * @throws PluginNotFoundException The plugin could not be found in any repositories.
79       * @throws PluginResolutionException The plugin could be found but could not be resolved.
80       * @throws InvalidPluginDescriptorException
81       */
82      public PluginDescriptor loadPlugin(
83              Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session)
84              throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
85                      InvalidPluginDescriptorException {
86          return mavenPluginManager.getPluginDescriptor(plugin, repositories, session);
87      }
88  
89      // ----------------------------------------------------------------------
90      // Mojo execution
91      // ----------------------------------------------------------------------
92  
93      public void executeMojo(MavenSession session, MojoExecution mojoExecution)
94              throws MojoFailureException, MojoExecutionException, PluginConfigurationException, PluginManagerException {
95          MavenProject project = session.getCurrentProject();
96  
97          MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
98  
99          Mojo mojo = null;
100 
101         ClassRealm pluginRealm;
102         try {
103             pluginRealm = getPluginRealm(session, mojoDescriptor.getPluginDescriptor());
104         } catch (PluginResolutionException e) {
105             throw new PluginExecutionException(mojoExecution, project, e);
106         }
107 
108         ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
109         Thread.currentThread().setContextClassLoader(pluginRealm);
110 
111         MavenSession oldSession = legacySupport.getSession();
112 
113         scope.enter();
114 
115         try {
116             scope.seed(MavenProject.class, project);
117             scope.seed(MojoExecution.class, mojoExecution);
118             scope.seed(
119                     org.apache.maven.api.plugin.Log.class,
120                     new DefaultLog(LoggerFactory.getLogger(
121                             mojoExecution.getMojoDescriptor().getFullGoalName())));
122             scope.seed(Project.class, ((DefaultSession) session.getSession()).getProject(project));
123             scope.seed(org.apache.maven.api.MojoExecution.class, new DefaultMojoExecution(mojoExecution));
124 
125             if (mojoDescriptor.isV4Api()) {
126                 org.apache.maven.api.plugin.Mojo mojoV4 = mavenPluginManager.getConfiguredMojo(
127                         org.apache.maven.api.plugin.Mojo.class, session, mojoExecution);
128                 mojo = new MojoWrapper(mojoV4);
129             } else {
130                 mojo = mavenPluginManager.getConfiguredMojo(Mojo.class, session, mojoExecution);
131             }
132 
133             legacySupport.setSession(session);
134 
135             // NOTE: DuplicateArtifactAttachmentException is currently unchecked, so be careful removing this try/catch!
136             // This is necessary to avoid creating compatibility problems for existing plugins that use
137             // MavenProjectHelper.attachArtifact(..).
138             try {
139                 MojoExecutionEvent mojoExecutionEvent = new MojoExecutionEvent(session, project, mojoExecution, mojo);
140                 mojoExecutionListener.beforeMojoExecution(mojoExecutionEvent);
141                 mojo.execute();
142                 mojoExecutionListener.afterMojoExecutionSuccess(mojoExecutionEvent);
143             } catch (ClassCastException e) {
144                 // to be processed in the outer catch block
145                 throw e;
146             } catch (RuntimeException e) {
147                 throw new PluginExecutionException(mojoExecution, project, e);
148             }
149         } catch (PluginContainerException e) {
150             mojoExecutionListener.afterExecutionFailure(
151                     new MojoExecutionEvent(session, project, mojoExecution, mojo, e));
152             throw new PluginExecutionException(mojoExecution, project, e);
153         } catch (NoClassDefFoundError e) {
154             mojoExecutionListener.afterExecutionFailure(
155                     new MojoExecutionEvent(session, project, mojoExecution, mojo, e));
156             ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
157             PrintStream ps = new PrintStream(os);
158             ps.println(
159                     "A required class was missing while executing " + mojoDescriptor.getId() + ": " + e.getMessage());
160             pluginRealm.display(ps);
161             Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e);
162             throw new PluginExecutionException(mojoExecution, project, wrapper);
163         } catch (LinkageError e) {
164             mojoExecutionListener.afterExecutionFailure(
165                     new MojoExecutionEvent(session, project, mojoExecution, mojo, e));
166             ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
167             PrintStream ps = new PrintStream(os);
168             ps.println("An API incompatibility was encountered while executing " + mojoDescriptor.getId() + ": "
169                     + e.getClass().getName() + ": " + e.getMessage());
170             pluginRealm.display(ps);
171             Exception wrapper = new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), e);
172             throw new PluginExecutionException(mojoExecution, project, wrapper);
173         } catch (ClassCastException e) {
174             mojoExecutionListener.afterExecutionFailure(
175                     new MojoExecutionEvent(session, project, mojoExecution, mojo, e));
176             ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
177             PrintStream ps = new PrintStream(os);
178             ps.println("A type incompatibility occurred while executing " + mojoDescriptor.getId() + ": "
179                     + e.getMessage());
180             pluginRealm.display(ps);
181             throw new PluginExecutionException(mojoExecution, project, os.toString(), e);
182         } catch (RuntimeException e) {
183             mojoExecutionListener.afterExecutionFailure(
184                     new MojoExecutionEvent(session, project, mojoExecution, mojo, e));
185             throw e;
186         } finally {
187             mavenPluginManager.releaseMojo(mojo, mojoExecution);
188             scope.exit();
189             Thread.currentThread().setContextClassLoader(oldClassLoader);
190             legacySupport.setSession(oldSession);
191         }
192     }
193 
194     /**
195      * TODO pluginDescriptor classRealm and artifacts are set as a side effect of this
196      *      call, which is not nice.
197      * @throws PluginResolutionException
198      */
199     public ClassRealm getPluginRealm(MavenSession session, PluginDescriptor pluginDescriptor)
200             throws PluginResolutionException, PluginManagerException {
201         ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
202         if (pluginRealm != null) {
203             return pluginRealm;
204         }
205 
206         mavenPluginManager.setupPluginRealm(pluginDescriptor, session, null, null, null);
207 
208         return pluginDescriptor.getClassRealm();
209     }
210 
211     public MojoDescriptor getMojoDescriptor(
212             Plugin plugin, String goal, List<RemoteRepository> repositories, RepositorySystemSession session)
213             throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
214                     MojoNotFoundException, InvalidPluginDescriptorException {
215         return mavenPluginManager.getMojoDescriptor(plugin, goal, repositories, session);
216     }
217 
218     private static class MojoWrapper implements Mojo {
219         private final org.apache.maven.api.plugin.Mojo mojoV4;
220 
221         MojoWrapper(org.apache.maven.api.plugin.Mojo mojoV4) {
222             this.mojoV4 = mojoV4;
223         }
224 
225         @Override
226         public void execute() throws MojoExecutionException, MojoFailureException {
227             try {
228                 mojoV4.execute();
229             } catch (MojoException e) {
230                 throw new MojoExecutionException(e.getMessage(), e);
231             }
232         }
233 
234         @Override
235         public void setLog(Log log) {}
236 
237         @Override
238         public Log getLog() {
239             return null;
240         }
241     }
242 }