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 java.io.File;
23  import java.io.FileReader;
24  import java.io.IOException;
25  import java.io.PrintWriter;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Map.Entry;
31  import java.util.zip.ZipException;
32  
33  import org.apache.maven.archiver.MavenArchiveConfiguration;
34  import org.apache.maven.archiver.MavenArchiver;
35  import org.apache.maven.execution.MavenSession;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugin.MojoFailureException;
38  import org.apache.maven.plugin.ear.util.EarMavenArchiver;
39  import org.apache.maven.plugin.ear.util.JavaEEVersion;
40  import org.apache.maven.plugin.ear.util.ModuleIdentifierValidator;
41  import org.apache.maven.plugins.annotations.Component;
42  import org.apache.maven.plugins.annotations.LifecyclePhase;
43  import org.apache.maven.plugins.annotations.Mojo;
44  import org.apache.maven.plugins.annotations.Parameter;
45  import org.apache.maven.plugins.annotations.ResolutionScope;
46  import org.apache.maven.project.MavenProjectHelper;
47  import org.apache.maven.shared.filtering.MavenFileFilter;
48  import org.apache.maven.shared.filtering.MavenFilteringException;
49  import org.apache.maven.shared.filtering.MavenResourcesExecution;
50  import org.apache.maven.shared.filtering.MavenResourcesFiltering;
51  import org.apache.maven.shared.utils.io.FileUtils;
52  import org.codehaus.plexus.archiver.Archiver;
53  import org.codehaus.plexus.archiver.ArchiverException;
54  import org.codehaus.plexus.archiver.UnArchiver;
55  import org.codehaus.plexus.archiver.jar.JarArchiver;
56  import org.codehaus.plexus.archiver.jar.Manifest;
57  import org.codehaus.plexus.archiver.jar.Manifest.Attribute;
58  import org.codehaus.plexus.archiver.jar.ManifestException;
59  import org.codehaus.plexus.archiver.manager.ArchiverManager;
60  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
61  import org.codehaus.plexus.archiver.zip.ZipArchiver;
62  import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
63  import org.codehaus.plexus.util.DirectoryScanner;
64  import org.codehaus.plexus.util.StringUtils;
65  
66  /**
67   * Builds J2EE Enterprise Archive (EAR) files.
68   * 
69   * @author <a href="snicoll@apache.org">Stephane Nicoll</a>
70   * @version $Id: EarMojo.java 1645331 2014-12-13 17:31:09Z khmarbaise $
71   */
72  // CHECKSTYLE_OFF: LineLength
73  @Mojo( name = "ear", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST )
74  // CHECKSTYLE_ON: LineLength
75  public class EarMojo
76      extends AbstractEarMojo
77  {
78      /**
79       * Single directory for extra files to include in the EAR.
80       */
81      @Parameter( defaultValue = "${basedir}/src/main/application", required = true )
82      private File earSourceDirectory;
83  
84      /**
85       * The comma separated list of tokens to include in the EAR.
86       */
87      @Parameter( alias = "includes", defaultValue = "**" )
88      private String earSourceIncludes;
89  
90      /**
91       * The comma separated list of tokens to exclude from the EAR.
92       */
93      @Parameter( alias = "excludes" )
94      private String earSourceExcludes;
95  
96      /**
97       * Specify that the EAR sources should be filtered.
98       * 
99       * @since 2.3.2
100      */
101     @Parameter( defaultValue = "false" )
102     private boolean filtering;
103 
104     /**
105      * Filters (property files) to include during the interpolation of the pom.xml.
106      * 
107      * @since 2.3.2
108      */
109     @Parameter
110     private List<String> filters;
111 
112     /**
113      * A list of file extensions that should not be filtered if filtering is enabled.
114      * 
115      * @since 2.3.2
116      */
117     @Parameter
118     private List<String> nonFilteredFileExtensions;
119 
120     /**
121      * To escape interpolated value with Windows path c:\foo\bar will be replaced with c:\\foo\\bar.
122      * 
123      * @since 2.3.2
124      */
125     @Parameter( property = "maven.ear.escapedBackslashesInFilePath", defaultValue = "false" )
126     private boolean escapedBackslashesInFilePath;
127 
128     /**
129      * Expression preceded with this String won't be interpolated \${foo} will be replaced with ${foo}.
130      * 
131      * @since 2.3.2
132      */
133     @Parameter( property = "maven.ear.escapeString" )
134     protected String escapeString;
135 
136     /**
137      * In case of using the {@link #skinnyWars} and {@link #defaultLibBundleDir} usually the
138      * classpath will be modified.
139      * By settings this option {@code true} you can change this and keep the classpath untouched.
140      * This option has been introduced to keep the backward compatibility with earlier versions
141      * of the plugin.
142      * 
143      * @since 2.10
144      */
145     @Parameter( defaultValue = "false" )
146     private boolean skipClassPathModification;
147 
148     /**
149      * The location of the manifest file to be used within the EAR file. If no value if specified, the default location
150      * in the workDirectory is taken. If the file does not exist, a manifest will be generated automatically.
151      */
152     @Parameter
153     private File manifestFile;
154 
155     /**
156      * The location of a custom application.xml file to be used within the EAR file.
157      */
158     @Parameter
159     private String applicationXml;
160 
161     /**
162      * The directory for the generated EAR.
163      */
164     @Parameter( defaultValue = "${project.build.directory}", required = true )
165     private String outputDirectory;
166 
167     /**
168      * The name of the EAR file to generate.
169      */
170     @Parameter( alias = "earName", defaultValue = "${project.build.finalName}", required = true )
171     private String finalName;
172 
173     /**
174      * The comma separated list of artifact's type(s) to unpack by default.
175      */
176     @Parameter
177     private String unpackTypes;
178 
179     /**
180      * Classifier to add to the artifact generated. If given, the artifact will be an attachment instead.
181      */
182     @Parameter
183     private String classifier;
184 
185     /**
186      * A comma separated list of tokens to exclude when packaging the EAR. By default nothing is excluded. Note that you
187      * can use the Java Regular Expressions engine to include and exclude specific pattern using the expression
188      * %regex[]. Hint: read the about (?!Pattern).
189      * 
190      * @since 2.7
191      */
192     @Parameter
193     private String packagingExcludes;
194 
195     /**
196      * A comma separated list of tokens to include when packaging the EAR. By default everything is included. Note that
197      * you can use the Java Regular Expressions engine to include and exclude specific pattern using the expression
198      * %regex[].
199      * 
200      * @since 2.7
201      */
202     @Parameter
203     private String packagingIncludes;
204 
205     /**
206      * Whether to create skinny WARs or not. A skinny WAR is a WAR that does not have all of its dependencies in
207      * WEB-INF/lib. Instead those dependencies are shared between the WARs through the EAR.
208      * 
209      * @since 2.7
210      */
211     @Parameter( property = "maven.ear.skinnyWars", defaultValue = "false" )
212     private boolean skinnyWars;
213 
214     /**
215      * The Jar archiver.
216      */
217     @Component( role = Archiver.class, hint = "jar" )
218     private JarArchiver jarArchiver;
219 
220     /**
221      * The Zip archiver.
222      */
223     @Component( role = Archiver.class, hint = "zip" )
224     private ZipArchiver zipArchiver;
225 
226     /**
227      * The Zip Un archiver.
228      */
229     @Component( role = UnArchiver.class, hint = "zip" )
230     private ZipUnArchiver zipUnArchiver;
231 
232     /**
233      * The archive configuration to use. See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven
234      * Archiver Reference</a>.
235      */
236     @Parameter
237     private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
238 
239     /**
240      */
241     @Component
242     private MavenProjectHelper projectHelper;
243 
244     /**
245      * The archive manager.
246      */
247     @Component
248     private ArchiverManager archiverManager;
249 
250     /**
251      */
252     @Component( role = MavenFileFilter.class, hint = "default" )
253     private MavenFileFilter mavenFileFilter;
254 
255     /**
256      */
257     @Component( role = MavenResourcesFiltering.class, hint = "default" )
258     private MavenResourcesFiltering mavenResourcesFiltering;
259 
260     /**
261      * @since 2.3.2
262      */
263     @Parameter( defaultValue = "${session}", readonly = true, required = true )
264     private MavenSession session;
265 
266     private List<FileUtils.FilterWrapper> filterWrappers;
267 
268     /**
269      * @since 2.9
270      */
271     @Parameter( property = "maven.ear.useJvmChmod", defaultValue = "true" )
272     private boolean useJvmChmod = true;
273 
274     /**
275      * The list of artifacts is checked and if you set this to {@code true} the build will fail if duplicate artifacts
276      * have been found within the build configuration.
277      * 
278      * @since 2.10
279      */
280     // TODO: This can be removed if we change to full unique identifiers in EAR (next major version!)
281     @Parameter( defaultValue = "false", property = "maven.ear.duplicateArtifactsBreakTheBuild" )
282     private boolean duplicateArtifactsBreakTheBuild;
283 
284     private void checkModuleUniqueness()
285         throws MojoExecutionException
286     {
287         ModuleIdentifierValidator miv = new ModuleIdentifierValidator( getModules() );
288         miv.checkForDuplicateArtifacts();
289         if ( miv.existDuplicateArtifacts() )
290         {
291             Map<String, List<EarModule>> duplicateArtifacts = miv.getDuplicateArtifacts();
292             for ( Entry<String, List<EarModule>> entry : duplicateArtifacts.entrySet() )
293             {
294                 getLog().warn( "The artifactId " + entry.getKey() + " exists more than once in the modules list." );
295                 for ( EarModule earModule : entry.getValue() )
296                 {
297                     getLog().warn( " --> " + earModule.getArtifact().getId() + " (" + earModule.getType() + ")" );
298                 }
299             }
300 
301             getLog().warn( "HINT: This can be simply solved by using the <fileNameMapping>full</fileNameMapping>" );
302 
303             if ( duplicateArtifactsBreakTheBuild )
304             {
305                 // CHECKSTYLE_OFF: LineLength
306                 throw new MojoExecutionException(
307                                                   "The build contains duplicate artifacts which result in unpredictable ear content." );
308                 // CHECKSTYLE_ON: LineLength
309             }
310         }
311 
312     }
313 
314     /** {@inheritDoc} */
315     public void execute()
316         throws MojoExecutionException, MojoFailureException
317     {
318         // Initializes ear modules
319         super.execute();
320 
321         zipArchiver.setUseJvmChmod( useJvmChmod );
322         zipUnArchiver.setUseJvmChmod( useJvmChmod );
323 
324         final JavaEEVersion javaEEVersion = JavaEEVersion.getJavaEEVersion( version );
325 
326         // Initializes unpack types
327         List<String> unpackTypesList = createUnpackList();
328 
329         // Copy modules
330         copyModules( javaEEVersion, unpackTypesList );
331 
332         // Copy source files
333         try
334         {
335             File earSourceDir = earSourceDirectory;
336             if ( earSourceDir.exists() )
337             {
338                 getLog().info( "Copy ear sources to " + getWorkDirectory().getAbsolutePath() );
339                 String[] fileNames = getEarFiles( earSourceDir );
340                 for ( String fileName : fileNames )
341                 {
342                     copyFile( new File( earSourceDir, fileName ), new File( getWorkDirectory(), fileName ) );
343                 }
344             }
345 
346             if ( applicationXml != null && !"".equals( applicationXml ) )
347             {
348                 // rename to application.xml
349                 getLog().info( "Including custom application.xml[" + applicationXml + "]" );
350                 File metaInfDir = new File( getWorkDirectory(), META_INF );
351                 copyFile( new File( applicationXml ), new File( metaInfDir, "/application.xml" ) );
352             }
353 
354         }
355         catch ( IOException e )
356         {
357             throw new MojoExecutionException( "Error copying EAR sources", e );
358         }
359         catch ( MavenFilteringException e )
360         {
361             throw new MojoExecutionException( "Error filtering EAR sources", e );
362         }
363 
364         // Check if deployment descriptor is there
365         File ddFile = new File( getWorkDirectory(), APPLICATION_XML_URI );
366         if ( !ddFile.exists() && ( javaEEVersion.lt( JavaEEVersion.FIVE ) ) )
367         {
368             // CHECKSTYLE_OFF: LineLength
369             throw new MojoExecutionException( "Deployment descriptor: " + ddFile.getAbsolutePath() + " does not exist." );
370             // CHECKSTYLE_ON: LineLength
371         }
372 
373         try
374         {
375             File earFile = getEarFile( outputDirectory, finalName, classifier );
376             final MavenArchiver archiver = new EarMavenArchiver( getModules() );
377             final JarArchiver theJarArchiver = getJarArchiver();
378             getLog().debug( "Jar archiver implementation [" + theJarArchiver.getClass().getName() + "]" );
379             archiver.setArchiver( theJarArchiver );
380             archiver.setOutputFile( earFile );
381 
382             // Include custom manifest if necessary
383             includeCustomManifestFile();
384 
385             getLog().debug( "Excluding " + Arrays.asList( getPackagingExcludes() ) + " from the generated EAR." );
386             getLog().debug( "Including " + Arrays.asList( getPackagingIncludes() ) + " in the generated EAR." );
387 
388             archiver.getArchiver().addDirectory( getWorkDirectory(), getPackagingIncludes(), getPackagingExcludes() );
389             archiver.createArchive( session, getProject(), archive );
390 
391             if ( classifier != null )
392             {
393                 projectHelper.attachArtifact( getProject(), "ear", classifier, earFile );
394             }
395             else
396             {
397                 getProject().getArtifact().setFile( earFile );
398             }
399         }
400         catch ( Exception e )
401         {
402             throw new MojoExecutionException( "Error assembling EAR", e );
403         }
404     }
405 
406     private void copyModules( final JavaEEVersion javaEEVersion, List<String> unpackTypesList )
407         throws MojoExecutionException, MojoFailureException
408     {
409         try
410         {
411             // TODO: With the next major release the modules
412             // should be identified by a unique id instead of the
413             // the artifactId's only which means this
414             // check can be removed.
415             // http://jira.codehaus.org/browse/MEAR-209
416             checkModuleUniqueness();
417 
418             for ( EarModule module : getModules() )
419             {
420                 if ( module instanceof JavaModule )
421                 {
422                     getLog().warn( "JavaModule is deprecated (" + module + "), please use JarModule instead." );
423                 }
424                 if ( module instanceof Ejb3Module )
425                 {
426                     getLog().warn( "Ejb3Module is deprecated (" + module + "), please use EjbModule instead." );
427                 }
428                 final File sourceFile = module.getArtifact().getFile();
429                 final File destinationFile = buildDestinationFile( getWorkDirectory(), module.getUri() );
430                 if ( !sourceFile.isFile() )
431                 {
432                     throw new MojoExecutionException( "Cannot copy a directory: " + sourceFile.getAbsolutePath()
433                         + "; Did you package/install " + module.getArtifact() + "?" );
434                 }
435 
436                 if ( destinationFile.getCanonicalPath().equals( sourceFile.getCanonicalPath() ) )
437                 {
438                     getLog().info( "Skipping artifact [" + module + "], as it already exists at [" + module.getUri()
439                                        + "]" );
440                     continue;
441                 }
442 
443                 // If the module is within the unpack list, make sure that no unpack wasn't forced (null or true)
444                 // If the module is not in the unpack list, it should be true
445                 // CHECKSTYLE_OFF: LineLength
446                 if ( ( unpackTypesList.contains( module.getType() ) && ( module.shouldUnpack() == null || module.shouldUnpack() ) )
447                     || ( module.shouldUnpack() != null && module.shouldUnpack() ) )
448                 // CHECKSTYLE_ON: LineLength
449                 {
450                     getLog().info( "Copying artifact [" + module + "] to [" + module.getUri() + "] (unpacked)" );
451                     // Make sure that the destination is a directory to avoid plexus nasty stuff :)
452                     destinationFile.mkdirs();
453                     unpack( sourceFile, destinationFile );
454 
455                     if ( skinnyWars && module.changeManifestClasspath() )
456                     {
457                         changeManifestClasspath( module, destinationFile, javaEEVersion );
458                     }
459                 }
460                 else
461                 {
462                     if ( sourceFile.lastModified() > destinationFile.lastModified() )
463                     {
464                         getLog().info( "Copying artifact [" + module + "] to [" + module.getUri() + "]" );
465                         FileUtils.copyFile( sourceFile, destinationFile );
466 
467                         if ( skinnyWars && module.changeManifestClasspath() )
468                         {
469                             changeManifestClasspath( module, destinationFile, javaEEVersion );
470                         }
471                     }
472                     else
473                     {
474                         getLog().debug( "Skipping artifact [" + module + "], as it is already up to date at ["
475                                             + module.getUri() + "]" );
476                     }
477                 }
478             }
479         }
480         catch ( IOException e )
481         {
482             throw new MojoExecutionException( "Error copying EAR modules", e );
483         }
484         catch ( ArchiverException e )
485         {
486             throw new MojoExecutionException( "Error unpacking EAR modules", e );
487         }
488         catch ( NoSuchArchiverException e )
489         {
490             throw new MojoExecutionException( "No Archiver found for EAR modules", e );
491         }
492     }
493 
494     private List<String> createUnpackList()
495         throws MojoExecutionException
496     {
497         List<String> unpackTypesList = new ArrayList<String>();
498         if ( unpackTypes != null )
499         {
500             unpackTypesList = Arrays.asList( unpackTypes.split( "," ) );
501             for ( String type : unpackTypesList )
502             {
503                 if ( !EarModuleFactory.STANDARD_ARTIFACT_TYPE.contains( type ) )
504                 {
505                     throw new MojoExecutionException( "Invalid type [" + type + "] supported types are "
506                         + EarModuleFactory.STANDARD_ARTIFACT_TYPE );
507                 }
508             }
509             getLog().debug( "Initialized unpack types " + unpackTypesList );
510         }
511         return unpackTypesList;
512     }
513 
514     /**
515      * @return {@link #applicationXml}
516      */
517     public String getApplicationXml()
518     {
519         return applicationXml;
520     }
521 
522     /**
523      * @param applicationXml {@link #applicationXml}
524      */
525     public void setApplicationXml( String applicationXml )
526     {
527         this.applicationXml = applicationXml;
528     }
529 
530     /**
531      * Returns a string array of the excludes to be used when assembling/copying the ear.
532      * 
533      * @return an array of tokens to exclude
534      */
535     protected String[] getExcludes()
536     {
537         List<String> excludeList = new ArrayList<String>( FileUtils.getDefaultExcludesAsList() );
538         if ( earSourceExcludes != null && !"".equals( earSourceExcludes ) )
539         {
540             excludeList.addAll( Arrays.asList( StringUtils.split( earSourceExcludes, "," ) ) );
541         }
542 
543         // if applicationXml is specified, omit the one in the source directory
544         if ( getApplicationXml() != null && !"".equals( getApplicationXml() ) )
545         {
546             excludeList.add( "**/" + META_INF + "/application.xml" );
547         }
548 
549         return excludeList.toArray( new String[excludeList.size()] );
550     }
551 
552     /**
553      * Returns a string array of the includes to be used when assembling/copying the ear.
554      * 
555      * @return an array of tokens to include
556      */
557     protected String[] getIncludes()
558     {
559         return StringUtils.split( StringUtils.defaultString( earSourceIncludes ), "," );
560     }
561 
562     /**
563      * @return The array with the packaging excludes.
564      */
565     public String[] getPackagingExcludes()
566     {
567         if ( StringUtils.isEmpty( packagingExcludes ) )
568         {
569             return new String[0];
570         }
571         else
572         {
573             return StringUtils.split( packagingExcludes, "," );
574         }
575     }
576 
577     /**
578      * @param packagingExcludes {@link #packagingExcludes}
579      */
580     public void setPackagingExcludes( String packagingExcludes )
581     {
582         this.packagingExcludes = packagingExcludes;
583     }
584 
585     /**
586      * @return The arrays with the includes.
587      */
588     public String[] getPackagingIncludes()
589     {
590         if ( StringUtils.isEmpty( packagingIncludes ) )
591         {
592             return new String[] { "**" };
593         }
594         else
595         {
596             return StringUtils.split( packagingIncludes, "," );
597         }
598     }
599 
600     /**
601      * @param packagingIncludes {@link #packagingIncludes}
602      */
603     public void setPackagingIncludes( String packagingIncludes )
604     {
605         this.packagingIncludes = packagingIncludes;
606     }
607 
608     private static File buildDestinationFile( File buildDir, String uri )
609     {
610         return new File( buildDir, uri );
611     }
612 
613     private void includeCustomManifestFile()
614     {
615         if ( manifestFile == null )
616         {
617             manifestFile = new File( getWorkDirectory(), "META-INF/MANIFEST.MF" );
618         }
619 
620         if ( !manifestFile.exists() )
621         {
622             getLog().info( "Could not find manifest file: " + manifestFile + " - Generating one" );
623         }
624         else
625         {
626             getLog().info( "Including custom manifest file [" + manifestFile + "]" );
627             archive.setManifestFile( manifestFile );
628         }
629     }
630 
631     /**
632      * Returns the EAR file to generate, based on an optional classifier.
633      * 
634      * @param basedir the output directory
635      * @param finalName the name of the ear file
636      * @param classifier an optional classifier
637      * @return the EAR file to generate
638      */
639     private static File getEarFile( String basedir, String finalName, String classifier )
640     {
641         if ( classifier == null )
642         {
643             classifier = "";
644         }
645         else if ( classifier.trim().length() > 0 && !classifier.startsWith( "-" ) )
646         {
647             classifier = "-" + classifier;
648         }
649 
650         return new File( basedir, finalName + classifier + ".ear" );
651     }
652 
653     /**
654      * Returns a list of filenames that should be copied over to the destination directory.
655      * 
656      * @param sourceDir the directory to be scanned
657      * @return the array of filenames, relative to the sourceDir
658      */
659     private String[] getEarFiles( File sourceDir )
660     {
661         DirectoryScanner scanner = new DirectoryScanner();
662         scanner.setBasedir( sourceDir );
663         scanner.setExcludes( getExcludes() );
664         scanner.addDefaultExcludes();
665 
666         scanner.setIncludes( getIncludes() );
667 
668         scanner.scan();
669 
670         return scanner.getIncludedFiles();
671     }
672 
673     /**
674      * Unpacks the module into the EAR structure.
675      * 
676      * @param source File to be unpacked.
677      * @param destDir Location where to put the unpacked files.
678      * @throws NoSuchArchiverException In case of we don't have an appropriate archiver.
679      * @throws IOException In case of a general IOException.
680      */
681     public void unpack( File source, File destDir )
682         throws NoSuchArchiverException, IOException
683     {
684         UnArchiver unArchiver = archiverManager.getUnArchiver( "zip" );
685         unArchiver.setSourceFile( source );
686         unArchiver.setDestDirectory( destDir );
687 
688         // Extract the module
689         unArchiver.extract();
690     }
691 
692     /**
693      * Returns the {@link JarArchiver} implementation used to package the EAR file.
694      * <p/>
695      * By default the archiver is obtained from the Plexus container.
696      * 
697      * @return the archiver
698      */
699     protected JarArchiver getJarArchiver()
700     {
701         return jarArchiver;
702     }
703 
704     private void copyFile( File source, File target )
705         throws MavenFilteringException, IOException, MojoExecutionException
706     {
707         if ( filtering && !isNonFilteredExtension( source.getName() ) )
708         {
709             // Silly that we have to do this ourselves
710             if ( target.getParentFile() != null && !target.getParentFile().exists() )
711             {
712                 target.getParentFile().mkdirs();
713             }
714 
715             mavenFileFilter.copyFile( source, target, true, getFilterWrappers(), null );
716         }
717         else
718         {
719             FileUtils.copyFile( source, target );
720         }
721     }
722 
723     /**
724      * @param fileName The name of the file which should be checked.
725      * @return {@code true} if the name is part of the non filtered extensions {@code false} otherwise.
726      */
727     public boolean isNonFilteredExtension( String fileName )
728     {
729         return !mavenResourcesFiltering.filteredFileExtension( fileName, nonFilteredFileExtensions );
730     }
731 
732     private List<FileUtils.FilterWrapper> getFilterWrappers()
733         throws MojoExecutionException
734     {
735         if ( filterWrappers == null )
736         {
737             try
738             {
739                 MavenResourcesExecution mavenResourcesExecution = new MavenResourcesExecution();
740                 mavenResourcesExecution.setMavenProject( getProject() );
741                 mavenResourcesExecution.setEscapedBackslashesInFilePath( escapedBackslashesInFilePath );
742                 mavenResourcesExecution.setFilters( filters );
743                 mavenResourcesExecution.setEscapeString( escapeString );
744 
745                 filterWrappers = mavenFileFilter.getDefaultFilterWrappers( mavenResourcesExecution );
746             }
747             catch ( MavenFilteringException e )
748             {
749                 getLog().error( "Fail to build filtering wrappers " + e.getMessage() );
750                 throw new MojoExecutionException( e.getMessage(), e );
751             }
752         }
753         return filterWrappers;
754     }
755 
756     private void changeManifestClasspath( EarModule module, File original, JavaEEVersion javaEEVersion )
757         throws MojoFailureException
758     {
759         try
760         {
761             File workDirectory;
762 
763             // Handle the case that the destination might be a directory (project-038)
764             if ( original.isFile() )
765             {
766                 // Create a temporary work directory
767                 // MEAR-167 use uri as directory to prevent merging of artifacts with the same artifactId
768                 workDirectory = new File( new File( getTempFolder(), "temp" ), module.getUri() );
769                 workDirectory.mkdirs();
770                 getLog().debug( "Created a temporary work directory: " + workDirectory.getAbsolutePath() );
771 
772                 // Unpack the archive to a temporary work directory
773                 zipUnArchiver.setSourceFile( original );
774                 zipUnArchiver.setDestDirectory( workDirectory );
775                 zipUnArchiver.extract();
776             }
777             else
778             {
779                 workDirectory = original;
780             }
781 
782             // Create a META-INF/MANIFEST.MF file if it doesn't exist (project-038)
783             File metaInfDirectory = new File( workDirectory, "META-INF" );
784             boolean newMetaInfCreated = metaInfDirectory.mkdirs();
785             if ( newMetaInfCreated )
786             {
787                 // CHECKSTYLE_OFF: LineLength
788                 getLog().debug( "This project did not have a META-INF directory before, so a new directory was created." );
789                 // CHECKSTYLE_ON: LineLength
790             }
791             File newCreatedManifestFile = new File( metaInfDirectory, "MANIFEST.MF" );
792             boolean newManifestCreated = newCreatedManifestFile.createNewFile();
793             if ( newManifestCreated )
794             {
795                 // CHECKSTYLE_OFF: LineLength
796                 getLog().debug( "This project did not have a META-INF/MANIFEST.MF file before, so a new file was created." );
797                 // CHECKSTYLE_ON: LineLength
798             }
799 
800             // Read the manifest from disk
801             Manifest mf = new Manifest( new FileReader( newCreatedManifestFile ) );
802             Attribute classPath = mf.getMainSection().getAttribute( "Class-Path" );
803             List<String> classPathElements = new ArrayList<String>();
804 
805             if ( classPath != null )
806             {
807                 classPathElements.addAll( Arrays.asList( classPath.getValue().split( " " ) ) );
808             }
809             else
810             {
811                 classPath = new Attribute( "Class-Path", "" );
812             }
813 
814             // Modify the classpath entries in the manifest
815             for ( EarModule o : getModules() )
816             {
817                 if ( o instanceof JarModule )
818                 {
819                     JarModule jm = (JarModule) o;
820 
821                     if ( module.getLibDir() != null )
822                     {
823                         // MEAR-189:
824                         // We use the original name, cause in case of fileNameMapping to no-version/full
825                         // we could not not delete it and it will end up in the resulting EAR and the WAR
826                         // will not be cleaned up.
827                         File artifact =
828                             new File( new File( workDirectory, module.getLibDir() ), jm.getOriginalBundleFileName() );
829 
830                         if ( artifact.exists() )
831                         {
832                             getLog().debug( " -> Artifact to delete: " + artifact );
833                             if ( !artifact.delete() )
834                             {
835                                 getLog().error( "Could not delete '" + artifact + "'" );
836                             }
837                         }
838                     }
839 
840                     if ( classPathElements.contains( jm.getBundleFileName() ) )
841                     {
842                         classPathElements.set( classPathElements.indexOf( jm.getBundleFileName() ), jm.getUri() );
843                     }
844                     else
845                     {
846                         if ( !skipClassPathModification )
847                         {
848                             classPathElements.add( jm.getUri() );
849                         }
850                         else
851                         {
852                             if ( javaEEVersion.lt( JavaEEVersion.FIVE ) || defaultLibBundleDir == null )
853                             {
854                                 classPathElements.add( jm.getUri() );
855                             }
856                         }
857                     }
858                 }
859             }
860             classPath.setValue( StringUtils.join( classPathElements.iterator(), " " ) );
861             mf.getMainSection().addConfiguredAttribute( classPath );
862 
863             // Write the manifest to disk
864             PrintWriter pw = new PrintWriter( newCreatedManifestFile );
865             mf.write( pw );
866             pw.close();
867 
868             if ( original.isFile() )
869             {
870                 // Pack up the archive again from the work directory
871                 if ( !original.delete() )
872                 {
873                     getLog().error( "Could not delete original artifact file " + original );
874                 }
875 
876                 getLog().debug( "Zipping module" );
877                 zipArchiver.setDestFile( original );
878                 zipArchiver.addDirectory( workDirectory );
879                 zipArchiver.createArchive();
880             }
881         }
882         catch ( ManifestException e )
883         {
884             throw new MojoFailureException( e.getMessage() );
885         }
886         catch ( ZipException e )
887         {
888             throw new MojoFailureException( e.getMessage() );
889         }
890         catch ( IOException e )
891         {
892             throw new MojoFailureException( e.getMessage() );
893         }
894         catch ( ArchiverException e )
895         {
896             throw new MojoFailureException( e.getMessage() );
897         }
898     }
899 }