View Javadoc

1   package org.apache.maven.plugin.ear;
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.archiver.MavenArchiveConfiguration;
23  import org.apache.maven.archiver.MavenArchiver;
24  import org.apache.maven.plugin.MojoExecutionException;
25  import org.apache.maven.plugin.MojoFailureException;
26  import org.apache.maven.project.MavenProjectHelper;
27  import org.apache.maven.shared.filtering.MavenFileFilter;
28  import org.apache.maven.shared.filtering.MavenResourcesExecution;
29  import org.apache.maven.shared.filtering.MavenFilteringException;
30  import org.apache.maven.shared.filtering.MavenResourcesFiltering;
31  import org.apache.maven.execution.MavenSession;
32  import org.codehaus.plexus.archiver.ArchiverException;
33  import org.codehaus.plexus.archiver.UnArchiver;
34  import org.codehaus.plexus.archiver.jar.JarArchiver;
35  import org.codehaus.plexus.archiver.manager.ArchiverManager;
36  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
37  import org.codehaus.plexus.util.DirectoryScanner;
38  import org.codehaus.plexus.util.FileUtils;
39  import org.codehaus.plexus.util.StringUtils;
40  
41  import java.io.File;
42  import java.io.IOException;
43  import java.util.ArrayList;
44  import java.util.Arrays;
45  import java.util.Iterator;
46  import java.util.List;
47  
48  /**
49   * Builds J2EE Enteprise Archive (EAR) files.
50   *
51   * @author <a href="snicoll@apache.org">Stephane Nicoll</a>
52   * @version $Id: EarMojo.java 746740 2009-02-22 15:44:32Z snicoll $
53   * @goal ear
54   * @phase package
55   * @requiresDependencyResolution test
56   */
57  public class EarMojo
58      extends AbstractEarMojo
59  {
60      private static final String[] EMPTY_STRING_ARRAY = {};
61  
62  
63      /**
64       * Single directory for extra files to include in the EAR.
65       *
66       * @parameter expression="${basedir}/src/main/application"
67       * @required
68       */
69      private File earSourceDirectory;
70  
71      /**
72       * The comma separated list of tokens to include in the EAR.
73       *
74       * @parameter alias="includes" default-value="**"
75       */
76      private String earSourceIncludes;
77  
78      /**
79       * The comma separated list of tokens to exclude from the EAR.
80       *
81       * @parameter alias="excludes"
82       */
83      private String earSourceExcludes;
84  
85      /**
86       * Specify that the ear sources should be filtered.
87       *
88       * @parameter default-value="false"
89       * @since 2.3.2     
90       */
91      private boolean filtering;
92  
93      /**
94       * Filters (property files) to include during the interpolation of the pom.xml.
95       *
96       * @parameter
97       * @since 2.3.2
98       */
99      private List filters;
100 
101     /**
102      * A list of file extensions that should not be filtered if
103      * filtering is actived.
104      *
105      * @parameter
106      * @since 2.3.2
107      */
108     private List nonFilteredFileExtensions;
109 
110     /**
111      * To escape interpolated value with windows path
112      * c:\foo\bar will be replaced with c:\\foo\\bar
113      *
114      * @parameter expression="${maven.ear.escapedBackslashesInFilePath}" default-value="false"
115      * @since 2.3.2
116      */
117     private boolean escapedBackslashesInFilePath;
118 
119     /**
120      * Expression preceded with the String won't be interpolated
121      * \${foo} will be replaced with ${foo}
122      *
123      * @parameter expression="${maven.ear.escapeString}"
124      * @since 2.3.2
125      */
126     protected String escapeString;
127 
128     /**
129      * The location of the manifest file to be used within the ear file.
130      *
131      * @parameter expression="${basedir}/src/main/application/META-INF/MANIFEST.MF"
132      */
133     private File manifestFile;
134 
135     /**
136      * The location of a custom application.xml file to be used
137      * within the ear file.
138      *
139      * @parameter
140      */
141     private String applicationXml;
142 
143     /**
144      * The directory for the generated EAR.
145      *
146      * @parameter expression="${project.build.directory}"
147      * @required
148      */
149     private String outputDirectory;
150 
151     /**
152      * The name of the EAR file to generate.
153      *
154      * @parameter alias="earName" expression="${project.build.finalName}"
155      * @required
156      */
157     private String finalName;
158 
159     /**
160      * The comma separated list of artifact's type(s) to unpack
161      * by default.
162      *
163      * @parameter
164      */
165     private String unpackTypes;
166 
167     /**
168      * Classifier to add to the artifact generated. If given, the artifact will
169      * be an attachment instead.
170      *
171      * @parameter
172      */
173     private String classifier;
174 
175     /**
176      * The directory to get the resources from.
177      *
178      * @parameter
179      * @deprecated please use earSourcesDirectory instead
180      */
181     private File resourcesDir;
182 
183     /**
184      * The Jar archiver.
185      *
186      * @component role="org.codehaus.plexus.archiver.Archiver" role-hint="jar"
187      */
188     private JarArchiver jarArchiver;
189 
190     /**
191      * The archive configuration to use.
192      * See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven Archiver Reference</a>.
193      *
194      * @parameter
195      */
196     private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
197 
198     /**
199      * @component
200      */
201     private MavenProjectHelper projectHelper;
202 
203     /**
204      * The archive manager.
205      *
206      * @component
207      */
208     private ArchiverManager archiverManager;
209 
210     /**
211      *
212      * @component role="org.apache.maven.shared.filtering.MavenFileFilter" role-hint="default"
213      * @required
214      */
215     private MavenFileFilter mavenFileFilter;
216 
217     /**
218      *
219      * @component role="org.apache.maven.shared.filtering.MavenResourcesFiltering" role-hint="default"
220      * @required
221      */
222     private MavenResourcesFiltering mavenResourcesFiltering;
223 
224     /**
225      * @parameter expression="${session}"
226      * @readonly
227      * @required
228      * @since 2.3.2
229      */
230     private MavenSession session;
231 
232 
233     private List filterWrappers;
234 
235 
236     public void execute()
237         throws MojoExecutionException, MojoFailureException
238     {
239         // Initializes ear modules
240         super.execute();
241 
242         // Initializes unpack types
243         List unpackTypesList = new ArrayList();
244         if ( unpackTypes != null )
245         {
246             unpackTypesList = Arrays.asList( unpackTypes.split( "," ) );
247             final Iterator it = unpackTypesList.iterator();
248             while ( it.hasNext() )
249             {
250                 String type = (String) it.next();
251                 if ( !EarModuleFactory.standardArtifactTypes.contains( type ) )
252                 {
253                     throw new MojoExecutionException(
254                         "Invalid type[" + type + "] supported types are " + EarModuleFactory.standardArtifactTypes );
255                 }
256             }
257             getLog().debug( "Initialized unpack types " + unpackTypesList );
258         }
259 
260         // Copy modules
261         try
262         {
263             for ( Iterator iter = getModules().iterator(); iter.hasNext(); )
264             {
265                 EarModule module = (EarModule) iter.next();
266                 if ( module instanceof JavaModule )
267                 {
268                     getLog().warn( "JavaModule is deprecated (" + module + "), please use JarModule instead." );
269                 }
270                 if ( module instanceof Ejb3Module )
271                 {
272                     getLog().warn( "Ejb3Module is deprecated (" + module + "), please use EjbModule instead." );
273                 }
274                 final File sourceFile = module.getArtifact().getFile();
275                 final File destinationFile = buildDestinationFile( getWorkDirectory(), module.getUri() );
276                 if ( !sourceFile.isFile() )
277                 {
278                     throw new MojoExecutionException( "Cannot copy a directory: " + sourceFile.getAbsolutePath() +
279                         "; Did you package/install " + module.getArtifact() + "?" );
280                 }
281 
282                 if ( destinationFile.getCanonicalPath().equals( sourceFile.getCanonicalPath() ) )
283                 {
284                     getLog().info(
285                         "Skipping artifact[" + module + "], as it already exists at[" + module.getUri() + "]" );
286                     continue;
287                 }
288 
289                 // If the module is within the unpack list, make sure that no unpack wasn't forced (null or true)
290                 // If the module is not in the unpack list, it should be true
291                 if ( ( unpackTypesList.contains( module.getType() ) &&
292                     ( module.shouldUnpack() == null || module.shouldUnpack().booleanValue() ) ) ||
293                     ( module.shouldUnpack() != null && module.shouldUnpack().booleanValue() ) )
294                 {
295                     getLog().info( "Copying artifact[" + module + "] to[" + module.getUri() + "] (unpacked)" );
296                     // Make sure that the destination is a directory to avoid plexus nasty stuff :)
297                     destinationFile.mkdirs();
298                     unpack( sourceFile, destinationFile );
299                 }
300                 else
301                 {
302                     if ( sourceFile.lastModified() > destinationFile.lastModified() )
303                     {
304                         getLog().info( "Copying artifact[" + module + "] to[" + module.getUri() + "]" );
305                         FileUtils.copyFile( sourceFile, destinationFile );
306                     }
307                     else
308                     {
309                         getLog().debug( "Skipping artifact[" + module + "], as it is already up to date at[" +
310                             module.getUri() + "]" );
311                     }
312                 }
313             }
314         }
315         catch ( IOException e )
316         {
317             throw new MojoExecutionException( "Error copying EAR modules", e );
318         }
319         catch ( ArchiverException e )
320         {
321             throw new MojoExecutionException( "Error unpacking EAR modules", e );
322         }
323         catch ( NoSuchArchiverException e )
324         {
325             throw new MojoExecutionException( "No Archiver found for EAR modules", e );
326         }
327 
328         // Copy source files
329         try
330         {
331             File earSourceDir = earSourceDirectory;
332             if ( earSourceDir.exists() )
333             {
334                 getLog().info( "Copy ear sources to " + getWorkDirectory().getAbsolutePath() );
335                 String[] fileNames = getEarFiles( earSourceDir );
336                 for ( int i = 0; i < fileNames.length; i++ )
337                 {
338                     copyFile( new File( earSourceDir, fileNames[i] ),
339                               new File( getWorkDirectory(), fileNames[i] ) );
340                 }
341             }
342 
343             if ( applicationXml != null && !"".equals( applicationXml ) )
344             {
345                 //rename to application.xml
346                 getLog().info( "Including custom application.xml[" + applicationXml + "]" );
347                 File metaInfDir = new File( getWorkDirectory(), META_INF );
348                 copyFile( new File( applicationXml ), new File( metaInfDir, "/application.xml" ) );
349             }
350 
351         }
352         catch ( IOException e )
353         {
354             throw new MojoExecutionException( "Error copying EAR sources", e );
355         }
356         catch ( MavenFilteringException e )
357         {
358             throw new MojoExecutionException( "Error filetering EAR sources", e );
359         }
360 
361         // Copy resources files
362         try
363         {
364             if ( resourcesDir != null && resourcesDir.exists() )
365             {
366                 getLog().warn( "resourcesDir is deprecated. Please use the earSourceDirectory property instead.");
367                 getLog().info( "Copy ear resources to " + getWorkDirectory().getAbsolutePath() );
368                 String[] fileNames = getEarFiles( resourcesDir );
369                 for ( int i = 0; i < fileNames.length; i++ )
370                 {
371                     FileUtils.copyFile( new File( resourcesDir, fileNames[i] ),
372                                         new File( getWorkDirectory(), fileNames[i] ) );
373                 }
374             }
375         }
376         catch ( IOException e )
377         {
378             throw new MojoExecutionException( "Error copying EAR resources", e );
379         }
380 
381         // Check if deployment descriptor is there
382         File ddFile = new File( getWorkDirectory(), APPLICATION_XML_URI );
383         if ( !ddFile.exists() && !version.equals(VERSION_5 ) )
384         {
385             throw new MojoExecutionException(
386                 "Deployment descriptor: " + ddFile.getAbsolutePath() + " does not exist." );
387         }
388 
389         try
390         {
391             File earFile = getEarFile( outputDirectory, finalName, classifier );
392             MavenArchiver archiver = new MavenArchiver();
393             final JarArchiver jarArchiver = getJarArchiver();
394             getLog().debug( "Jar archiver implementation[" + jarArchiver.getClass().getName() + "]" );
395             archiver.setArchiver( jarArchiver );
396             archiver.setOutputFile( earFile );
397 
398             // Include custom manifest if necessary
399             includeCustomManifestFile();
400 
401             archiver.getArchiver().addDirectory( getWorkDirectory() );
402             archiver.createArchive( getProject(), archive );
403 
404             if ( classifier != null )
405             {
406                 projectHelper.attachArtifact( getProject(), "ear", classifier, earFile );
407             }
408             else
409             {
410                 getProject().getArtifact().setFile( earFile );
411             }
412         }
413         catch ( Exception e )
414         {
415             throw new MojoExecutionException( "Error assembling EAR", e );
416         }
417     }
418 
419     public String getApplicationXml()
420     {
421         return applicationXml;
422     }
423 
424     public void setApplicationXml( String applicationXml )
425     {
426         this.applicationXml = applicationXml;
427     }
428 
429     /**
430      * Returns a string array of the excludes to be used
431      * when assembling/copying the ear.
432      *
433      * @return an array of tokens to exclude
434      */
435     protected String[] getExcludes()
436     {
437         List excludeList = new ArrayList( FileUtils.getDefaultExcludesAsList() );
438         if ( earSourceExcludes != null && !"".equals( earSourceExcludes ) )
439         {
440             excludeList.addAll( Arrays.asList( StringUtils.split( earSourceExcludes, "," ) ) );
441         }
442 
443         // if applicationXml is specified, omit the one in the source directory
444         if ( getApplicationXml() != null && !"".equals( getApplicationXml() ) )
445         {
446             excludeList.add( "**/" + META_INF + "/application.xml" );
447         }
448 
449         return (String[]) excludeList.toArray( EMPTY_STRING_ARRAY );
450     }
451 
452     /**
453      * Returns a string array of the includes to be used
454      * when assembling/copying the ear.
455      *
456      * @return an array of tokens to include
457      */
458     protected String[] getIncludes()
459     {
460         return StringUtils.split( StringUtils.defaultString( earSourceIncludes ), "," );
461     }
462 
463     private static File buildDestinationFile( File buildDir, String uri )
464     {
465         return new File( buildDir, uri );
466     }
467 
468     private void includeCustomManifestFile()
469     {
470         File customManifestFile = manifestFile;
471 
472         if ( !customManifestFile.exists() )
473         {
474             getLog().info( "Could not find manifest file: " + manifestFile + " - Generating one" );
475         }
476         else
477         {
478             getLog().info( "Including custom manifest file[" + customManifestFile + "]" );
479             archive.setManifestFile( customManifestFile );
480         }
481     }
482 
483     /**
484      * Returns the EAR file to generate, based on an optional classifier.
485      *
486      * @param basedir    the output directory
487      * @param finalName  the name of the ear file
488      * @param classifier an optional classifier
489      * @return the EAR file to generate
490      */
491     private static File getEarFile( String basedir, String finalName, String classifier )
492     {
493         if ( classifier == null )
494         {
495             classifier = "";
496         }
497         else if ( classifier.trim().length() > 0 && !classifier.startsWith( "-" ) )
498         {
499             classifier = "-" + classifier;
500         }
501 
502         return new File( basedir, finalName + classifier + ".ear" );
503     }
504 
505     /**
506      * Returns a list of filenames that should be copied
507      * over to the destination directory.
508      *
509      * @param sourceDir the directory to be scanned
510      * @return the array of filenames, relative to the sourceDir
511      */
512     private String[] getEarFiles( File sourceDir )
513     {
514         DirectoryScanner scanner = new DirectoryScanner();
515         scanner.setBasedir( sourceDir );
516         scanner.setExcludes( getExcludes() );
517         scanner.addDefaultExcludes();
518 
519         scanner.setIncludes( getIncludes() );
520 
521         scanner.scan();
522 
523         return scanner.getIncludedFiles();
524     }
525 
526     /**
527      * Unpacks the module into the EAR structure.
528      *
529      * @param source  File to be unpacked.
530      * @param destDir Location where to put the unpacked files.
531      */
532     public void unpack( File source, File destDir )
533         throws NoSuchArchiverException, IOException, ArchiverException
534     {
535         UnArchiver unArchiver = archiverManager.getUnArchiver( "zip" );
536         unArchiver.setSourceFile( source );
537         unArchiver.setDestDirectory( destDir );
538 
539         // Extract the module
540         unArchiver.extract();
541     }
542 
543     /**
544      * Returns the {@link JarArchiver} implementation used
545      * to package the EAR file.
546      * <p/>
547      * By default the archiver is obtained from the Plexus container.
548      *
549      * @return the archiver
550      */
551     protected JarArchiver getJarArchiver()
552     {
553         return jarArchiver;
554     }
555 
556     private void copyFile(File source, File target)
557             throws MavenFilteringException, IOException, MojoExecutionException {
558         if ( filtering && !isNonFilteredExtension( source.getName() ) )
559         {
560            mavenFileFilter.copyFile( source, target, true, getFilterWrappers(), null );
561         }
562         else
563         {
564              FileUtils.copyFile(source,target);
565         }
566     }
567 
568     public boolean isNonFilteredExtension( String fileName )
569     {
570         return !mavenResourcesFiltering.filteredFileExtension( fileName, nonFilteredFileExtensions );
571     }
572 
573     private List getFilterWrappers() throws MojoExecutionException {
574         if (filterWrappers == null)
575         {
576             try
577             {
578                 MavenResourcesExecution mavenResourcesExecution = new MavenResourcesExecution();
579                 mavenResourcesExecution.setEscapeString( escapeString );
580                 filterWrappers = mavenFileFilter.getDefaultFilterWrappers( project, filters,
581                         escapedBackslashesInFilePath, this.session, mavenResourcesExecution );
582             }
583             catch ( MavenFilteringException e )
584             {
585                 getLog().error( "fail to build filering wrappers " + e.getMessage() );
586                 throw new MojoExecutionException( e.getMessage(), e );
587             }
588         }
589         return filterWrappers;
590     }
591 }