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.lifecycle.internal;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.Collection;
26  import java.util.Set;
27  import java.util.stream.Collectors;
28  import java.util.stream.Stream;
29  
30  import org.apache.maven.api.services.MessageBuilder;
31  import org.apache.maven.api.services.MessageBuilderFactory;
32  import org.apache.maven.api.xml.XmlNode;
33  import org.apache.maven.internal.impl.DefaultMessageBuilderFactory;
34  import org.apache.maven.internal.xml.XmlNodeImpl;
35  import org.apache.maven.lifecycle.MojoExecutionConfigurator;
36  import org.apache.maven.model.Plugin;
37  import org.apache.maven.model.PluginExecution;
38  import org.apache.maven.plugin.MojoExecution;
39  import org.apache.maven.plugin.descriptor.MojoDescriptor;
40  import org.apache.maven.plugin.descriptor.Parameter;
41  import org.apache.maven.project.MavenProject;
42  import org.codehaus.plexus.util.xml.Xpp3Dom;
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  import static java.util.Arrays.stream;
47  import static java.util.Objects.requireNonNull;
48  
49  /**
50   * @since 3.3.1, MNG-5753
51   */
52  @Named
53  @Singleton
54  public class DefaultMojoExecutionConfigurator implements MojoExecutionConfigurator {
55      private final Logger logger = LoggerFactory.getLogger(getClass());
56  
57      private final MessageBuilderFactory messageBuilderFactory;
58  
59      /**
60       * Default ctor is used in IT and most probably some 3rd party code. For those cases, we do provide sane defaults
61       * but given this is a component, injection should be used, replacing direct instantiation.
62       *
63       * @deprecated Do not use this ctor directly, inject this component instead.
64       */
65      @Deprecated
66      public DefaultMojoExecutionConfigurator() {
67          this.messageBuilderFactory = new DefaultMessageBuilderFactory();
68      }
69  
70      @Inject
71      public DefaultMojoExecutionConfigurator(MessageBuilderFactory messageBuilderFactory) {
72          this.messageBuilderFactory = requireNonNull(messageBuilderFactory);
73      }
74  
75      @Override
76      public void configure(MavenProject project, MojoExecution mojoExecution, boolean allowPluginLevelConfig) {
77          String g = mojoExecution.getPlugin().getGroupId();
78  
79          String a = mojoExecution.getPlugin().getArtifactId();
80  
81          Plugin plugin = findPlugin(g, a, project.getBuildPlugins());
82  
83          if (plugin == null && project.getPluginManagement() != null) {
84              plugin = findPlugin(g, a, project.getPluginManagement().getPlugins());
85          }
86  
87          if (plugin != null) {
88              PluginExecution pluginExecution =
89                      findPluginExecution(mojoExecution.getExecutionId(), plugin.getExecutions());
90  
91              XmlNode pomConfiguration = null;
92  
93              if (pluginExecution != null) {
94                  pomConfiguration = pluginExecution.getDelegate().getConfiguration();
95              } else if (allowPluginLevelConfig) {
96                  pomConfiguration = plugin.getDelegate().getConfiguration();
97              }
98  
99              XmlNode mojoConfiguration = mojoExecution.getConfiguration() != null
100                     ? mojoExecution.getConfiguration().getDom()
101                     : null;
102 
103             XmlNode mergedConfiguration = XmlNodeImpl.merge(mojoConfiguration, pomConfiguration);
104 
105             mojoExecution.setConfiguration(mergedConfiguration);
106 
107             checkUnknownMojoConfigurationParameters(mojoExecution);
108         }
109     }
110 
111     private Plugin findPlugin(String groupId, String artifactId, Collection<Plugin> plugins) {
112         for (Plugin plugin : plugins) {
113             if (artifactId.equals(plugin.getArtifactId()) && groupId.equals(plugin.getGroupId())) {
114                 return plugin;
115             }
116         }
117 
118         return null;
119     }
120 
121     private PluginExecution findPluginExecution(String executionId, Collection<PluginExecution> executions) {
122         if (executionId != null && !executionId.isEmpty()) {
123             for (PluginExecution execution : executions) {
124                 if (executionId.equals(execution.getId())) {
125                     return execution;
126                 }
127             }
128         }
129 
130         return null;
131     }
132 
133     private void checkUnknownMojoConfigurationParameters(MojoExecution mojoExecution) {
134         if (mojoExecution.getConfiguration() == null
135                 || mojoExecution.getConfiguration().getChildCount() == 0) {
136             return;
137         }
138 
139         MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
140 
141         // in first step get parameter names of current goal
142         Set<String> parametersNamesGoal = mojoDescriptor.getParameters().stream()
143                 .flatMap(this::getParameterNames)
144                 .collect(Collectors.toSet());
145 
146         Set<String> unknownParameters = getUnknownParameters(mojoExecution, parametersNamesGoal);
147 
148         if (unknownParameters.isEmpty()) {
149             return;
150         }
151 
152         // second step get parameter names of all plugin goals
153         Set<String> parametersNamesAll = mojoDescriptor.getPluginDescriptor().getMojos().stream()
154                 .flatMap(m -> m.getParameters().stream())
155                 .flatMap(this::getParameterNames)
156                 .collect(Collectors.toSet());
157 
158         unknownParameters = getUnknownParameters(mojoExecution, parametersNamesAll);
159 
160         unknownParameters.forEach(name -> {
161             MessageBuilder messageBuilder = messageBuilderFactory
162                     .builder()
163                     .warning("Parameter '")
164                     .warning(name)
165                     .warning("' is unknown for plugin '")
166                     .warning(mojoExecution.getArtifactId())
167                     .warning(":")
168                     .warning(mojoExecution.getVersion())
169                     .warning(":")
170                     .warning(mojoExecution.getGoal());
171 
172             if (mojoExecution.getExecutionId() != null) {
173                 messageBuilder.warning(" (");
174                 messageBuilder.warning(mojoExecution.getExecutionId());
175                 messageBuilder.warning(")");
176             }
177 
178             messageBuilder.warning("'");
179 
180             logger.warn(messageBuilder.toString());
181         });
182     }
183 
184     private Stream<String> getParameterNames(Parameter parameter) {
185         if (parameter.getAlias() != null) {
186             return Stream.of(parameter.getName(), parameter.getAlias());
187         } else {
188             return Stream.of(parameter.getName());
189         }
190     }
191 
192     private Set<String> getUnknownParameters(MojoExecution mojoExecution, Set<String> parameters) {
193         return stream(mojoExecution.getConfiguration().getChildren())
194                 .map(Xpp3Dom::getName)
195                 .filter(name -> !parameters.contains(name))
196                 .collect(Collectors.toSet());
197     }
198 }