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