View Javadoc
1   package com.soebes.maven.plugins.iterator;
2   
3   import java.util.ArrayList;
4   
5   /*
6    * Licensed to the Apache Software Foundation (ASF) under one
7    * or more contributor license agreements.  See the NOTICE file
8    * distributed with this work for additional information
9    * regarding copyright ownership.  The ASF licenses this file
10   * to you under the Apache License, Version 2.0 (the
11   * "License"); you may not use this file except in compliance
12   * with the License.  You may obtain a copy of the License at
13   *
14   *   http://www.apache.org/licenses/LICENSE-2.0
15   *
16   * Unless required by applicable law or agreed to in writing,
17   * software distributed under the License is distributed on an
18   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19   * KIND, either express or implied.  See the License for the
20   * specific language governing permissions and limitations
21   * under the License.
22   */
23  
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Map.Entry;
27  
28  import org.apache.maven.model.Plugin;
29  import org.apache.maven.plugin.BuildPluginManager;
30  import org.apache.maven.plugin.InvalidPluginDescriptorException;
31  import org.apache.maven.plugin.MojoExecution;
32  import org.apache.maven.plugin.MojoExecutionException;
33  import org.apache.maven.plugin.MojoFailureException;
34  import org.apache.maven.plugin.PluginConfigurationException;
35  import org.apache.maven.plugin.PluginDescriptorParsingException;
36  import org.apache.maven.plugin.PluginManagerException;
37  import org.apache.maven.plugin.PluginResolutionException;
38  import org.apache.maven.plugin.descriptor.MojoDescriptor;
39  import org.apache.maven.plugin.descriptor.PluginDescriptor;
40  import org.apache.maven.plugins.annotations.Component;
41  import org.apache.maven.plugins.annotations.LifecyclePhase;
42  import org.apache.maven.plugins.annotations.Mojo;
43  import org.apache.maven.plugins.annotations.Parameter;
44  import org.apache.maven.plugins.annotations.ResolutionScope;
45  import org.apache.maven.reporting.exec.MavenPluginManagerHelper;
46  import org.codehaus.plexus.configuration.PlexusConfiguration;
47  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
48  import org.codehaus.plexus.util.xml.Xpp3Dom;
49  import org.codehaus.plexus.util.xml.Xpp3DomUtils;
50  
51  /**
52   * Executor will execute a given plugin by iterating through the given items.
53   *
54   * @author Karl-Heinz Marbaise <a href="mailto:khmarbaise@apache.org">khmarbaise@apache.org</a>
55   */
56  @Mojo( name = "iterator", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.TEST, requiresProject = true, threadSafe = true )
57  public class IteratorMojo
58      extends AbstractIteratorMojo
59  {
60  
61      /**
62       * By using the pluginExecutors you can define a list of plugins which will be executed during executor.
63       * 
64       * <pre>
65       * {@code
66       * <pluginExecutors>
67       *   <pluginExecutor>
68       *     ..Plugin
69       *   </pluginExecutor>
70       * </pluginExecutors>
71       * }
72       * </pre>
73       * 
74       * A plugin must be defined by using the plugin tag. You can omit the version tag if you have defined the plugin's
75       * version in the pluginManagement section.
76       * 
77       * <pre>
78       * {@code
79       * <plugin>
80       *   <groupId>..</groupId>
81       *   <artifactId>..</artifactId>
82       *   <version>..</version>
83       * </plugin>
84       * }
85       * </pre>
86       * 
87       * Furthermore you need to define the goal of the given plugin by using the tag:
88       * 
89       * <pre>
90       * {@code
91       * <goal>...</goal>
92       * }
93       * </pre>
94       * 
95       * And finally you need to define the configuration for the plugin by using the following:
96       * 
97       * <pre>
98       * {@code
99       * <configuration>
100      *   Plugin Configuration
101      * </configuration>
102      * }
103      * </pre>
104      */
105     @Parameter( required = true )
106     private List<PluginExecutor> pluginExecutors;
107 
108     /**
109      * This is the helper class to support Maven 3.1 and before.
110      */
111     @Component
112     protected MavenPluginManagerHelper mavenPluginManagerHelper;
113 
114     /**
115      * The Maven BuildPluginManager component.
116      */
117     @Component
118     private BuildPluginManager pluginManager;
119 
120     @Parameter( defaultValue = "${plugin}", required = true, readonly = true )
121     private PluginDescriptor pluginDescriptor;
122 
123     /**
124      * This will copy the configuration from <b>src</b> to the result whereas the placeholder will be replaced with the
125      * current value.
126      */
127     private PlexusConfiguration copyConfiguration( Xpp3Dom src, String iteratorName, String value )
128     {
129 
130         XmlPlexusConfiguration dom = new XmlPlexusConfiguration( src.getName() );
131 
132         if ( src.getValue() == null )
133         {
134             dom.setValue( src.getValue() );
135         }
136         else
137         {
138             if ( src.getValue().contains( iteratorName ) )
139             {
140                 dom.setValue( src.getValue().replaceAll( iteratorName, value ) );
141             }
142             else
143             {
144                 dom.setValue( src.getValue() );
145             }
146         }
147 
148         for ( String attributeName : src.getAttributeNames() )
149         {
150             dom.setAttribute( attributeName, src.getAttribute( attributeName ) );
151         }
152 
153         for ( Xpp3Dom child : src.getChildren() )
154         {
155             dom.addChild( copyConfiguration( child, iteratorName, value ) );
156         }
157 
158         return dom;
159     }
160 
161     /**
162      * This method will give back the plugin information which has been given as parameter in case of an not existing
163      * pluginManagement section or if the version of the plugin is already defined. Otherwise the version will be got
164      * from the pluginManagement area if the plugin can be found in it.
165      * 
166      * @param plugin The plugin which should be checked against the pluginManagement area.
167      * @return The plugin version. If the plugin version is not defined it means that neither the version has been
168      *         defined by the pluginManagement section nor by the user itself.
169      */
170     private Plugin getPluginVersionFromPluginManagement( Plugin plugin )
171     {
172 
173         if ( !isPluginManagementDefined() )
174         {
175             return plugin;
176         }
177 
178         if ( isPluginVersionDefined( plugin ) )
179         {
180             return plugin;
181         }
182 
183         Map<String, Plugin> plugins = getMavenProject().getPluginManagement().getPluginsAsMap();
184 
185         Plugin result = plugins.get( plugin.getKey() );
186         if ( result == null )
187         {
188             return plugin;
189         }
190         else
191         {
192             return result;
193         }
194     }
195 
196     private boolean isPluginVersionDefined( Plugin plugin )
197     {
198         return plugin.getVersion() != null;
199     }
200 
201     private boolean isPluginManagementDefined()
202     {
203         return getMavenProject().getPluginManagement() != null;
204     }
205 
206     public void execute()
207         throws MojoExecutionException, MojoFailureException
208     {
209 
210         if ( isSkip() )
211         {
212             getLog().info( "Skip by user request." );
213             return;
214         }
215 
216         if ( isNoneSet() )
217         {
218             getLog().warn("Neither items, itemsWithProperties, content nor folder have been set.");
219             return;
220         }
221 
222         if ( isMoreThanOneSet() )
223         {
224             throw new MojoExecutionException( "You can use only one element. "
225                 + "Either items, itemsWithProperties, content or folder element but not more than one of them." );
226         }
227 
228         List<Exception> exceptions = new ArrayList<>();
229         for ( ItemWithProperties item : getItemsConverted() )
230         {
231             for ( PluginExecutor pluginExecutor : pluginExecutors )
232             {
233                 try
234                 {
235                     handlePluginExecution( item, pluginExecutor );
236                 }
237                 catch ( MojoExecutionException e )
238                 {
239                     if ( isFailAtEnd() )
240                     {
241                         exceptions.add( e );
242                     }
243                     else
244                     {
245                         throw e;
246                     }
247                 }
248                 catch ( MojoFailureException e )
249                 {
250                     // An Failure will stop the iteration under any circumstances.
251                     throw e;
252                 }
253             }
254         }
255 
256         if ( !exceptions.isEmpty() )
257         {
258             for ( Exception exception : exceptions )
259             {
260                 getLog().error( exception );
261             }
262             throw new MojoExecutionException( "Failures during iteration" );
263         }
264     }
265 
266     /**
267      * This will inject the iteration into the current properties and furthermore the properties if they have given and
268      * execute the plugin with the appropriate context.
269      * 
270      * @param item The items which are using for the current iteration.
271      * @param pluginExecutor The {@link PluginExecutor}
272      * @throws MojoExecutionException in case of an error.
273      * @throws MojoFailureException failure during the run of a plugin.
274      */
275     private void handlePluginExecution( ItemWithProperties item, PluginExecutor pluginExecutor )
276         throws MojoExecutionException, MojoFailureException
277     {
278         Plugin executePlugin = getPluginVersionFromPluginManagement( pluginExecutor.getPlugin() );
279         if ( executePlugin.getVersion() == null )
280         {
281             throw new MojoExecutionException( "Unknown plugin version. You have to define the version either directly or via pluginManagement." );
282         }
283 
284         Xpp3Dom resultConfiguration = handlePluginConfigurationFromPluginManagement( pluginExecutor, executePlugin );
285 
286         PlexusConfiguration plexusConfiguration =
287             copyConfiguration( resultConfiguration, getPlaceHolder(), item.getName() );
288 
289         createLogOutput( pluginExecutor, executePlugin, item.getName() );
290 
291         // Put the value of the current iteration into the current context.
292         getMavenProject().getProperties().put( getIteratorName(), item.getName() );
293 
294         if ( item.hasProperties() )
295         {
296             // Add all properties to the context.
297             for ( Entry<Object, Object> entry : item.getProperties().entrySet() )
298             {
299                 getMavenProject().getProperties().put( entry.getKey(), entry.getValue() );
300             }
301         }
302 
303         try
304         {
305             executeMojo( executePlugin, pluginExecutor.getGoal(), toXpp3Dom( plexusConfiguration ) );
306         }
307         catch ( MojoExecutionException e )
308         {
309             throw e;
310         }
311         catch ( MojoFailureException e )
312         {
313             throw e;
314         }
315         catch ( PluginResolutionException e )
316         {
317             getLog().error( "PluginresourceException:", e );
318             throw new MojoExecutionException( "PluginRescourceException", e );
319         }
320         catch ( PluginDescriptorParsingException e )
321         {
322             getLog().error( "PluginDescriptorParsingException:", e );
323             throw new MojoExecutionException( "PluginDescriptorParsingException", e );
324         }
325         catch ( InvalidPluginDescriptorException e )
326         {
327             getLog().error( "InvalidPluginDescriptorException:", e );
328             throw new MojoExecutionException( "InvalidPluginDescriptorException", e );
329         }
330         catch ( PluginConfigurationException e )
331         {
332             getLog().error( "PluginConfigurationException:", e );
333             throw new MojoExecutionException( "PluginConfigurationException", e );
334         }
335         catch ( PluginManagerException e )
336         {
337             getLog().error( "PluginManagerException:", e );
338             throw new MojoExecutionException( "PluginManagerException", e );
339         }
340         finally
341         {
342             // Remove the old key from context.
343             getMavenProject().getProperties().remove( getIteratorName() );
344             if ( item.hasProperties() )
345             {
346                 // Remove all old properties from context.
347                 for ( Object entry : item.getProperties().keySet() )
348                 {
349                     getMavenProject().getProperties().remove( entry );
350                 }
351             }
352 
353         }
354     }
355 
356     private Xpp3Dom handlePluginConfigurationFromPluginManagement( PluginExecutor pluginExecutor, Plugin executePlugin )
357     {
358         Xpp3Dom resultConfiguration = toXpp3Dom( pluginExecutor.getConfiguration() );
359         getLog().debug( "Configuration: " + resultConfiguration.toString() );
360         if ( executePlugin.getConfiguration() != null )
361         {
362             Xpp3Dom x = (Xpp3Dom) executePlugin.getConfiguration();
363             resultConfiguration = Xpp3DomUtils.mergeXpp3Dom( toXpp3Dom( pluginExecutor.getConfiguration() ), x );
364 
365             getLog().debug( "ConfigurationExecutePlugin: " + executePlugin.getConfiguration().toString() );
366 
367         }
368         return resultConfiguration;
369     }
370 
371     /**
372      * Taken from MojoExecutor of Don Brown. Make it working with Maven 3.1.
373      * 
374      * @param plugin
375      * @param goal
376      * @param configuration
377      * @param env
378      * @throws MojoExecutionException
379      * @throws PluginResolutionException
380      * @throws PluginDescriptorParsingException
381      * @throws InvalidPluginDescriptorException
382      * @throws PluginManagerException
383      * @throws PluginConfigurationException
384      * @throws MojoFailureException
385      */
386     private void executeMojo( Plugin plugin, String goal, Xpp3Dom configuration )
387         throws MojoExecutionException, PluginResolutionException, PluginDescriptorParsingException,
388         InvalidPluginDescriptorException, MojoFailureException, PluginConfigurationException, PluginManagerException
389     {
390 
391         if ( configuration == null )
392         {
393             throw new NullPointerException( "configuration may not be null" );
394         }
395 
396         PluginDescriptor pluginDescriptor = getPluginDescriptor( plugin );
397 
398         MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( goal );
399         if ( mojoDescriptor == null )
400         {
401             throw new MojoExecutionException( "Could not find goal '" + goal + "' in plugin " + plugin.getGroupId()
402                 + ":" + plugin.getArtifactId() + ":" + plugin.getVersion() );
403         }
404 
405         MojoExecution exec = mojoExecution( mojoDescriptor, configuration );
406         pluginManager.executeMojo( getMavenSession(), exec );
407     }
408 
409     private PluginDescriptor getPluginDescriptor( Plugin plugin )
410         throws PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException
411     {
412         return mavenPluginManagerHelper.getPluginDescriptor( plugin, getMavenProject().getRemotePluginRepositories(),
413                                                              getMavenSession() );
414     }
415 
416     private MojoExecution mojoExecution( MojoDescriptor mojoDescriptor, Xpp3Dom configuration )
417     {
418         Xpp3Dom resultConfiguration =
419             Xpp3DomUtils.mergeXpp3Dom( configuration, toXpp3Dom( mojoDescriptor.getMojoConfiguration() ) );
420         return new MojoExecution( mojoDescriptor, resultConfiguration );
421     }
422 
423     /**
424      * Taken from MojoExecutor of Don Brown. Converts PlexusConfiguration to a Xpp3Dom.
425      * 
426      * @param config the PlexusConfiguration. Must not be {@code null}.
427      * @return the Xpp3Dom representation of the PlexusConfiguration
428      */
429     public Xpp3Dom toXpp3Dom( PlexusConfiguration config )
430     {
431         Xpp3Dom result = new Xpp3Dom( config.getName() );
432         result.setValue( config.getValue( null ) );
433         for ( String name : config.getAttributeNames() )
434         {
435             result.setAttribute( name, config.getAttribute( name ) );
436         }
437         for ( PlexusConfiguration child : config.getChildren() )
438         {
439             result.addChild( toXpp3Dom( child ) );
440         }
441         return result;
442     }
443 
444     /**
445      * Will create the output during the executions for the plugins like <code>groupId:artifactId:version:goal</code>.
446      * 
447      * @param pluginExecutor
448      * @param executePlugin
449      */
450     private void createLogOutput( PluginExecutor pluginExecutor, Plugin executePlugin, String item )
451     {
452         StringBuilder sb = new StringBuilder( "------ " );
453         sb.append( "(" );
454         sb.append( item );
455         sb.append( ") " );
456         sb.append( executePlugin.getKey() );
457         sb.append( ":" );
458         sb.append( executePlugin.getVersion() );
459         sb.append( ":" );
460         sb.append( pluginExecutor.getGoal() );
461         getLog().info( sb.toString() );
462     }
463 
464 }