1 package org.apache.maven.plugin.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.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
68
69
70
71
72
73 @Mojo( name = "ear", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST )
74
75 public class EarMojo
76 extends AbstractEarMojo
77 {
78
79
80
81 @Parameter( defaultValue = "${basedir}/src/main/application", required = true )
82 private File earSourceDirectory;
83
84
85
86
87 @Parameter( alias = "includes", defaultValue = "**" )
88 private String earSourceIncludes;
89
90
91
92
93 @Parameter( alias = "excludes" )
94 private String earSourceExcludes;
95
96
97
98
99
100
101 @Parameter( defaultValue = "false" )
102 private boolean filtering;
103
104
105
106
107
108
109 @Parameter
110 private List<String> filters;
111
112
113
114
115
116
117 @Parameter
118 private List<String> nonFilteredFileExtensions;
119
120
121
122
123
124
125 @Parameter( property = "maven.ear.escapedBackslashesInFilePath", defaultValue = "false" )
126 private boolean escapedBackslashesInFilePath;
127
128
129
130
131
132
133 @Parameter( property = "maven.ear.escapeString" )
134 protected String escapeString;
135
136
137
138
139
140
141
142
143
144
145 @Parameter( defaultValue = "false" )
146 private boolean skipClassPathModification;
147
148
149
150
151
152 @Parameter
153 private File manifestFile;
154
155
156
157
158 @Parameter
159 private String applicationXml;
160
161
162
163
164 @Parameter( defaultValue = "${project.build.directory}", required = true )
165 private String outputDirectory;
166
167
168
169
170 @Parameter( alias = "earName", defaultValue = "${project.build.finalName}", required = true )
171 private String finalName;
172
173
174
175
176 @Parameter
177 private String unpackTypes;
178
179
180
181
182 @Parameter
183 private String classifier;
184
185
186
187
188
189
190
191
192 @Parameter
193 private String packagingExcludes;
194
195
196
197
198
199
200
201
202 @Parameter
203 private String packagingIncludes;
204
205
206
207
208
209
210
211 @Parameter( property = "maven.ear.skinnyWars", defaultValue = "false" )
212 private boolean skinnyWars;
213
214
215
216
217 @Component( role = Archiver.class, hint = "jar" )
218 private JarArchiver jarArchiver;
219
220
221
222
223 @Component( role = Archiver.class, hint = "zip" )
224 private ZipArchiver zipArchiver;
225
226
227
228
229 @Component( role = UnArchiver.class, hint = "zip" )
230 private ZipUnArchiver zipUnArchiver;
231
232
233
234
235
236 @Parameter
237 private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
238
239
240
241 @Component
242 private MavenProjectHelper projectHelper;
243
244
245
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
262
263 @Parameter( defaultValue = "${session}", readonly = true, required = true )
264 private MavenSession session;
265
266 private List<FileUtils.FilterWrapper> filterWrappers;
267
268
269
270
271 @Parameter( property = "maven.ear.useJvmChmod", defaultValue = "true" )
272 private boolean useJvmChmod = true;
273
274
275
276
277
278
279
280
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
306 throw new MojoExecutionException(
307 "The build contains duplicate artifacts which result in unpredictable ear content." );
308
309 }
310 }
311
312 }
313
314
315 public void execute()
316 throws MojoExecutionException, MojoFailureException
317 {
318
319 super.execute();
320
321 zipArchiver.setUseJvmChmod( useJvmChmod );
322 zipUnArchiver.setUseJvmChmod( useJvmChmod );
323
324 final JavaEEVersion javaEEVersion = JavaEEVersion.getJavaEEVersion( version );
325
326
327 List<String> unpackTypesList = createUnpackList();
328
329
330 copyModules( javaEEVersion, unpackTypesList );
331
332
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
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
365 File ddFile = new File( getWorkDirectory(), APPLICATION_XML_URI );
366 if ( !ddFile.exists() && ( javaEEVersion.lt( JavaEEVersion.FIVE ) ) )
367 {
368
369 throw new MojoExecutionException( "Deployment descriptor: " + ddFile.getAbsolutePath() + " does not exist." );
370
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
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
412
413
414
415
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
444
445
446 if ( ( unpackTypesList.contains( module.getType() ) && ( module.shouldUnpack() == null || module.shouldUnpack() ) )
447 || ( module.shouldUnpack() != null && module.shouldUnpack() ) )
448
449 {
450 getLog().info( "Copying artifact [" + module + "] to [" + module.getUri() + "] (unpacked)" );
451
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
516
517 public String getApplicationXml()
518 {
519 return applicationXml;
520 }
521
522
523
524
525 public void setApplicationXml( String applicationXml )
526 {
527 this.applicationXml = applicationXml;
528 }
529
530
531
532
533
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
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
554
555
556
557 protected String[] getIncludes()
558 {
559 return StringUtils.split( StringUtils.defaultString( earSourceIncludes ), "," );
560 }
561
562
563
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
579
580 public void setPackagingExcludes( String packagingExcludes )
581 {
582 this.packagingExcludes = packagingExcludes;
583 }
584
585
586
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
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
633
634
635
636
637
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
655
656
657
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
675
676
677
678
679
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
689 unArchiver.extract();
690 }
691
692
693
694
695
696
697
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
710 if ( target.getParentFile() != null && !target.getParentFile().exists() )
711 {
712 target.getParentFile().mkdirs();
713 }
714
715 mavenFileFilter.copyFile( source, target, true, getFilterWrappers(), encoding );
716 }
717 else
718 {
719 FileUtils.copyFile( source, target );
720 }
721 }
722
723
724
725
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
764 if ( original.isFile() )
765 {
766
767
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
773 zipUnArchiver.setSourceFile( original );
774 zipUnArchiver.setDestDirectory( workDirectory );
775 zipUnArchiver.extract();
776 }
777 else
778 {
779 workDirectory = original;
780 }
781
782
783 File metaInfDirectory = new File( workDirectory, "META-INF" );
784 boolean newMetaInfCreated = metaInfDirectory.mkdirs();
785 if ( newMetaInfCreated )
786 {
787
788 getLog().debug( "This project did not have a META-INF directory before, so a new directory was created." );
789
790 }
791 File newCreatedManifestFile = new File( metaInfDirectory, "MANIFEST.MF" );
792 boolean newManifestCreated = newCreatedManifestFile.createNewFile();
793 if ( newManifestCreated )
794 {
795
796 getLog().debug( "This project did not have a META-INF/MANIFEST.MF file before, so a new file was created." );
797
798 }
799
800
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
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
824
825
826
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
864 PrintWriter pw = new PrintWriter( newCreatedManifestFile );
865 mf.write( pw );
866 pw.close();
867
868 if ( original.isFile() )
869 {
870
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 }