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.net.MalformedURLException;
25 import java.net.URL;
26 import java.net.URLClassLoader;
27 import java.util.Arrays;
28 import java.util.List;
29
30 import org.apache.maven.archiver.MavenArchiver;
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.artifact.DependencyResolutionRequiredException;
33 import org.apache.maven.plugin.MojoExecutionException;
34 import org.apache.maven.plugin.MojoFailureException;
35 import org.apache.maven.plugins.annotations.Component;
36 import org.apache.maven.plugins.annotations.LifecyclePhase;
37 import org.apache.maven.plugins.annotations.Mojo;
38 import org.apache.maven.plugins.annotations.Parameter;
39 import org.apache.maven.plugins.annotations.ResolutionScope;
40 import org.apache.maven.plugins.war.util.ClassesPackager;
41 import org.apache.maven.project.MavenProjectHelper;
42 import org.codehaus.plexus.archiver.Archiver;
43 import org.codehaus.plexus.archiver.ArchiverException;
44 import org.codehaus.plexus.archiver.jar.ManifestException;
45 import org.codehaus.plexus.archiver.war.WarArchiver;
46 import org.codehaus.plexus.util.FileUtils;
47 import org.codehaus.plexus.util.StringUtils;
48
49
50
51
52
53
54 @Mojo( name = "war", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true,
55 requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME )
56 public class WarMojo
57 extends AbstractWarMojo
58 {
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 @Parameter( defaultValue = "${project.build.outputTimestamp}" )
166 private String outputTimestamp;
167
168
169
170
171
172
173
174
175
176
177
178 @Override
179 public void execute()
180 throws MojoExecutionException, MojoFailureException
181 {
182
183 if ( isSkip() )
184 {
185 getLog().info( "Skipping the execution." );
186 return;
187 }
188
189 File warFile = getTargetWarFile();
190
191 try
192 {
193 performPackaging( warFile );
194 }
195 catch ( DependencyResolutionRequiredException | ArchiverException e )
196 {
197 throw new MojoExecutionException( "Error assembling WAR: " + e.getMessage(), e );
198 }
199 catch ( ManifestException | IOException e )
200 {
201 throw new MojoExecutionException( "Error assembling WAR", e );
202 }
203 }
204
205
206
207
208
209
210
211
212
213
214
215
216 private void performPackaging( File warFile )
217 throws IOException, ManifestException, DependencyResolutionRequiredException, MojoExecutionException,
218 MojoFailureException
219 {
220 getLog().info( "Packaging webapp" );
221
222 buildExplodedWebapp( getWebappDirectory() );
223
224 MavenArchiver archiver = new MavenArchiver();
225
226 archiver.setArchiver( warArchiver );
227
228 archiver.setCreatedBy( "Maven WAR Plugin", "org.apache.maven.plugins", "maven-war-plugin" );
229
230 archiver.setOutputFile( warFile );
231
232
233 archiver.configureReproducible( outputTimestamp );
234
235 getLog().debug( "Excluding " + Arrays.asList( getPackagingExcludes() )
236 + " from the generated webapp archive." );
237 getLog().debug( "Including " + Arrays.asList( getPackagingIncludes() ) + " in the generated webapp archive." );
238
239 warArchiver.addDirectory( getWebappDirectory(), getPackagingIncludes(), getPackagingExcludes() );
240
241 final File webXmlFile = new File( getWebappDirectory(), "WEB-INF/web.xml" );
242 if ( webXmlFile.exists() )
243 {
244 warArchiver.setWebxml( webXmlFile );
245 }
246
247 warArchiver.setRecompressAddedZips( isRecompressZippedFiles() );
248
249 warArchiver.setIncludeEmptyDirs( isIncludeEmptyDirectories() );
250
251 if ( Boolean.FALSE.equals( failOnMissingWebXml )
252 || ( failOnMissingWebXml == null && isProjectUsingAtLeastServlet30() ) )
253 {
254 getLog().debug( "Build won't fail if web.xml file is missing." );
255 warArchiver.setExpectWebXml( false );
256 }
257
258
259 archiver.createArchive( getSession(), getProject(), getArchive() );
260
261
262 if ( isAttachClasses() )
263 {
264 if ( isArchiveClasses() && getJarArchiver().getDestFile() != null )
265 {
266
267 File targetClassesFile = getTargetClassesFile();
268 FileUtils.copyFile( getJarArchiver().getDestFile(), targetClassesFile );
269 projectHelper.attachArtifact( getProject(), "jar", getClassesClassifier(), targetClassesFile );
270 }
271 else
272 {
273 ClassesPackager packager = new ClassesPackager();
274 final File classesDirectory = packager.getClassesDirectory( getWebappDirectory() );
275 if ( classesDirectory.exists() )
276 {
277 getLog().info( "Packaging classes" );
278 packager.packageClasses( classesDirectory, getTargetClassesFile(), getJarArchiver(), getSession(),
279 getProject(), getArchive() );
280 projectHelper.attachArtifact( getProject(), "jar", getClassesClassifier(), getTargetClassesFile() );
281 }
282 }
283 }
284
285 if ( this.classifier != null )
286 {
287 projectHelper.attachArtifact( getProject(), "war", this.classifier, warFile );
288 }
289 else
290 {
291 Artifact artifact = getProject().getArtifact();
292 if ( primaryArtifact )
293 {
294 artifact.setFile( warFile );
295 }
296 else if ( artifact.getFile() == null || artifact.getFile().isDirectory() )
297 {
298 artifact.setFile( warFile );
299 }
300 }
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317 private boolean isProjectUsingAtLeastServlet30()
318 throws DependencyResolutionRequiredException, MalformedURLException
319 {
320 List<String> classpathElements = getProject().getCompileClasspathElements();
321 URL[] urls = new URL[classpathElements.size()];
322 for ( int i = 0; i < urls.length; i++ )
323 {
324 urls[i] = new File( classpathElements.get( i ) ).toURI().toURL();
325 }
326 ClassLoader loader = new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() );
327
328 return hasWebServletAnnotationClassInClasspath( loader );
329 }
330
331 private static boolean hasWebServletAnnotationClassInClasspath( ClassLoader loader )
332 {
333 return hasClassInClasspath( loader, "javax.servlet.annotation.WebServlet" )
334 || hasClassInClasspath( loader, "jakarta.servlet.annotation.WebServlet" );
335 }
336
337 private static boolean hasClassInClasspath( ClassLoader loader, String clazz )
338 {
339 try
340 {
341 Class.forName( clazz, false, loader );
342 return true;
343 }
344 catch ( ClassNotFoundException e )
345 {
346 return false;
347 }
348 }
349
350
351
352
353
354
355
356
357 protected static File getTargetFile( File basedir, String finalName, String classifier, String type )
358 {
359 if ( classifier == null )
360 {
361 classifier = "";
362 }
363 else if ( classifier.trim().length() > 0 && !classifier.startsWith( "-" ) )
364 {
365 classifier = "-" + classifier;
366 }
367
368 return new File( basedir, finalName + classifier + "." + type );
369 }
370
371
372
373
374 protected File getTargetWarFile()
375 {
376 return getTargetFile( new File( getOutputDirectory() ), getWarName(), getClassifier(), "war" );
377
378 }
379
380
381
382
383 protected File getTargetClassesFile()
384 {
385 return getTargetFile( new File( getOutputDirectory() ), getWarName(), getClassesClassifier(), "jar" );
386 }
387
388
389
390
391
392
393 public String getClassifier()
394 {
395 return classifier;
396 }
397
398
399
400
401 public void setClassifier( String classifier )
402 {
403 this.classifier = classifier;
404 }
405
406
407
408
409 public String[] getPackagingExcludes()
410 {
411 if ( StringUtils.isEmpty( packagingExcludes ) )
412 {
413 return new String[0];
414 }
415 else
416 {
417 return StringUtils.split( packagingExcludes, "," );
418 }
419 }
420
421
422
423
424 public void setPackagingExcludes( String packagingExcludes )
425 {
426 this.packagingExcludes = packagingExcludes;
427 }
428
429
430
431
432 public String[] getPackagingIncludes()
433 {
434 if ( StringUtils.isEmpty( packagingIncludes ) )
435 {
436 return new String[] { "**" };
437 }
438 else
439 {
440 return StringUtils.split( packagingIncludes, "," );
441 }
442 }
443
444
445
446
447 public void setPackagingIncludes( String packagingIncludes )
448 {
449 this.packagingIncludes = packagingIncludes;
450 }
451
452
453
454
455 public String getOutputDirectory()
456 {
457 return outputDirectory;
458 }
459
460
461
462
463 public void setOutputDirectory( String outputDirectory )
464 {
465 this.outputDirectory = outputDirectory;
466 }
467
468
469
470
471 public String getWarName()
472 {
473 return warName;
474 }
475
476
477
478
479 public void setWarName( String warName )
480 {
481 this.warName = warName;
482 }
483
484
485
486
487 public WarArchiver getWarArchiver()
488 {
489 return warArchiver;
490 }
491
492
493
494
495 public void setWarArchiver( WarArchiver warArchiver )
496 {
497 this.warArchiver = warArchiver;
498 }
499
500
501
502
503 public MavenProjectHelper getProjectHelper()
504 {
505 return projectHelper;
506 }
507
508
509
510
511 public void setProjectHelper( MavenProjectHelper projectHelper )
512 {
513 this.projectHelper = projectHelper;
514 }
515
516
517
518
519 public boolean isPrimaryArtifact()
520 {
521 return primaryArtifact;
522 }
523
524
525
526
527 public void setPrimaryArtifact( boolean primaryArtifact )
528 {
529 this.primaryArtifact = primaryArtifact;
530 }
531
532
533
534
535 public boolean isAttachClasses()
536 {
537 return attachClasses;
538 }
539
540
541
542
543 public void setAttachClasses( boolean attachClasses )
544 {
545 this.attachClasses = attachClasses;
546 }
547
548
549
550
551 public String getClassesClassifier()
552 {
553 return classesClassifier;
554 }
555
556
557
558
559 public void setClassesClassifier( String classesClassifier )
560 {
561 this.classesClassifier = classesClassifier;
562 }
563
564
565
566
567 public boolean isFailOnMissingWebXml()
568 {
569 return failOnMissingWebXml;
570 }
571
572
573
574
575 public void setFailOnMissingWebXml( boolean failOnMissingWebXml )
576 {
577 this.failOnMissingWebXml = failOnMissingWebXml;
578 }
579
580 public boolean isSkip()
581 {
582 return skip;
583 }
584 }