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
356
357
358
359
360
361
362 @Parameter( defaultValue = "${project.build.outputTimestamp}" )
363 protected String outputTimestamp;
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 public Overlay getCurrentProjectOverlay()
379 {
380 return currentProjectOverlay;
381 }
382
383
384
385
386
387
388 protected String[] getExcludes()
389 {
390 List<String> excludeList = new ArrayList<>();
391 if ( StringUtils.isNotEmpty( warSourceExcludes ) )
392 {
393 excludeList.addAll( Arrays.asList( StringUtils.split( warSourceExcludes, "," ) ) );
394 }
395
396
397 if ( webXml != null && StringUtils.isNotEmpty( webXml.getName() ) )
398 {
399 excludeList.add( "**/" + WEB_INF + "/web.xml" );
400 }
401
402
403 if ( containerConfigXML != null && StringUtils.isNotEmpty( containerConfigXML.getName() ) )
404 {
405 excludeList.add( "**/" + META_INF + "/" + containerConfigXML.getName() );
406 }
407
408 return excludeList.toArray( new String[excludeList.size()] );
409 }
410
411
412
413
414
415
416 protected String[] getIncludes()
417 {
418 return StringUtils.split( StringUtils.defaultString( warSourceIncludes ), "," );
419 }
420
421
422
423
424
425
426 protected String[] getDependentWarExcludes()
427 {
428 return StringUtils.split( StringUtils.defaultString( dependentWarExcludes ), "," );
429 }
430
431
432
433
434
435
436 protected String[] getDependentWarIncludes()
437 {
438 return StringUtils.split( StringUtils.defaultString( dependentWarIncludes ), "," );
439 }
440
441
442
443
444
445
446 public void buildExplodedWebapp( File webapplicationDirectory )
447 throws MojoExecutionException, MojoFailureException
448 {
449 webapplicationDirectory.mkdirs();
450
451 try
452 {
453 buildWebapp( project, webapplicationDirectory );
454 }
455 catch ( IOException e )
456 {
457 throw new MojoExecutionException( "Could not build webapp", e );
458 }
459 }
460
461
462
463
464
465
466
467
468
469
470
471 public void buildWebapp( MavenProject mavenProject, File webapplicationDirectory )
472 throws MojoExecutionException, MojoFailureException, IOException
473 {
474
475 WebappStructure structure = new WebappStructure( mavenProject.getDependencies() );
476
477
478 final long startTime = System.currentTimeMillis();
479 getLog().info( "Assembling webapp [" + mavenProject.getArtifactId() + "] in [" + webapplicationDirectory + "]" );
480
481 final OverlayManager overlayManager =
482 new OverlayManager( overlays, mavenProject, getDependentWarIncludes(), getDependentWarExcludes(),
483 currentProjectOverlay );
484
485 List<FileUtils.FilterWrapper> defaultFilterWrappers;
486 try
487 {
488 MavenResourcesExecution mavenResourcesExecution = new MavenResourcesExecution();
489 mavenResourcesExecution.setEscapeString( escapeString );
490 mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering );
491 mavenResourcesExecution.setMavenProject( mavenProject );
492
493
494 mavenResourcesExecution.setDelimiters( delimiters, useDefaultDelimiters );
495
496 if ( nonFilteredFileExtensions != null )
497 {
498 mavenResourcesExecution.setNonFilteredFileExtensions( nonFilteredFileExtensions );
499 }
500
501 if ( filters == null )
502 {
503 filters = getProject().getBuild().getFilters();
504 }
505 mavenResourcesExecution.setFilters( filters );
506 mavenResourcesExecution.setEscapedBackslashesInFilePath( escapedBackslashesInFilePath );
507 mavenResourcesExecution.setMavenSession( this.session );
508 mavenResourcesExecution.setEscapeString( this.escapeString );
509 mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering );
510
511 defaultFilterWrappers = mavenFileFilter.getDefaultFilterWrappers( mavenResourcesExecution );
512
513 }
514 catch ( MavenFilteringException e )
515 {
516 getLog().error( "fail to build filtering wrappers " + e.getMessage() );
517 throw new MojoExecutionException( e.getMessage(), e );
518 }
519
520 final WarPackagingContext context =
521 new DefaultWarPackagingContext( webapplicationDirectory, structure, overlayManager, defaultFilterWrappers,
522 getNonFilteredFileExtensions(), filteringDeploymentDescriptors,
523 this.artifactFactory, resourceEncoding, useJvmChmod, failOnMissingWebXml,
524 outputTimestamp );
525
526 final List<WarPackagingTask> packagingTasks = getPackagingTasks( overlayManager );
527
528 for ( WarPackagingTask warPackagingTask : packagingTasks )
529 {
530 warPackagingTask.performPackaging( context );
531 }
532
533 getLog().debug( "Webapp assembled in [" + ( System.currentTimeMillis() - startTime ) + " msecs]" );
534
535 }
536
537
538
539
540
541
542
543
544
545 private List<WarPackagingTask> getPackagingTasks( OverlayManager overlayManager )
546 throws MojoExecutionException
547 {
548 final List<WarPackagingTask> packagingTasks = new ArrayList<>();
549
550 packagingTasks.add( new CopyUserManifestTask() );
551
552 final List<Overlay> resolvedOverlays = overlayManager.getOverlays();
553 for ( Overlay overlay : resolvedOverlays )
554 {
555 if ( overlay.isCurrentProject() )
556 {
557 packagingTasks.add( new WarProjectPackagingTask( webResources, webXml, containerConfigXML,
558 currentProjectOverlay ) );
559 }
560 else
561 {
562 packagingTasks.add( new OverlayPackagingTask( overlay, currentProjectOverlay ) );
563 }
564 }
565 return packagingTasks;
566 }
567
568
569
570
571 private class DefaultWarPackagingContext
572 implements WarPackagingContext
573 {
574 private final ArtifactFactory artifactFactory;
575
576 private final String resourceEncoding;
577
578 private final WebappStructure webappStructure;
579
580 private final File webappDirectory;
581
582 private final OverlayManager overlayManager;
583
584 private final List<FileUtils.FilterWrapper> filterWrappers;
585
586 private List<String> nonFilteredFileExtensions;
587
588 private boolean filteringDeploymentDescriptors;
589
590 private boolean useJvmChmod;
591
592 private final Boolean failOnMissingWebXml;
593
594 private final Collection<String> outdatedResources;
595
596 private final String outputTimestamp;
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611 DefaultWarPackagingContext( final File webappDirectory, final WebappStructure webappStructure,
612 final OverlayManager overlayManager,
613 List<FileUtils.FilterWrapper> filterWrappers,
614 List<String> nonFilteredFileExtensions,
615 boolean filteringDeploymentDescriptors, ArtifactFactory artifactFactory,
616 String resourceEncoding, boolean useJvmChmod,
617 final Boolean failOnMissingWebXml, String outputTimestamp )
618 {
619 this.webappDirectory = webappDirectory;
620 this.webappStructure = webappStructure;
621 this.overlayManager = overlayManager;
622 this.filterWrappers = filterWrappers;
623 this.artifactFactory = artifactFactory;
624 this.filteringDeploymentDescriptors = filteringDeploymentDescriptors;
625 this.nonFilteredFileExtensions =
626 nonFilteredFileExtensions == null ? Collections.<String>emptyList() : nonFilteredFileExtensions;
627 this.resourceEncoding = resourceEncoding;
628
629
630 for ( String overlayId : overlayManager.getOverlayIds() )
631 {
632 webappStructure.getStructure( overlayId );
633 }
634 this.useJvmChmod = useJvmChmod;
635 this.failOnMissingWebXml = failOnMissingWebXml;
636
637 if ( !webappDirectory.exists() )
638 {
639 outdatedResources = Collections.emptyList();
640 }
641 else if ( getWarSourceDirectory().toPath().equals( webappDirectory.toPath() ) )
642 {
643 getLog().info( "Can't detect outdated resources when running inplace goal" );
644 outdatedResources = Collections.emptyList();
645 }
646 else
647 {
648 outdatedResources = new ArrayList<>();
649 try
650 {
651 if ( '\\' == File.separatorChar )
652 {
653 outdatedCheckPath = outdatedCheckPath.replace( '/', '\\' );
654 }
655 Files.walkFileTree( webappDirectory.toPath(), new SimpleFileVisitor<Path>()
656 {
657 @Override
658 public FileVisitResult visitFile( Path file, BasicFileAttributes attrs )
659 throws IOException
660 {
661 if ( file.toFile().lastModified() < session.getStartTime().getTime() )
662 {
663
664 String path = webappDirectory.toPath().relativize( file ).toString();
665 if ( path.startsWith( outdatedCheckPath ) )
666 {
667 outdatedResources.add( path );
668 }
669 }
670 return super.visitFile( file, attrs );
671 }
672 } );
673 }
674 catch ( IOException e )
675 {
676 getLog().warn( "Can't detect outdated resources", e );
677 }
678 }
679 this.outputTimestamp = outputTimestamp;
680 }
681
682 @Override
683 public MavenProject getProject()
684 {
685 return project;
686 }
687
688 @Override
689 public File getWebappDirectory()
690 {
691 return webappDirectory;
692 }
693
694 @Override
695 public File getClassesDirectory()
696 {
697 return classesDirectory;
698 }
699
700 @Override
701 public Log getLog()
702 {
703 return AbstractWarMojo.this.getLog();
704 }
705
706 @Override
707 public String getOutputFileNameMapping()
708 {
709 return outputFileNameMapping;
710 }
711
712 @Override
713 public File getWebappSourceDirectory()
714 {
715 return warSourceDirectory;
716 }
717
718 @Override
719 public String[] getWebappSourceIncludes()
720 {
721 return getIncludes();
722 }
723
724 @Override
725 public String[] getWebappSourceExcludes()
726 {
727 return getExcludes();
728 }
729
730 @Override
731 public boolean isWebappSourceIncludeEmptyDirectories()
732 {
733 return includeEmptyDirectories;
734 }
735
736 @Override
737 public boolean archiveClasses()
738 {
739 return archiveClasses;
740 }
741
742 @Override
743 public File getOverlaysWorkDirectory()
744 {
745 return workDirectory;
746 }
747
748 @Override
749 public ArchiverManager getArchiverManager()
750 {
751 return archiverManager;
752 }
753
754 @Override
755 public MavenArchiveConfiguration getArchive()
756 {
757 return archive;
758 }
759
760 @Override
761 public JarArchiver getJarArchiver()
762 {
763 return jarArchiver;
764 }
765
766 @Override
767 public List<String> getFilters()
768 {
769 return filters;
770 }
771
772 @Override
773 public WebappStructure getWebappStructure()
774 {
775 return webappStructure;
776 }
777
778 @Override
779 public List<String> getOwnerIds()
780 {
781 return overlayManager.getOverlayIds();
782 }
783
784 @Override
785 public MavenFileFilter getMavenFileFilter()
786 {
787 return mavenFileFilter;
788 }
789
790 @Override
791 public List<FileUtils.FilterWrapper> getFilterWrappers()
792 {
793 return filterWrappers;
794 }
795
796 @Override
797 public boolean isNonFilteredExtension( String fileName )
798 {
799 return !mavenResourcesFiltering.filteredFileExtension( fileName, nonFilteredFileExtensions );
800 }
801
802 @Override
803 public boolean isFilteringDeploymentDescriptors()
804 {
805 return filteringDeploymentDescriptors;
806 }
807
808 @Override
809 public ArtifactFactory getArtifactFactory()
810 {
811 return this.artifactFactory;
812 }
813
814 @Override
815 public MavenSession getSession()
816 {
817 return session;
818 }
819
820 @Override
821 public String getResourceEncoding()
822 {
823 return resourceEncoding;
824 }
825
826 @Override
827 public boolean isUseJvmChmod()
828 {
829 return useJvmChmod;
830 }
831
832 @Override
833 public Boolean isFailOnMissingWebXml()
834 {
835 return failOnMissingWebXml;
836 }
837
838 @Override
839 public void addResource( String resource )
840 {
841 outdatedResources.remove( resource.replace( '/', File.separatorChar ) );
842 }
843
844 @Override
845 public void deleteOutdatedResources()
846 {
847 for ( String resource : outdatedResources )
848 {
849 getLog().info( "deleting outdated resource " + resource );
850 new File( getWebappDirectory(), resource ).delete();
851 }
852 }
853
854 @Override
855 public String getOutputTimestamp()
856 {
857 return outputTimestamp;
858 }
859 }
860
861
862
863
864 public MavenProject getProject()
865 {
866 return project;
867 }
868
869
870
871
872 public void setProject( MavenProject project )
873 {
874 this.project = project;
875 }
876
877
878
879
880 public File getClassesDirectory()
881 {
882 return classesDirectory;
883 }
884
885
886
887
888 public void setClassesDirectory( File classesDirectory )
889 {
890 this.classesDirectory = classesDirectory;
891 }
892
893
894
895
896 public File getWebappDirectory()
897 {
898 return webappDirectory;
899 }
900
901
902
903
904 public void setWebappDirectory( File webappDirectory )
905 {
906 this.webappDirectory = webappDirectory;
907 }
908
909
910
911
912 public File getWarSourceDirectory()
913 {
914 return warSourceDirectory;
915 }
916
917
918
919
920 public void setWarSourceDirectory( File warSourceDirectory )
921 {
922 this.warSourceDirectory = warSourceDirectory;
923 }
924
925
926
927
928 public File getWebXml()
929 {
930 return webXml;
931 }
932
933
934
935
936 public void setWebXml( File webXml )
937 {
938 this.webXml = webXml;
939 }
940
941
942
943
944 public File getContainerConfigXML()
945 {
946 return containerConfigXML;
947 }
948
949
950
951
952 public void setContainerConfigXML( File containerConfigXML )
953 {
954 this.containerConfigXML = containerConfigXML;
955 }
956
957
958
959
960 public String getOutputFileNameMapping()
961 {
962 return outputFileNameMapping;
963 }
964
965
966
967
968 public void setOutputFileNameMapping( String outputFileNameMapping )
969 {
970 this.outputFileNameMapping = outputFileNameMapping;
971 }
972
973
974
975
976 public List<Overlay> getOverlays()
977 {
978 return overlays;
979 }
980
981
982
983
984 public void setOverlays( List<Overlay> overlays )
985 {
986 this.overlays = overlays;
987 }
988
989
990
991
992 public void addOverlay( Overlay overlay )
993 {
994 overlays.add( overlay );
995 }
996
997
998
999
1000 public boolean isArchiveClasses()
1001 {
1002 return archiveClasses;
1003 }
1004
1005
1006
1007
1008 public void setArchiveClasses( boolean archiveClasses )
1009 {
1010 this.archiveClasses = archiveClasses;
1011 }
1012
1013
1014
1015
1016 public JarArchiver getJarArchiver()
1017 {
1018 return jarArchiver;
1019 }
1020
1021
1022
1023
1024 public void setJarArchiver( JarArchiver jarArchiver )
1025 {
1026 this.jarArchiver = jarArchiver;
1027 }
1028
1029
1030
1031
1032 public Resource[] getWebResources()
1033 {
1034 return webResources;
1035 }
1036
1037
1038
1039
1040 public void setWebResources( Resource[] webResources )
1041 {
1042 this.webResources = webResources;
1043 }
1044
1045
1046
1047
1048 public List<String> getFilters()
1049 {
1050 return filters;
1051 }
1052
1053
1054
1055
1056 public void setFilters( List<String> filters )
1057 {
1058 this.filters = filters;
1059 }
1060
1061
1062
1063
1064 public File getWorkDirectory()
1065 {
1066 return workDirectory;
1067 }
1068
1069
1070
1071
1072 public void setWorkDirectory( File workDirectory )
1073 {
1074 this.workDirectory = workDirectory;
1075 }
1076
1077
1078
1079
1080 public String getWarSourceIncludes()
1081 {
1082 return warSourceIncludes;
1083 }
1084
1085
1086
1087
1088 public void setWarSourceIncludes( String warSourceIncludes )
1089 {
1090 this.warSourceIncludes = warSourceIncludes;
1091 }
1092
1093
1094
1095
1096 public String getWarSourceExcludes()
1097 {
1098 return warSourceExcludes;
1099 }
1100
1101
1102
1103
1104 public void setWarSourceExcludes( String warSourceExcludes )
1105 {
1106 this.warSourceExcludes = warSourceExcludes;
1107 }
1108
1109
1110
1111
1112 public MavenArchiveConfiguration getArchive()
1113 {
1114 return archive;
1115 }
1116
1117
1118
1119
1120 public List<String> getNonFilteredFileExtensions()
1121 {
1122 return nonFilteredFileExtensions;
1123 }
1124
1125
1126
1127
1128 public void setNonFilteredFileExtensions( List<String> nonFilteredFileExtensions )
1129 {
1130 this.nonFilteredFileExtensions = nonFilteredFileExtensions;
1131 }
1132
1133
1134
1135
1136 public ArtifactFactory getArtifactFactory()
1137 {
1138 return this.artifactFactory;
1139 }
1140
1141
1142
1143
1144 public void setArtifactFactory( ArtifactFactory artifactFactory )
1145 {
1146 this.artifactFactory = artifactFactory;
1147 }
1148
1149
1150
1151
1152 protected MavenSession getSession()
1153 {
1154 return this.session;
1155 }
1156
1157
1158
1159
1160 protected boolean isRecompressZippedFiles()
1161 {
1162 return recompressZippedFiles;
1163 }
1164
1165
1166
1167
1168 protected boolean isIncludeEmptyDirectories()
1169 {
1170 return includeEmptyDirectories;
1171 }
1172 }