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