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