1 package org.apache.maven.plugins.ear;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
65
66
67
68
69 @Mojo( name = "ear", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST )
70
71 public class EarMojo
72 extends AbstractEarMojo
73 {
74
75
76
77 @Parameter( defaultValue = "${basedir}/src/main/application", required = true )
78 private File earSourceDirectory;
79
80
81
82
83 @Parameter( alias = "includes", defaultValue = "**" )
84 private String earSourceIncludes;
85
86
87
88
89 @Parameter( alias = "excludes" )
90 private String earSourceExcludes;
91
92
93
94
95
96
97 @Parameter( defaultValue = "false" )
98 private boolean filtering;
99
100
101
102
103
104
105 @Parameter
106 private List<String> filters;
107
108
109
110
111
112
113 @Parameter
114 private List<String> nonFilteredFileExtensions;
115
116
117
118
119
120
121 @Parameter( defaultValue = "false" )
122 private boolean escapedBackslashesInFilePath;
123
124
125
126
127
128
129 @Parameter
130 protected String escapeString;
131
132
133
134
135
136
137
138
139 @Parameter( defaultValue = "false" )
140 private boolean skipClassPathModification;
141
142
143
144
145 @Parameter
146 private String applicationXml;
147
148
149
150
151 @Parameter( defaultValue = "${project.build.directory}", required = true )
152 private String outputDirectory;
153
154
155
156
157 @Parameter( defaultValue = "${project.build.finalName}", required = true, readonly = true )
158 private String finalName;
159
160
161
162
163 @Parameter
164 private String unpackTypes;
165
166
167
168
169 @Parameter
170 private String classifier;
171
172
173
174
175
176
177
178
179 @Parameter
180 private String packagingExcludes;
181
182
183
184
185
186
187
188
189 @Parameter
190 private String packagingIncludes;
191
192
193
194
195
196
197
198 @Parameter( defaultValue = "false" )
199 private boolean skinnyWars;
200
201
202
203
204 @Component( role = Archiver.class, hint = "jar" )
205 private JarArchiver jarArchiver;
206
207
208
209
210 @Component( role = Archiver.class, hint = "zip" )
211 private ZipArchiver zipArchiver;
212
213
214
215
216 @Component( role = UnArchiver.class, hint = "zip" )
217 private ZipUnArchiver zipUnArchiver;
218
219
220
221
222
223 @Parameter
224 private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
225
226
227
228 @Component
229 private MavenProjectHelper projectHelper;
230
231
232
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
249
250 @Parameter( defaultValue = "${session}", readonly = true, required = true )
251 private MavenSession session;
252
253 private List<FileUtils.FilterWrapper> filterWrappers;
254
255
256
257
258 @Parameter( defaultValue = "true" )
259 private boolean useJvmChmod = true;
260
261
262 public void execute()
263 throws MojoExecutionException, MojoFailureException
264 {
265
266 super.execute();
267
268 zipArchiver.setUseJvmChmod( useJvmChmod );
269 zipUnArchiver.setUseJvmChmod( useJvmChmod );
270
271 final JavaEEVersion javaEEVersion = JavaEEVersion.getJavaEEVersion( version );
272
273
274 List<String> unpackTypesList = createUnpackList();
275
276
277 copyModules( javaEEVersion, unpackTypesList );
278
279
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
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
312 File ddFile = new File( getWorkDirectory(), APPLICATION_XML_URI );
313 if ( !ddFile.exists() && ( javaEEVersion.lt( JavaEEVersion.FIVE ) ) )
314 {
315
316 throw new MojoExecutionException( "Deployment descriptor: " + ddFile.getAbsolutePath()
317 + " does not exist." );
318
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
371 continue;
372 }
373
374
375
376
377 if ( ( unpackTypesList.contains( module.getType() )
378 && ( module.shouldUnpack() == null || module.shouldUnpack() ) )
379 || ( module.shouldUnpack() != null && module.shouldUnpack() ) )
380
381 {
382 getLog().info( "Copying artifact [" + module + "] to [" + module.getUri() + "] (unpacked)" );
383
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
448
449 public String getApplicationXml()
450 {
451 return applicationXml;
452 }
453
454
455
456
457 public void setApplicationXml( String applicationXml )
458 {
459 this.applicationXml = applicationXml;
460 }
461
462
463
464
465
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
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
486
487
488
489 protected String[] getIncludes()
490 {
491 return StringUtils.split( StringUtils.defaultString( earSourceIncludes ), "," );
492 }
493
494
495
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
511
512 public void setPackagingExcludes( String packagingExcludes )
513 {
514 this.packagingExcludes = packagingExcludes;
515 }
516
517
518
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
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
547
548
549
550
551
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
569
570
571
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
589
590
591
592
593
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
603 unArchiver.extract();
604 }
605
606
607
608
609
610
611
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
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
639
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
678 if ( original.isFile() )
679 {
680
681
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
687 zipUnArchiver.setSourceFile( original );
688 zipUnArchiver.setDestDirectory( workDirectory );
689 zipUnArchiver.extract();
690 }
691 else
692 {
693 workDirectory = original;
694 }
695
696
697 File metaInfDirectory = new File( workDirectory, "META-INF" );
698 boolean newMetaInfCreated = metaInfDirectory.mkdirs();
699 if ( newMetaInfCreated )
700 {
701
702 getLog().debug( "This project did not have a META-INF directory before, so a new directory was created." );
703
704 }
705 File newCreatedManifestFile = new File( metaInfDirectory, "MANIFEST.MF" );
706 boolean newManifestCreated = newCreatedManifestFile.createNewFile();
707 if ( newManifestCreated )
708 {
709
710 getLog().debug( "This project did not have a META-INF/MANIFEST.MF file before, so a new file was created." );
711
712 }
713
714
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
729 for ( JarModule jm : getAllJarModules() )
730 {
731 if ( module.getLibDir() != null )
732 {
733
734
735
736
737
738 File artifact = new File( new File( workDirectory, module.getLibDir() ),
739 module.getArtifact().getFile().getName() );
740
741
742
743
744
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
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
802 PrintWriter pw = new PrintWriter( newCreatedManifestFile );
803 mf.write( pw );
804 pw.close();
805
806 if ( original.isFile() )
807 {
808
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 }