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