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