1 package org.apache.maven.plugins.war;
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.IOException;
24 import java.nio.file.FileVisitResult;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.SimpleFileVisitor;
28 import java.nio.file.attribute.BasicFileAttributes;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35
36 import org.apache.maven.archiver.MavenArchiveConfiguration;
37 import org.apache.maven.artifact.factory.ArtifactFactory;
38 import org.apache.maven.execution.MavenSession;
39 import org.apache.maven.model.Resource;
40 import org.apache.maven.plugin.AbstractMojo;
41 import org.apache.maven.plugin.MojoExecutionException;
42 import org.apache.maven.plugin.MojoFailureException;
43 import org.apache.maven.plugin.logging.Log;
44 import org.apache.maven.plugins.annotations.Component;
45 import org.apache.maven.plugins.annotations.Parameter;
46 import org.apache.maven.plugins.war.overlay.OverlayManager;
47 import org.apache.maven.plugins.war.packaging.CopyUserManifestTask;
48 import org.apache.maven.plugins.war.packaging.OverlayPackagingTask;
49 import org.apache.maven.plugins.war.packaging.WarPackagingContext;
50 import org.apache.maven.plugins.war.packaging.WarPackagingTask;
51 import org.apache.maven.plugins.war.packaging.WarProjectPackagingTask;
52 import org.apache.maven.plugins.war.util.WebappStructure;
53 import org.apache.maven.project.MavenProject;
54 import org.apache.maven.shared.filtering.MavenFileFilter;
55 import org.apache.maven.shared.filtering.MavenFilteringException;
56 import org.apache.maven.shared.filtering.MavenResourcesExecution;
57 import org.apache.maven.shared.filtering.MavenResourcesFiltering;
58 import org.apache.maven.shared.utils.StringUtils;
59 import org.apache.maven.shared.utils.io.FileUtils;
60 import org.codehaus.plexus.archiver.Archiver;
61 import org.codehaus.plexus.archiver.jar.JarArchiver;
62 import org.codehaus.plexus.archiver.manager.ArchiverManager;
63
64
65
66
67 public abstract class AbstractWarMojo
68 extends AbstractMojo
69 {
70 private static final String META_INF = "META-INF";
71
72 private static final String WEB_INF = "WEB-INF";
73
74
75
76
77
78
79
80
81
82
83 @Parameter
84 protected Boolean failOnMissingWebXml;
85
86
87
88
89 @Parameter( defaultValue = "${project}", readonly = true, required = true )
90 private MavenProject project;
91
92
93
94
95 @Parameter( defaultValue = "${project.build.outputDirectory}", required = true, readonly = true )
96 private File classesDirectory;
97
98
99
100
101
102
103
104
105 @Parameter( defaultValue = "false" )
106 private boolean archiveClasses;
107
108
109
110
111
112
113 @Parameter( defaultValue = "${project.build.sourceEncoding}" )
114 private String resourceEncoding;
115
116
117
118
119 @Component( role = Archiver.class, hint = "jar" )
120 private JarArchiver jarArchiver;
121
122
123
124
125 @Parameter( defaultValue = "${project.build.directory}/${project.build.finalName}", required = true )
126 private File webappDirectory;
127
128
129
130
131 @Parameter( defaultValue = "${basedir}/src/main/webapp", required = true )
132 private File warSourceDirectory;
133
134
135
136
137 @Parameter
138 private Resource[] webResources;
139
140
141
142
143 @Parameter
144 private List<String> filters;
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 @Parameter
168 private LinkedHashSet<String> delimiters;
169
170
171
172
173
174
175 @Parameter( defaultValue = "true" )
176 private boolean useDefaultDelimiters;
177
178
179
180
181 @Parameter
182 private File webXml;
183
184
185
186
187
188
189 @Parameter
190 private File containerConfigXML;
191
192
193
194
195 @Parameter( defaultValue = "${project.build.directory}/war/work", required = true )
196 private File workDirectory;
197
198
199
200
201
202
203
204 @Parameter
205 private String outputFileNameMapping;
206
207
208
209 @Component( role = ArtifactFactory.class )
210 private ArtifactFactory artifactFactory;
211
212
213
214
215 @Component( role = ArchiverManager.class )
216 private ArchiverManager archiverManager;
217
218
219
220 @Component( role = MavenFileFilter.class, hint = "default" )
221 private MavenFileFilter mavenFileFilter;
222
223
224
225 @Component( role = MavenResourcesFiltering.class, hint = "default" )
226 private MavenResourcesFiltering mavenResourcesFiltering;
227
228
229
230
231 @Parameter( defaultValue = "**" )
232 private String warSourceIncludes;
233
234
235
236
237 @Parameter
238 private String warSourceExcludes;
239
240
241
242
243
244
245 @Parameter
246 private String dependentWarIncludes = StringUtils.join( Overlay.DEFAULT_INCLUDES, "," );
247
248
249
250
251
252
253 @Parameter
254 private String dependentWarExcludes = StringUtils.join( Overlay.DEFAULT_EXCLUDES, "," );
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273 @Parameter
274 private List<Overlay> overlays = new ArrayList<>();
275
276
277
278
279
280
281
282 @Parameter
283 private List<String> nonFilteredFileExtensions;
284
285
286
287
288 @Parameter( defaultValue = "${session}", readonly = true, required = true )
289 private MavenSession session;
290
291
292
293
294
295
296 @Parameter( defaultValue = "false" )
297 private boolean filteringDeploymentDescriptors;
298
299
300
301
302
303
304
305 @Parameter( defaultValue = "false" )
306 private boolean escapedBackslashesInFilePath;
307
308
309
310
311
312
313
314 @Parameter
315 protected String escapeString;
316
317
318
319
320
321
322
323 @Parameter( defaultValue = "true" )
324 private boolean recompressZippedFiles;
325
326
327
328
329 @Parameter( defaultValue = "false" )
330 private boolean includeEmptyDirectories;
331
332
333
334
335
336
337 @Parameter( defaultValue = "false" )
338 private boolean supportMultiLineFiltering;
339
340
341
342
343
344
345 @Parameter( defaultValue = "true" )
346 private boolean useJvmChmod;
347
348
349
350
351
352 @Parameter
353 private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
354
355 private final Overlay currentProjectOverlay = Overlay.createInstance();
356
357
358
359
360 public Overlay getCurrentProjectOverlay()
361 {
362 return currentProjectOverlay;
363 }
364
365
366
367
368
369
370 protected String[] getExcludes()
371 {
372 List<String> excludeList = new ArrayList<>();
373 if ( StringUtils.isNotEmpty( warSourceExcludes ) )
374 {
375 excludeList.addAll( Arrays.asList( StringUtils.split( warSourceExcludes, "," ) ) );
376 }
377
378
379 if ( webXml != null && StringUtils.isNotEmpty( webXml.getName() ) )
380 {
381 excludeList.add( "**/" + WEB_INF + "/web.xml" );
382 }
383
384
385 if ( containerConfigXML != null && StringUtils.isNotEmpty( containerConfigXML.getName() ) )
386 {
387 excludeList.add( "**/" + META_INF + "/" + containerConfigXML.getName() );
388 }
389
390 return excludeList.toArray( new String[excludeList.size()] );
391 }
392
393
394
395
396
397
398 protected String[] getIncludes()
399 {
400 return StringUtils.split( StringUtils.defaultString( warSourceIncludes ), "," );
401 }
402
403
404
405
406
407
408 protected String[] getDependentWarExcludes()
409 {
410 return StringUtils.split( StringUtils.defaultString( dependentWarExcludes ), "," );
411 }
412
413
414
415
416
417
418 protected String[] getDependentWarIncludes()
419 {
420 return StringUtils.split( StringUtils.defaultString( dependentWarIncludes ), "," );
421 }
422
423
424
425
426
427
428 public void buildExplodedWebapp( File webapplicationDirectory )
429 throws MojoExecutionException, MojoFailureException
430 {
431 webapplicationDirectory.mkdirs();
432
433 try
434 {
435 buildWebapp( project, webapplicationDirectory );
436 }
437 catch ( IOException e )
438 {
439 throw new MojoExecutionException( "Could not build webapp", e );
440 }
441 }
442
443
444
445
446
447
448
449
450
451
452
453 public void buildWebapp( MavenProject mavenProject, File webapplicationDirectory )
454 throws MojoExecutionException, MojoFailureException, IOException
455 {
456
457 WebappStructure structure = new WebappStructure( mavenProject.getDependencies() );
458
459
460 final long startTime = System.currentTimeMillis();
461 getLog().info( "Assembling webapp [" + mavenProject.getArtifactId() + "] in [" + webapplicationDirectory + "]" );
462
463 final OverlayManager overlayManager =
464 new OverlayManager( overlays, mavenProject, getDependentWarIncludes(), getDependentWarExcludes(),
465 currentProjectOverlay );
466
467 List<FileUtils.FilterWrapper> defaultFilterWrappers;
468 try
469 {
470 MavenResourcesExecution mavenResourcesExecution = new MavenResourcesExecution();
471 mavenResourcesExecution.setEscapeString( escapeString );
472 mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering );
473 mavenResourcesExecution.setMavenProject( mavenProject );
474
475
476 mavenResourcesExecution.setDelimiters( delimiters, useDefaultDelimiters );
477
478 if ( nonFilteredFileExtensions != null )
479 {
480 mavenResourcesExecution.setNonFilteredFileExtensions( nonFilteredFileExtensions );
481 }
482
483 if ( filters == null )
484 {
485 filters = getProject().getBuild().getFilters();
486 }
487 mavenResourcesExecution.setFilters( filters );
488 mavenResourcesExecution.setEscapedBackslashesInFilePath( escapedBackslashesInFilePath );
489 mavenResourcesExecution.setMavenSession( this.session );
490 mavenResourcesExecution.setEscapeString( this.escapeString );
491 mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering );
492
493 defaultFilterWrappers = mavenFileFilter.getDefaultFilterWrappers( mavenResourcesExecution );
494
495 }
496 catch ( MavenFilteringException e )
497 {
498 getLog().error( "fail to build filtering wrappers " + e.getMessage() );
499 throw new MojoExecutionException( e.getMessage(), e );
500 }
501
502 final WarPackagingContext context =
503 new DefaultWarPackagingContext( webapplicationDirectory, structure, overlayManager, defaultFilterWrappers,
504 getNonFilteredFileExtensions(), filteringDeploymentDescriptors,
505 this.artifactFactory, resourceEncoding, useJvmChmod, failOnMissingWebXml );
506
507 final List<WarPackagingTask> packagingTasks = getPackagingTasks( overlayManager );
508
509 for ( WarPackagingTask warPackagingTask : packagingTasks )
510 {
511 warPackagingTask.performPackaging( context );
512 }
513
514 getLog().debug( "Webapp assembled in [" + ( System.currentTimeMillis() - startTime ) + " msecs]" );
515
516 }
517
518
519
520
521
522
523
524
525
526 private List<WarPackagingTask> getPackagingTasks( OverlayManager overlayManager )
527 throws MojoExecutionException
528 {
529 final List<WarPackagingTask> packagingTasks = new ArrayList<>();
530
531 packagingTasks.add( new CopyUserManifestTask() );
532
533 final List<Overlay> resolvedOverlays = overlayManager.getOverlays();
534 for ( Overlay overlay : resolvedOverlays )
535 {
536 if ( overlay.isCurrentProject() )
537 {
538 packagingTasks.add( new WarProjectPackagingTask( webResources, webXml, containerConfigXML,
539 currentProjectOverlay ) );
540 }
541 else
542 {
543 packagingTasks.add( new OverlayPackagingTask( overlay, currentProjectOverlay ) );
544 }
545 }
546 return packagingTasks;
547 }
548
549
550
551
552 private class DefaultWarPackagingContext
553 implements WarPackagingContext
554 {
555 private final ArtifactFactory artifactFactory;
556
557 private final String resourceEncoding;
558
559 private final WebappStructure webappStructure;
560
561 private final File webappDirectory;
562
563 private final OverlayManager overlayManager;
564
565 private final List<FileUtils.FilterWrapper> filterWrappers;
566
567 private List<String> nonFilteredFileExtensions;
568
569 private boolean filteringDeploymentDescriptors;
570
571 private boolean useJvmChmod;
572
573 private final Boolean failOnMissingWebXml;
574
575 private final Collection<String> outdatedResources;
576
577
578
579
580
581
582
583
584
585
586
587
588
589 DefaultWarPackagingContext( final File webappDirectory, final WebappStructure webappStructure,
590 final OverlayManager overlayManager,
591 List<FileUtils.FilterWrapper> filterWrappers,
592 List<String> nonFilteredFileExtensions,
593 boolean filteringDeploymentDescriptors, ArtifactFactory artifactFactory,
594 String resourceEncoding, boolean useJvmChmod,
595 final Boolean failOnMissingWebXml )
596 {
597 this.webappDirectory = webappDirectory;
598 this.webappStructure = webappStructure;
599 this.overlayManager = overlayManager;
600 this.filterWrappers = filterWrappers;
601 this.artifactFactory = artifactFactory;
602 this.filteringDeploymentDescriptors = filteringDeploymentDescriptors;
603 this.nonFilteredFileExtensions =
604 nonFilteredFileExtensions == null ? Collections.<String>emptyList() : nonFilteredFileExtensions;
605 this.resourceEncoding = resourceEncoding;
606
607
608 for ( String overlayId : overlayManager.getOverlayIds() )
609 {
610 webappStructure.getStructure( overlayId );
611 }
612 this.useJvmChmod = useJvmChmod;
613 this.failOnMissingWebXml = failOnMissingWebXml;
614
615 if ( !webappDirectory.exists() )
616 {
617 outdatedResources = Collections.emptyList();
618 }
619 else if ( getWarSourceDirectory().toPath().equals( webappDirectory.toPath() ) )
620 {
621 getLog().info( "Can't detect outdated resources when running inplace goal" );
622 outdatedResources = Collections.emptyList();
623 }
624 else
625 {
626 outdatedResources = new ArrayList<>();
627 try
628 {
629 Files.walkFileTree( webappDirectory.toPath(), new SimpleFileVisitor<Path>()
630 {
631 @Override
632 public FileVisitResult visitFile( Path file, BasicFileAttributes attrs )
633 throws IOException
634 {
635 outdatedResources.add( webappDirectory.toPath().relativize( file ).toString() );
636 return super.visitFile( file, attrs );
637 }
638 } );
639 }
640 catch ( IOException e )
641 {
642 getLog().warn( "Can't detect outdated resources", e );
643 }
644 }
645 }
646
647 @Override
648 public MavenProject getProject()
649 {
650 return project;
651 }
652
653 @Override
654 public File getWebappDirectory()
655 {
656 return webappDirectory;
657 }
658
659 @Override
660 public File getClassesDirectory()
661 {
662 return classesDirectory;
663 }
664
665 @Override
666 public Log getLog()
667 {
668 return AbstractWarMojo.this.getLog();
669 }
670
671 @Override
672 public String getOutputFileNameMapping()
673 {
674 return outputFileNameMapping;
675 }
676
677 @Override
678 public File getWebappSourceDirectory()
679 {
680 return warSourceDirectory;
681 }
682
683 @Override
684 public String[] getWebappSourceIncludes()
685 {
686 return getIncludes();
687 }
688
689 @Override
690 public String[] getWebappSourceExcludes()
691 {
692 return getExcludes();
693 }
694
695 @Override
696 public boolean isWebappSourceIncludeEmptyDirectories()
697 {
698 return includeEmptyDirectories;
699 }
700
701 @Override
702 public boolean archiveClasses()
703 {
704 return archiveClasses;
705 }
706
707 @Override
708 public File getOverlaysWorkDirectory()
709 {
710 return workDirectory;
711 }
712
713 @Override
714 public ArchiverManager getArchiverManager()
715 {
716 return archiverManager;
717 }
718
719 @Override
720 public MavenArchiveConfiguration getArchive()
721 {
722 return archive;
723 }
724
725 @Override
726 public JarArchiver getJarArchiver()
727 {
728 return jarArchiver;
729 }
730
731 @Override
732 public List<String> getFilters()
733 {
734 return filters;
735 }
736
737 @Override
738 public WebappStructure getWebappStructure()
739 {
740 return webappStructure;
741 }
742
743 @Override
744 public List<String> getOwnerIds()
745 {
746 return overlayManager.getOverlayIds();
747 }
748
749 @Override
750 public MavenFileFilter getMavenFileFilter()
751 {
752 return mavenFileFilter;
753 }
754
755 @Override
756 public List<FileUtils.FilterWrapper> getFilterWrappers()
757 {
758 return filterWrappers;
759 }
760
761 @Override
762 public boolean isNonFilteredExtension( String fileName )
763 {
764 return !mavenResourcesFiltering.filteredFileExtension( fileName, nonFilteredFileExtensions );
765 }
766
767 @Override
768 public boolean isFilteringDeploymentDescriptors()
769 {
770 return filteringDeploymentDescriptors;
771 }
772
773 @Override
774 public ArtifactFactory getArtifactFactory()
775 {
776 return this.artifactFactory;
777 }
778
779 @Override
780 public MavenSession getSession()
781 {
782 return session;
783 }
784
785 @Override
786 public String getResourceEncoding()
787 {
788 return resourceEncoding;
789 }
790
791 @Override
792 public boolean isUseJvmChmod()
793 {
794 return useJvmChmod;
795 }
796
797 @Override
798 public Boolean isFailOnMissingWebXml()
799 {
800 return failOnMissingWebXml;
801 }
802
803 @Override
804 public Collection<String> getOutdatedResources()
805 {
806 return outdatedResources;
807 }
808 }
809
810
811
812
813 public MavenProject getProject()
814 {
815 return project;
816 }
817
818
819
820
821 public void setProject( MavenProject project )
822 {
823 this.project = project;
824 }
825
826
827
828
829 public File getClassesDirectory()
830 {
831 return classesDirectory;
832 }
833
834
835
836
837 public void setClassesDirectory( File classesDirectory )
838 {
839 this.classesDirectory = classesDirectory;
840 }
841
842
843
844
845 public File getWebappDirectory()
846 {
847 return webappDirectory;
848 }
849
850
851
852
853 public void setWebappDirectory( File webappDirectory )
854 {
855 this.webappDirectory = webappDirectory;
856 }
857
858
859
860
861 public File getWarSourceDirectory()
862 {
863 return warSourceDirectory;
864 }
865
866
867
868
869 public void setWarSourceDirectory( File warSourceDirectory )
870 {
871 this.warSourceDirectory = warSourceDirectory;
872 }
873
874
875
876
877 public File getWebXml()
878 {
879 return webXml;
880 }
881
882
883
884
885 public void setWebXml( File webXml )
886 {
887 this.webXml = webXml;
888 }
889
890
891
892
893 public File getContainerConfigXML()
894 {
895 return containerConfigXML;
896 }
897
898
899
900
901 public void setContainerConfigXML( File containerConfigXML )
902 {
903 this.containerConfigXML = containerConfigXML;
904 }
905
906
907
908
909 public String getOutputFileNameMapping()
910 {
911 return outputFileNameMapping;
912 }
913
914
915
916
917 public void setOutputFileNameMapping( String outputFileNameMapping )
918 {
919 this.outputFileNameMapping = outputFileNameMapping;
920 }
921
922
923
924
925 public List<Overlay> getOverlays()
926 {
927 return overlays;
928 }
929
930
931
932
933 public void setOverlays( List<Overlay> overlays )
934 {
935 this.overlays = overlays;
936 }
937
938
939
940
941 public void addOverlay( Overlay overlay )
942 {
943 overlays.add( overlay );
944 }
945
946
947
948
949 public boolean isArchiveClasses()
950 {
951 return archiveClasses;
952 }
953
954
955
956
957 public void setArchiveClasses( boolean archiveClasses )
958 {
959 this.archiveClasses = archiveClasses;
960 }
961
962
963
964
965 public JarArchiver getJarArchiver()
966 {
967 return jarArchiver;
968 }
969
970
971
972
973 public void setJarArchiver( JarArchiver jarArchiver )
974 {
975 this.jarArchiver = jarArchiver;
976 }
977
978
979
980
981 public Resource[] getWebResources()
982 {
983 return webResources;
984 }
985
986
987
988
989 public void setWebResources( Resource[] webResources )
990 {
991 this.webResources = webResources;
992 }
993
994
995
996
997 public List<String> getFilters()
998 {
999 return filters;
1000 }
1001
1002
1003
1004
1005 public void setFilters( List<String> filters )
1006 {
1007 this.filters = filters;
1008 }
1009
1010
1011
1012
1013 public File getWorkDirectory()
1014 {
1015 return workDirectory;
1016 }
1017
1018
1019
1020
1021 public void setWorkDirectory( File workDirectory )
1022 {
1023 this.workDirectory = workDirectory;
1024 }
1025
1026
1027
1028
1029 public String getWarSourceIncludes()
1030 {
1031 return warSourceIncludes;
1032 }
1033
1034
1035
1036
1037 public void setWarSourceIncludes( String warSourceIncludes )
1038 {
1039 this.warSourceIncludes = warSourceIncludes;
1040 }
1041
1042
1043
1044
1045 public String getWarSourceExcludes()
1046 {
1047 return warSourceExcludes;
1048 }
1049
1050
1051
1052
1053 public void setWarSourceExcludes( String warSourceExcludes )
1054 {
1055 this.warSourceExcludes = warSourceExcludes;
1056 }
1057
1058
1059
1060
1061 public MavenArchiveConfiguration getArchive()
1062 {
1063 return archive;
1064 }
1065
1066
1067
1068
1069 public List<String> getNonFilteredFileExtensions()
1070 {
1071 return nonFilteredFileExtensions;
1072 }
1073
1074
1075
1076
1077 public void setNonFilteredFileExtensions( List<String> nonFilteredFileExtensions )
1078 {
1079 this.nonFilteredFileExtensions = nonFilteredFileExtensions;
1080 }
1081
1082
1083
1084
1085 public ArtifactFactory getArtifactFactory()
1086 {
1087 return this.artifactFactory;
1088 }
1089
1090
1091
1092
1093 public void setArtifactFactory( ArtifactFactory artifactFactory )
1094 {
1095 this.artifactFactory = artifactFactory;
1096 }
1097
1098
1099
1100
1101 protected MavenSession getSession()
1102 {
1103 return this.session;
1104 }
1105
1106
1107
1108
1109 protected boolean isRecompressZippedFiles()
1110 {
1111 return recompressZippedFiles;
1112 }
1113
1114
1115
1116
1117 protected boolean isIncludeEmptyDirectories()
1118 {
1119 return includeEmptyDirectories;
1120 }
1121 }