001package org.apache.maven.plugin.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 org.apache.maven.artifact.Artifact;
023import org.apache.maven.artifact.repository.ArtifactRepository;
024import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
025import org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter;
026import org.apache.maven.plugin.AbstractMojo;
027import org.apache.maven.plugin.MojoExecutionException;
028import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
029import org.apache.maven.plugin.descriptor.PluginDescriptor;
030import org.apache.maven.plugins.annotations.Component;
031import org.apache.maven.plugins.annotations.Parameter;
032import org.apache.maven.project.MavenProject;
033import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
034import org.apache.maven.tools.plugin.PluginToolsRequest;
035import org.apache.maven.tools.plugin.extractor.ExtractionException;
036import org.apache.maven.tools.plugin.generator.Generator;
037import org.apache.maven.tools.plugin.generator.GeneratorException;
038import org.apache.maven.tools.plugin.generator.GeneratorUtils;
039import org.apache.maven.tools.plugin.scanner.MojoScanner;
040import org.codehaus.plexus.component.repository.ComponentDependency;
041import org.codehaus.plexus.util.ReaderFactory;
042
043import java.io.File;
044import java.util.Arrays;
045import java.util.LinkedHashSet;
046import java.util.List;
047import java.util.Set;
048
049/**
050 * Abstract class for this Plugin.
051 *
052 * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
053 * @version $Id: AbstractGeneratorMojo.html 1030109 2018-05-20 14:45:18Z hboutemy $
054 */
055public abstract class AbstractGeneratorMojo
056    extends AbstractMojo
057{
058    /**
059     * The project currently being built.
060     */
061    @Parameter( defaultValue = "${project}", readonly = true )
062    protected MavenProject project;
063
064    /**
065     * The component used for scanning the source tree for mojos.
066     */
067    @Component
068    protected MojoScanner mojoScanner;
069
070    /**
071     * The file encoding of the source files.
072     *
073     * @since 2.5
074     */
075    @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
076    protected String encoding;
077
078    /**
079     * The goal prefix that will appear before the ":".
080     */
081    @Parameter
082    protected String goalPrefix;
083
084    /**
085     * By default an exception is throw if no mojo descriptor is found. As the maven-plugin is defined in core, the
086     * descriptor generator mojo is bound to generate-resources phase.
087     * But for annotations, the compiled classes are needed, so skip error
088     *
089     * @since 3.0
090     */
091    @Parameter( property = "maven.plugin.skipErrorNoDescriptorsFound", defaultValue = "false" )
092    protected boolean skipErrorNoDescriptorsFound;
093
094    /**
095     * The role names of mojo extractors to use.
096     * <p/>
097     * If not set, all mojo extractors will be used. If set to an empty extractor name, no mojo extractors
098     * will be used.
099     * <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}