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