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