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