View Javadoc
1   package org.apache.maven.plugin.plugin;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.repository.ArtifactRepository;
24  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
25  import org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter;
26  import org.apache.maven.plugin.AbstractMojo;
27  import org.apache.maven.plugin.MojoExecutionException;
28  import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
29  import org.apache.maven.plugin.descriptor.PluginDescriptor;
30  import org.apache.maven.plugins.annotations.Component;
31  import org.apache.maven.plugins.annotations.Parameter;
32  import org.apache.maven.project.MavenProject;
33  import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
34  import org.apache.maven.tools.plugin.PluginToolsRequest;
35  import org.apache.maven.tools.plugin.extractor.ExtractionException;
36  import org.apache.maven.tools.plugin.generator.Generator;
37  import org.apache.maven.tools.plugin.generator.GeneratorException;
38  import org.apache.maven.tools.plugin.generator.GeneratorUtils;
39  import org.apache.maven.tools.plugin.scanner.MojoScanner;
40  import org.codehaus.plexus.component.repository.ComponentDependency;
41  import org.codehaus.plexus.util.ReaderFactory;
42  
43  import java.io.File;
44  import java.util.Arrays;
45  import java.util.LinkedHashSet;
46  import java.util.List;
47  import java.util.Set;
48  
49  /**
50   * Abstract class for this Plugin.
51   *
52   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
53   * @version $Id: AbstractGeneratorMojo.html 1024032 2018-01-19 18:16:30Z hboutemy $
54   */
55  public abstract class AbstractGeneratorMojo
56      extends AbstractMojo
57  {
58      /**
59       * The project currently being built.
60       */
61      @Parameter( defaultValue = "${project}", readonly = true )
62      protected MavenProject project;
63  
64      /**
65       * The component used for scanning the source tree for mojos.
66       */
67      @Component
68      protected MojoScanner mojoScanner;
69  
70      /**
71       * The file encoding of the source files.
72       *
73       * @since 2.5
74       */
75      @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
76      protected String encoding;
77  
78      /**
79       * The goal prefix that will appear before the ":".
80       */
81      @Parameter
82      protected String goalPrefix;
83  
84      /**
85       * By default an exception is throw if no mojo descriptor is found. As the maven-plugin is defined in core, the
86       * descriptor generator mojo is bound to generate-resources phase.
87       * But for annotations, the compiled classes are needed, so skip error
88       *
89       * @since 3.0
90       */
91      @Parameter( property = "maven.plugin.skipErrorNoDescriptorsFound", defaultValue = "false" )
92      protected boolean skipErrorNoDescriptorsFound;
93  
94      /**
95       * The role names of mojo extractors to use.
96       * <p/>
97       * If not set, all mojo extractors will be used. If set to an empty extractor name, no mojo extractors
98       * will be used.
99       * <p/>
100      * Example:
101      * <p/>
102      * <pre>
103      *  &lt;!-- Use all mojo extractors --&gt;
104      *  &lt;extractors/&gt;
105      *
106      *  &lt;!-- Use no mojo extractors --&gt;
107      *  &lt;extractors&gt;
108      *      &lt;extractor/&gt;
109      *  &lt;/extractors&gt;
110      *
111      *  &lt;!-- Use only bsh mojo extractor --&gt;
112      *  &lt;extractors&gt;
113      *      &lt;extractor&gt;bsh&lt;/extractor&gt;
114      *  &lt;/extractors&gt;
115      * </pre>
116      */
117     @Parameter
118     protected Set<String> extractors;
119 
120     /**
121      * Set this to "true" to skip invoking any goals or reports of the plugin.
122      *
123      * @since 2.8
124      */
125     @Parameter( defaultValue = "false", property = "maven.plugin.skip" )
126     protected boolean skip;
127 
128     /**
129      * The set of dependencies for the current project
130      *
131      * @since 3.0
132      */
133     @Parameter( defaultValue = "${project.artifacts}", required = true, readonly = true )
134     protected Set<Artifact> dependencies;
135     
136     /**
137      * Specify the dependencies as {@code groupId:artifactId} containing (abstract) Mojos, to filter
138      * dependencies scanned at runtime and focus on dependencies that are really useful to Mojo analysis.
139      * By default, the value is {@code null} and all dependencies are scanned (as before this parameter was added).
140      * If specified in the configuration with no children, no dependencies are scanned.
141      * 
142      * @since 3.5
143      */
144     @Parameter
145     private List<String> mojoDependencies;
146 
147     /**
148      * List of Remote Repositories used by the resolver
149      *
150      * @since 3.0
151      */
152     @Parameter( defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true )
153     protected List<ArtifactRepository> remoteRepos;
154 
155     /**
156      * Location of the local repository.
157      *
158      * @since 3.0
159      */
160     @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
161     protected ArtifactRepository local;
162 
163     /**
164      * Maven plugin packaging types. Default is single "maven-plugin".
165      * 
166      * @since 3.3
167      */
168     @Parameter
169     protected List<String> packagingTypes = Arrays.asList( "maven-plugin" );
170 
171     /**
172      * @return the output directory where files will be generated.
173      */
174     protected abstract File getOutputDirectory();
175 
176     /**
177      * @return the wanted <code>Generator</code> implementation.
178      */
179     protected abstract Generator createGenerator();
180 
181     /**
182      * {@inheritDoc}
183      */
184     public void execute()
185         throws MojoExecutionException
186     {
187         if ( !packagingTypes.contains( project.getPackaging() ) )
188         {
189             getLog().warn( "Unsupported packaging type " + project.getPackaging() + ", execution skipped" );
190             return;
191         }
192         if ( skip )
193         {
194             getLog().warn( "Execution skipped" );
195             return;
196         }
197 
198         if ( project.getArtifactId().toLowerCase().startsWith( "maven-" )
199             && project.getArtifactId().toLowerCase().endsWith( "-plugin" ) && !"org.apache.maven.plugins".equals(
200             project.getGroupId() ) )
201         {
202             getLog().error( "\n\nArtifact Ids of the format maven-___-plugin are reserved for \n"
203                                 + "plugins in the Group Id org.apache.maven.plugins\n"
204                                 + "Please change your artifactId to the format ___-maven-plugin\n"
205                                 + "In the future this error will break the build.\n\n" );
206         }
207 
208         String defaultGoalPrefix = PluginDescriptor.getGoalPrefixFromArtifactId( project.getArtifactId() );
209         if ( goalPrefix == null )
210         {
211             goalPrefix = defaultGoalPrefix;
212         }
213         else if ( !goalPrefix.equals( defaultGoalPrefix ) )
214         {
215             getLog().warn(
216                 "\n\nGoal prefix is specified as: '" + goalPrefix + "'. " + "Maven currently expects it to be '"
217                     + defaultGoalPrefix + "'.\n" );
218         }
219 
220         mojoScanner.setActiveExtractors( extractors );
221 
222         // TODO: could use this more, eg in the writing of the plugin descriptor!
223         PluginDescriptor pluginDescriptor = new PluginDescriptor();
224 
225         pluginDescriptor.setGroupId( project.getGroupId() );
226 
227         pluginDescriptor.setArtifactId( project.getArtifactId() );
228 
229         pluginDescriptor.setVersion( project.getVersion() );
230 
231         pluginDescriptor.setGoalPrefix( goalPrefix );
232 
233         pluginDescriptor.setName( project.getName() );
234 
235         pluginDescriptor.setDescription( project.getDescription() );
236 
237         if ( encoding == null || encoding.length() < 1 )
238         {
239             getLog().warn( "Using platform encoding (" + ReaderFactory.FILE_ENCODING
240                                + " actually) to read mojo source files, i.e. build is platform dependent!" );
241         }
242         else
243         {
244             getLog().info( "Using '" + encoding + "' encoding to read mojo source files." );
245         }
246 
247         try
248         {
249             List<ComponentDependency> deps = GeneratorUtils.toComponentDependencies( project.getRuntimeDependencies() );
250             pluginDescriptor.setDependencies( deps );
251 
252             PluginToolsRequest request = new DefaultPluginToolsRequest( project, pluginDescriptor );
253             request.setEncoding( encoding );
254             request.setSkipErrorNoDescriptorsFound( skipErrorNoDescriptorsFound );
255             request.setDependencies( filterMojoDependencies() );
256             request.setLocal( this.local );
257             request.setRemoteRepos( this.remoteRepos );
258 
259             mojoScanner.populatePluginDescriptor( request );
260 
261             getOutputDirectory().mkdirs();
262 
263             createGenerator().execute( getOutputDirectory(), request );
264         }
265         catch ( GeneratorException e )
266         {
267             throw new MojoExecutionException( "Error writing plugin descriptor", e );
268         }
269         catch ( InvalidPluginDescriptorException e )
270         {
271             throw new MojoExecutionException( "Error extracting plugin descriptor: \'" + e.getLocalizedMessage() + "\'",
272                                               e );
273         }
274         catch ( ExtractionException e )
275         {
276             throw new MojoExecutionException( "Error extracting plugin descriptor: \'" + e.getLocalizedMessage() + "\'",
277                                               e );
278         }
279         catch ( LinkageError e )
280         {
281             throw new MojoExecutionException( "The API of the mojo scanner is not compatible with this plugin version."
282                 + " Please check the plugin dependencies configured in the POM and ensure the versions match.", e );
283         }
284     }
285 
286     /**
287      * Get dependencies filtered with mojoDependencies configuration.
288      * 
289      * @return eventually filtered dependencies, or even <code>null</code> if configured with empty mojoDependencies
290      * list
291      * @see #mojoDependencies
292      */
293     private Set<Artifact> filterMojoDependencies()
294     {
295         Set<Artifact> filteredDependencies;
296         if ( mojoDependencies == null )
297         {
298             filteredDependencies = dependencies;
299         }
300         else if ( mojoDependencies.size() == 0 )
301         {
302             filteredDependencies = null;
303         }
304         else
305         {
306             filteredDependencies = new LinkedHashSet<Artifact>();
307             
308             ArtifactFilter filter = new IncludesArtifactFilter( mojoDependencies );
309 
310             for ( Artifact artifact : dependencies )
311             {
312                 if ( filter.include( artifact ) )
313                 {
314                     filteredDependencies.add( artifact );
315                 }
316             }
317         }
318 
319         return filteredDependencies;
320     }
321 }