View Javadoc
1   package com.soebes.maven.plugins.multienv;
2   
3   import java.io.File;
4   import java.io.FileFilter;
5   import java.io.IOException;
6   import java.util.ArrayList;
7   import java.util.Collections;
8   import java.util.LinkedHashSet;
9   import java.util.List;
10  
11  import org.apache.maven.archiver.MavenArchiveConfiguration;
12  import org.apache.maven.artifact.Artifact;
13  import org.apache.maven.execution.MavenSession;
14  import org.apache.maven.model.Resource;
15  import org.apache.maven.plugin.AbstractMojo;
16  import org.apache.maven.plugin.MojoExecutionException;
17  import org.apache.maven.plugin.MojoFailureException;
18  import org.apache.maven.plugins.annotations.Component;
19  import org.apache.maven.plugins.annotations.Parameter;
20  import org.apache.maven.project.MavenProject;
21  import org.apache.maven.project.MavenProjectHelper;
22  import org.apache.maven.shared.filtering.MavenFilteringException;
23  import org.apache.maven.shared.filtering.MavenResourcesExecution;
24  import org.apache.maven.shared.filtering.MavenResourcesFiltering;
25  import org.codehaus.plexus.util.FileUtils;
26  
27  /**
28   * @author Karl-Heinz Marbaise <a href="mailto:khmarbaise@soebes.de">khmarbaise@soebes.de</a>
29   */
30  public abstract class AbstractMultiEnvMojo
31      extends AbstractMojo
32  {
33  
34      /**
35       * The project currently being build.
36       */
37      @Parameter( defaultValue = "${project}", required = true, readonly = true )
38      private MavenProject mavenProject;
39  
40      /**
41       * The current Maven session.
42       */
43      @Parameter( defaultValue = "${session}", required = true, readonly = true )
44      private MavenSession mavenSession;
45  
46      /**
47       * The directory for the generated configuration packages.
48       */
49      @Parameter( defaultValue = "${project.build.directory}", required = true, readonly = true )
50      private File outputDirectory;
51  
52      /**
53       * directory which contains the different environments
54       */
55      // TODO: src/main ? property?
56      @Parameter( defaultValue = "${basedir}/src/main/environments" )
57      private File sourceDirectory;
58  
59      /**
60       * The character encoding scheme to be applied when filtering resources.
61       */
62      @Parameter( defaultValue = "${project.build.sourceEncoding}" )
63      private String encoding;
64  
65      /**
66       * Name of the generated JAR.
67       */
68      @Parameter( defaultValue = "${project.build.finalName}", readonly = true )
69      private String finalName;
70  
71      @Component
72      private MavenProjectHelper projectHelper;
73  
74      /**
75       * The archive configuration to use. See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven
76       * Archiver Reference</a>.
77       */
78      @Parameter
79      private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
80  
81      /**
82       * Expression preceded with the String won't be interpolated \${foo} will be replaced with ${foo}
83       */
84      @Parameter
85      private String escapeString;
86  
87      /**
88       * Whether to escape backslashes and colons in windows-style paths.
89       */
90      @Parameter( defaultValue = "true" )
91      private boolean escapeWindowsPaths;
92  
93      /**
94       * The list of extra filter properties files to be used along with System properties, project properties, and filter
95       * properties files specified in the POM build/filters section, which should be used for the filtering during the
96       * current mojo execution. <br/>
97       * Normally, these will be configured from a plugin's execution section, to provide a different set of filters for a
98       * particular execution. For instance, starting in Maven 2.2.0, you have the option of configuring executions with
99       * the id's <code>default-resources</code> and <code>default-testResources</code> to supply different configurations
100      * for the two different types of resources. By supplying <code>extraFilters</code> configurations, you can separate
101      * which filters are used for which type of resource.
102      */
103     @Parameter
104     private List<String> filters;
105 
106     /**
107      * Support filtering of filenames directories etc.
108      */
109     @Parameter( defaultValue = "false" )
110     private boolean fileNameFiltering;
111 
112     /**
113      * <p>
114      * Set of delimiters for expressions to filter within the resources. These delimiters are specified in the form
115      * 'beginToken*endToken'. If no '*' is given, the delimiter is assumed to be the same for start and end.
116      * </p>
117      * <p>
118      * So, the default filtering delimiters might be specified as:
119      * </p>
120      * 
121      * <pre>
122      * &lt;delimiters&gt;
123      *   &lt;delimiter&gt;${*}&lt;/delimiter&gt;
124      *   &lt;delimiter&gt;@&lt;/delimiter&gt;
125      * &lt;/delimiters&gt;
126      * </pre>
127      * <p>
128      * Since the '@' delimiter is the same on both ends, we don't need to specify '@*@' (though we can).
129      * </p>
130      */
131     @Parameter
132     private LinkedHashSet<String> delimiters;
133 
134     /**
135      * Use default delimiters in addition to custom delimiters, if any.
136      */
137     @Parameter( defaultValue = "true" )
138     private boolean useDefaultDelimiters;
139 
140     /**
141      * Include empty directories or not.
142      */
143     @Parameter( defaultValue = "true" )
144     private boolean includeEmptyDirs;
145 
146     /**
147      * Additional file extensions to not apply filtering (already defined are : jpg, jpeg, gif, bmp, png)
148      */
149     @Parameter
150     private List<String> nonFilteredFileExtensions;
151 
152     /**
153      * stop searching endToken at the end of line
154      */
155     @Parameter( defaultValue = "false" )
156     private boolean supportMultiLineFiltering;
157 
158     @Component( role = MavenResourcesFiltering.class, hint = "default" )
159     protected MavenResourcesFiltering mavenResourcesFiltering;
160 
161     public MavenArchiveConfiguration getArchive()
162     {
163         return archive;
164     }
165 
166     public MavenProject getMavenProject()
167     {
168         return mavenProject;
169     }
170 
171     public MavenSession getMavenSession()
172     {
173         return mavenSession;
174     }
175 
176     public File getOutputDirectory()
177     {
178         return outputDirectory;
179     }
180 
181     public MavenProjectHelper getProjectHelper()
182     {
183         return projectHelper;
184     }
185 
186     public File getSourceDirectory()
187     {
188         return sourceDirectory;
189     }
190 
191     public String getFinalName()
192     {
193         return finalName;
194     }
195 
196     public String getEncoding()
197     {
198         return encoding;
199     }
200 
201     /**
202      * @param resourceResult The directory where to search for different environments.
203      * @return The list of identified environments. This list is converted to lower case.
204      */
205     protected String[] getTheEnvironments( File resourceResult )
206     {
207         File[] theResultingFolders = resourceResult.listFiles( new FileFilter()
208         {
209             @Override
210             public boolean accept( File pathname )
211             {
212                 return pathname.isDirectory() && pathname.exists();
213             }
214         } );
215 
216         String[] result = new String[theResultingFolders.length];
217         for ( int i = 0; i < theResultingFolders.length; i++ )
218         {
219             getLog().debug( "Folders: " + theResultingFolders[i].getName() );
220             result[i] = theResultingFolders[i].getName().toLowerCase();
221         }
222         return result;
223     }
224 
225     /**
226      * This will check if an environment (directory) contains a space cause the environment will later being used as a
227      * classifier which does not allow a space.
228      * 
229      * @param environmens The environments which should be checked.
230      * @return The list contains the invalid environments. If the list is {@code empty} all environments are ok.
231      */
232     protected List<String> environmentNamesAreValid( String[] environmens )
233     {
234         List<String> result = new ArrayList<>();
235         for ( String item : environmens )
236         {
237             if ( item.contains( " " ) )
238             {
239                 result.add( item );
240             }
241         }
242         return result;
243     }
244 
245     /**
246      * Returns the archive file to generate, based on an optional classifier.
247      *
248      * @param basedir the output directory
249      * @param finalName the name of the ear file
250      * @param classifier an optional classifier
251      * @return the file to generate
252      */
253     protected File getArchiveFile( File basedir, String finalName, String classifier, String archiveExt )
254     {
255         if ( basedir == null )
256         {
257             throw new IllegalArgumentException( "basedir is not allowed to be null" );
258         }
259         if ( finalName == null )
260         {
261             throw new IllegalArgumentException( "finalName is not allowed to be null" );
262         }
263         if ( archiveExt == null )
264         {
265             throw new IllegalArgumentException( "archiveExt is not allowed to be null" );
266         }
267 
268         if ( finalName.isEmpty() )
269         {
270             throw new IllegalArgumentException( "finalName is not allowed to be empty." );
271         }
272         if ( archiveExt.isEmpty() )
273         {
274             throw new IllegalArgumentException( "archiveExt is not allowed to be empty." );
275         }
276 
277         StringBuilder fileName = new StringBuilder( finalName );
278 
279         if ( hasClassifier( classifier ) )
280         {
281             fileName.append( "-" ).append( classifier );
282         }
283 
284         fileName.append( '.' );
285         fileName.append( archiveExt );
286 
287         return new File( basedir, fileName.toString() );
288     }
289 
290     public String getEscapeString()
291     {
292         return escapeString;
293     }
294 
295     public boolean isEscapeWindowsPaths()
296     {
297         return escapeWindowsPaths;
298     }
299 
300     public List<String> getFilters()
301     {
302         return filters;
303     }
304 
305     public boolean isFileNameFiltering()
306     {
307         return fileNameFiltering;
308     }
309 
310     public LinkedHashSet<String> getDelimiters()
311     {
312         return delimiters;
313     }
314 
315     public boolean isUseDefaultDelimiters()
316     {
317         return useDefaultDelimiters;
318     }
319 
320     public List<String> getNonFilteredFileExtensions()
321     {
322         return nonFilteredFileExtensions;
323     }
324 
325     public boolean isSupportMultiLineFiltering()
326     {
327         return supportMultiLineFiltering;
328     }
329 
330     private boolean hasClassifier( String classifier )
331     {
332         boolean result = false;
333         if ( classifier != null && classifier.trim().length() > 0 )
334         {
335             result = true;
336         }
337 
338         return result;
339     }
340 
341     protected void deleteDirectoryOfPreviousRunIfExist( File directoryOfPreviousRun )
342         throws MojoExecutionException
343     {
344 
345         if ( directoryOfPreviousRun.exists() )
346         {
347             try
348             {
349                 FileUtils.deleteDirectory( directoryOfPreviousRun );
350             }
351             catch ( IOException e )
352             {
353                 throw new MojoExecutionException( "Failure while deleting " + directoryOfPreviousRun.getAbsolutePath(),
354                                                   e );
355             }
356         }
357     }
358 
359     /**
360      * Create the unpack directory for later unpacking of the main artifact.
361      * 
362      * @return The directory which has been created.
363      * @throws MojoExecutionException in case of failures.
364      */
365     protected File createUnpackDirectory()
366         throws MojoFailureException, MojoExecutionException
367     {
368         // TODO: Should we use a different name or temp file? File.createTempFile( prefix, suffix );
369         File unpackDirectory = new File( getOutputDirectory(), "multienv-maven-plugin-unpack" );
370 
371         deleteDirectoryOfPreviousRunIfExist( unpackDirectory );
372 
373         if ( !unpackDirectory.mkdirs() )
374         {
375             throw new MojoExecutionException( "The unpack directory " + unpackDirectory.getAbsolutePath()
376                 + " couldn't generated!" );
377         }
378         return unpackDirectory;
379     }
380 
381     protected String getArchiveExtensionOfTheProjectMainArtifact()
382         throws MojoExecutionException
383     {
384         if ( getMavenProject().getArtifact() == null )
385         {
386             throw new MojoExecutionException( "No main artifact has been set yet." );
387         }
388 
389         if ( getMavenProject().getArtifact().getFile() == null )
390         {
391             throw new MojoExecutionException( "No main artifact file has been set yet." );
392         }
393 
394         return FileUtils.getExtension( getMavenProject().getArtifact().getFile().getAbsolutePath() ).toLowerCase();
395 
396     }
397 
398     protected String getArchiveExtensionOfTheArtifact(Artifact artifact)
399         throws MojoExecutionException
400     {
401         if ( artifact == null )
402         {
403             throw new MojoExecutionException( "No artifact has been set yet." );
404         }
405 
406         if ( artifact.getFile() == null )
407         {
408             throw new MojoExecutionException( "No artifact file has been set yet." );
409         }
410 
411         return FileUtils.getExtension( artifact.getFile().getAbsolutePath() ).toLowerCase();
412 
413     }
414 
415     protected File createPluginResourceOutput()
416         throws MojoExecutionException
417     {
418         // TODO: Should we use a different name? Or temp File?
419         File resourceResult = new File( getOutputDirectory(), "multienv-maven-plugin-resource-output" );
420 
421         deleteDirectoryOfPreviousRunIfExist( resourceResult );
422 
423         if ( !resourceResult.mkdirs() )
424         {
425             throw new MojoExecutionException( "Failure while trying to create " + resourceResult.getAbsolutePath() );
426         }
427 
428         return resourceResult;
429     }
430 
431     public boolean isIncludeEmptyDirs()
432     {
433         return includeEmptyDirs;
434     }
435 
436     public void setIncludeEmptyDirs( boolean includeEmptyDirs )
437     {
438         this.includeEmptyDirs = includeEmptyDirs;
439     }
440 
441     protected void filterResources( File outputDirectory )
442         throws MojoExecutionException
443     {
444 
445         Resource res = new Resource();
446         // TODO: Check how to prevent hard coding here?
447         res.setDirectory( getSourceDirectory().getAbsolutePath() );
448         res.setFiltering( true );
449         // TODO: Check if it makes sense to make this list configurable?
450         res.setIncludes( Collections.singletonList( "**/*" ) );
451 
452         List<String> filtersFile = new ArrayList<String>();
453         MavenResourcesExecution execution =
454             new MavenResourcesExecution( Collections.singletonList( res ), outputDirectory, getMavenProject(),
455                                          getEncoding(), filtersFile, getNonFilteredFileExtensions(),
456                                          getMavenSession() );
457 
458         execution.setEscapeString( getEscapeString() );
459         execution.setSupportMultiLineFiltering( isSupportMultiLineFiltering() );
460         execution.setIncludeEmptyDirs( isIncludeEmptyDirs() );
461         execution.setEscapeWindowsPaths( isEscapeWindowsPaths() );
462         execution.setFilterFilenames( isFileNameFiltering() );
463         //// execution.setFilters( filters );
464         //
465         // // TODO: Check if we need a parameter?
466         execution.setOverwrite( true );
467         execution.setDelimiters( getDelimiters(), isUseDefaultDelimiters() );
468         execution.setEncoding( getEncoding() );
469         //
470         // execution.setUseDefaultFilterWrappers( true );
471 
472         if ( getNonFilteredFileExtensions() != null )
473         {
474             execution.setNonFilteredFileExtensions( getNonFilteredFileExtensions() );
475         }
476 
477         try
478         {
479             mavenResourcesFiltering.filterResources( execution );
480         }
481         catch ( MavenFilteringException e )
482         {
483             getLog().error( "Failure during filtering.", e );
484             throw new MojoExecutionException( "Failure during filtering", e );
485         }
486 
487     }
488 
489     protected void createLoggingOutput( String[] identifiedEnvironments )
490     {
491         getLog().info( "" );
492         getLog().info( "We have found " + identifiedEnvironments.length + " environments." );
493 
494         StringBuilder sb = new StringBuilder();
495         for ( int i = 0; i < identifiedEnvironments.length; i++ )
496         {
497             if ( sb.length() > 0 )
498             {
499                 sb.append( ',' );
500             }
501             sb.append( identifiedEnvironments[i] );
502         }
503 
504         getLog().info( "We have the following environments: " + sb.toString() );
505         getLog().info( "" );
506     }
507 
508     /**
509      * This will validate the environments and will fail the build in case of errors.
510      * 
511      * @param identifiedEnvironments The environments which will be checked.
512      * @throws MojoFailureException in case of invalid environments.
513      */
514     protected void validateEnvironments( String[] identifiedEnvironments )
515         throws MojoFailureException
516     {
517         List<String> environmentNamesAreValid = environmentNamesAreValid( identifiedEnvironments );
518         if ( !environmentNamesAreValid.isEmpty() )
519         {
520             for ( String invalidEnv : environmentNamesAreValid )
521             {
522                 getLog().error( "Your environment '" + invalidEnv + "' name contains spaces which is not allowed." );
523 
524             }
525             throw new MojoFailureException( "Your environment names contain spaces which are not allowed."
526                 + "See previous error messages for details." );
527         }
528     }
529 
530 }