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