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.net.MalformedURLException;
24 import java.net.URL;
25 import java.net.URLClassLoader;
26 import java.util.Arrays;
27 import java.util.List;
28
29 import org.apache.maven.archiver.MavenArchiver;
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.artifact.DependencyResolutionRequiredException;
32 import org.apache.maven.plugin.MojoExecutionException;
33 import org.apache.maven.plugin.MojoFailureException;
34 import org.apache.maven.plugins.annotations.Component;
35 import org.apache.maven.plugins.annotations.LifecyclePhase;
36 import org.apache.maven.plugins.annotations.Mojo;
37 import org.apache.maven.plugins.annotations.Parameter;
38 import org.apache.maven.plugins.annotations.ResolutionScope;
39 import org.apache.maven.plugins.war.util.ClassesPackager;
40 import org.apache.maven.project.MavenProjectHelper;
41 import org.codehaus.plexus.archiver.Archiver;
42 import org.codehaus.plexus.archiver.ArchiverException;
43 import org.codehaus.plexus.archiver.jar.ManifestException;
44 import org.codehaus.plexus.archiver.war.WarArchiver;
45 import org.codehaus.plexus.util.FileUtils;
46 import org.codehaus.plexus.util.StringUtils;
47
48
49
50
51
52
53 @Mojo(
54 name = "war",
55 defaultPhase = LifecyclePhase.PACKAGE,
56 threadSafe = true,
57 requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
58 public class WarMojo extends AbstractWarMojo {
59
60
61
62 @Parameter(defaultValue = "${project.build.directory}", required = true)
63 private String outputDirectory;
64
65
66
67
68 @Parameter(defaultValue = "${project.build.finalName}", required = true, readonly = true)
69 private String warName;
70
71
72
73
74
75 @Parameter
76 private String classifier;
77
78
79
80
81
82
83
84
85 @Parameter
86 private String packagingExcludes;
87
88
89
90
91
92
93
94
95 @Parameter
96 private String packagingIncludes;
97
98
99
100
101 @Component(role = Archiver.class, hint = "war")
102 private WarArchiver warArchiver;
103
104
105
106 @Component
107 private MavenProjectHelper projectHelper;
108
109
110
111
112
113 @Parameter(defaultValue = "true")
114 private boolean primaryArtifact;
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 @Parameter(defaultValue = "false")
139 private boolean attachClasses;
140
141
142
143
144
145
146 @Parameter(defaultValue = "classes")
147 private String classesClassifier;
148
149
150
151
152
153
154
155 @Parameter(property = "maven.war.skip", defaultValue = "false")
156 private boolean skip;
157
158
159
160
161
162
163
164
165
166
167
168 @Override
169 public void execute() throws MojoExecutionException, MojoFailureException {
170
171 if (isSkip()) {
172 getLog().info("Skipping the execution.");
173 return;
174 }
175
176 File warFile = getTargetWarFile();
177
178 try {
179 performPackaging(warFile);
180 } catch (DependencyResolutionRequiredException | ArchiverException e) {
181 throw new MojoExecutionException("Error assembling WAR: " + e.getMessage(), e);
182 } catch (ManifestException | IOException e) {
183 throw new MojoExecutionException("Error assembling WAR", e);
184 }
185 }
186
187
188
189
190
191
192
193
194
195
196
197
198 private void performPackaging(File warFile)
199 throws IOException, ManifestException, DependencyResolutionRequiredException, MojoExecutionException,
200 MojoFailureException {
201 getLog().info("Packaging webapp");
202
203 buildExplodedWebapp(getWebappDirectory());
204
205 MavenArchiver archiver = new MavenArchiver();
206
207 archiver.setArchiver(warArchiver);
208
209 archiver.setCreatedBy("Maven WAR Plugin", "org.apache.maven.plugins", "maven-war-plugin");
210
211 archiver.setOutputFile(warFile);
212
213
214 archiver.configureReproducible(outputTimestamp);
215
216 getLog().debug("Excluding " + Arrays.asList(getPackagingExcludes()) + " from the generated webapp archive.");
217 getLog().debug("Including " + Arrays.asList(getPackagingIncludes()) + " in the generated webapp archive.");
218
219 warArchiver.addDirectory(getWebappDirectory(), getPackagingIncludes(), getPackagingExcludes());
220
221 final File webXmlFile = new File(getWebappDirectory(), "WEB-INF/web.xml");
222 if (webXmlFile.exists()) {
223 warArchiver.setWebxml(webXmlFile);
224 }
225
226 warArchiver.setRecompressAddedZips(isRecompressZippedFiles());
227
228 warArchiver.setIncludeEmptyDirs(isIncludeEmptyDirectories());
229
230 if (Boolean.FALSE.equals(failOnMissingWebXml)
231 || (failOnMissingWebXml == null && isProjectUsingAtLeastServlet30())) {
232 getLog().debug("Build won't fail if web.xml file is missing.");
233 warArchiver.setExpectWebXml(false);
234 }
235
236
237 archiver.createArchive(getSession(), getProject(), getArchive());
238
239
240 if (isAttachClasses()) {
241 if (isArchiveClasses() && getJarArchiver().getDestFile() != null) {
242
243 File targetClassesFile = getTargetClassesFile();
244 FileUtils.copyFile(getJarArchiver().getDestFile(), targetClassesFile);
245 projectHelper.attachArtifact(getProject(), "jar", getClassesClassifier(), targetClassesFile);
246 } else {
247 ClassesPackager packager = new ClassesPackager();
248 final File classesDirectory = packager.getClassesDirectory(getWebappDirectory());
249 if (classesDirectory.exists()) {
250 getLog().info("Packaging classes");
251 packager.packageClasses(
252 classesDirectory,
253 getTargetClassesFile(),
254 getJarArchiver(),
255 getSession(),
256 getProject(),
257 getArchive(),
258 outputTimestamp);
259 projectHelper.attachArtifact(getProject(), "jar", getClassesClassifier(), getTargetClassesFile());
260 }
261 }
262 }
263
264 if (this.classifier != null) {
265 projectHelper.attachArtifact(getProject(), "war", this.classifier, warFile);
266 } else {
267 Artifact artifact = getProject().getArtifact();
268 if (primaryArtifact) {
269 artifact.setFile(warFile);
270 } else if (artifact.getFile() == null || artifact.getFile().isDirectory()) {
271 artifact.setFile(warFile);
272 }
273 }
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290 private boolean isProjectUsingAtLeastServlet30()
291 throws DependencyResolutionRequiredException, MalformedURLException {
292 List<String> classpathElements = getProject().getCompileClasspathElements();
293 URL[] urls = new URL[classpathElements.size()];
294 for (int i = 0; i < urls.length; i++) {
295 urls[i] = new File(classpathElements.get(i)).toURI().toURL();
296 }
297 URLClassLoader loader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
298 try {
299 return hasWebServletAnnotationClassInClasspath(loader);
300 } finally {
301 try {
302 loader.close();
303 } catch (IOException ex) {
304
305 }
306 }
307 }
308
309 private static boolean hasWebServletAnnotationClassInClasspath(ClassLoader loader) {
310 return hasClassInClasspath(loader, "javax.servlet.annotation.WebServlet")
311 || hasClassInClasspath(loader, "jakarta.servlet.annotation.WebServlet");
312 }
313
314 private static boolean hasClassInClasspath(ClassLoader loader, String clazz) {
315 try {
316 Class.forName(clazz, false, loader);
317 return true;
318 } catch (ClassNotFoundException e) {
319 return false;
320 }
321 }
322
323
324
325
326
327
328
329
330 protected static File getTargetFile(File basedir, String finalName, String classifier, String type) {
331 if (classifier == null) {
332 classifier = "";
333 } else if (classifier.trim().length() > 0 && !classifier.startsWith("-")) {
334 classifier = "-" + classifier;
335 }
336
337 return new File(basedir, finalName + classifier + "." + type);
338 }
339
340
341
342
343 protected File getTargetWarFile() {
344 return getTargetFile(new File(getOutputDirectory()), getWarName(), getClassifier(), "war");
345 }
346
347
348
349
350 protected File getTargetClassesFile() {
351 return getTargetFile(new File(getOutputDirectory()), getWarName(), getClassesClassifier(), "jar");
352 }
353
354
355
356
357
358
359 public String getClassifier() {
360 return classifier;
361 }
362
363
364
365
366 public void setClassifier(String classifier) {
367 this.classifier = classifier;
368 }
369
370
371
372
373 public String[] getPackagingExcludes() {
374 if (packagingExcludes == null || packagingExcludes.isEmpty()) {
375 return new String[0];
376 } else {
377 return StringUtils.split(packagingExcludes, ",");
378 }
379 }
380
381
382
383
384 public void setPackagingExcludes(String packagingExcludes) {
385 this.packagingExcludes = packagingExcludes;
386 }
387
388
389
390
391 public String[] getPackagingIncludes() {
392 if (packagingIncludes == null || packagingIncludes.isEmpty()) {
393 return new String[] {"**"};
394 } else {
395 return StringUtils.split(packagingIncludes, ",");
396 }
397 }
398
399
400
401
402 public void setPackagingIncludes(String packagingIncludes) {
403 this.packagingIncludes = packagingIncludes;
404 }
405
406
407
408
409 public String getOutputDirectory() {
410 return outputDirectory;
411 }
412
413
414
415
416 public void setOutputDirectory(String outputDirectory) {
417 this.outputDirectory = outputDirectory;
418 }
419
420
421
422
423 public String getWarName() {
424 return warName;
425 }
426
427
428
429
430 public void setWarName(String warName) {
431 this.warName = warName;
432 }
433
434
435
436
437 public WarArchiver getWarArchiver() {
438 return warArchiver;
439 }
440
441
442
443
444 public void setWarArchiver(WarArchiver warArchiver) {
445 this.warArchiver = warArchiver;
446 }
447
448
449
450
451 public MavenProjectHelper getProjectHelper() {
452 return projectHelper;
453 }
454
455
456
457
458 public void setProjectHelper(MavenProjectHelper projectHelper) {
459 this.projectHelper = projectHelper;
460 }
461
462
463
464
465 public boolean isPrimaryArtifact() {
466 return primaryArtifact;
467 }
468
469
470
471
472 public void setPrimaryArtifact(boolean primaryArtifact) {
473 this.primaryArtifact = primaryArtifact;
474 }
475
476
477
478
479 public boolean isAttachClasses() {
480 return attachClasses;
481 }
482
483
484
485
486 public void setAttachClasses(boolean attachClasses) {
487 this.attachClasses = attachClasses;
488 }
489
490
491
492
493 public String getClassesClassifier() {
494 return classesClassifier;
495 }
496
497
498
499
500 public void setClassesClassifier(String classesClassifier) {
501 this.classesClassifier = classesClassifier;
502 }
503
504
505
506
507 public boolean isFailOnMissingWebXml() {
508 return failOnMissingWebXml;
509 }
510
511
512
513
514 public void setFailOnMissingWebXml(boolean failOnMissingWebXml) {
515 this.failOnMissingWebXml = failOnMissingWebXml;
516 }
517
518
519
520
521
522 public boolean isSkip() {
523 return skip;
524 }
525 }