View Javadoc

1   package org.apache.maven.plugin.war;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.archiver.MavenArchiver;
23  import org.apache.maven.artifact.Artifact;
24  import org.apache.maven.artifact.DependencyResolutionRequiredException;
25  import org.apache.maven.plugin.MojoExecutionException;
26  import org.apache.maven.plugin.MojoFailureException;
27  import org.apache.maven.plugin.war.util.ClassesPackager;
28  import org.apache.maven.project.MavenProjectHelper;
29  import org.codehaus.plexus.archiver.ArchiverException;
30  import org.codehaus.plexus.archiver.jar.ManifestException;
31  import org.codehaus.plexus.archiver.war.WarArchiver;
32  import org.codehaus.plexus.util.FileUtils;
33  import org.codehaus.plexus.util.StringUtils;
34  
35  import java.io.File;
36  import java.io.IOException;
37  import java.util.Arrays;
38  
39  /**
40   * Build a WAR file.
41   *
42   * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
43   * @version $Id: WarMojo.java 1235053 2012-01-23 23:03:53Z dennisl $
44   * @goal war
45   * @phase package
46   * @threadSafe
47   * @requiresDependencyResolution runtime
48   */
49  public class WarMojo
50      extends AbstractWarMojo
51  {
52      /**
53       * The directory for the generated WAR.
54       *
55       * @parameter default-value="${project.build.directory}"
56       * @required
57       */
58      private String outputDirectory;
59  
60      /**
61       * The name of the generated WAR.
62       *
63       * @parameter default-value="${project.build.finalName}"
64       * @required
65       */
66      private String warName;
67  
68      /**
69       * Classifier to add to the generated WAR. If given, the artifact will be an attachment instead.
70       * The classifier will not be applied to the JAR file of the project - only to the WAR file.
71       *
72       * @parameter
73       */
74      private String classifier;
75  
76      /**
77       * The comma separated list of tokens to exclude from the WAR before
78       * packaging. This option may be used to implement the skinny WAR use
79       * case. Note that you can use the Java Regular Expressions engine to
80       * include and exclude specific pattern using the expression %regex[].
81       * Hint: read the about (?!Pattern).
82       *
83       * @parameter
84       * @since 2.1-alpha-2
85       */
86      private String packagingExcludes;
87  
88      /**
89       * The comma separated list of tokens to include in the WAR before
90       * packaging. By default everything is included. This option may be used
91       * to implement the skinny WAR use case. Note that you can use the
92       * Java Regular Expressions engine to include and exclude specific pattern
93       * using the expression %regex[].
94       *
95       * @parameter
96       * @since 2.1-beta-1
97       */
98      private String packagingIncludes;
99  
100     /**
101      * The WAR archiver.
102      *
103      * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="war"
104      */
105     private WarArchiver warArchiver;
106 
107     /**
108      * @component
109      */
110     private MavenProjectHelper projectHelper;
111 
112     /**
113      * Whether this is the main artifact being built. Set to <code>false</code> if you don't want to install or
114      * deploy it to the local repository instead of the default one in an execution.
115      *
116      * @parameter expression="${primaryArtifact}" default-value="true"
117      */
118     private boolean primaryArtifact = true;
119 
120     /**
121      * Whether or not to fail the build if the <code>web.xml</code> file is missing. Set to <code>false</code>
122      * if you want you WAR built without a <code>web.xml</code> file.
123      * This may be useful if you are building an overlay that has no web.xml file.
124      *
125      * @parameter expression="${failOnMissingWebXml}" default-value="true"
126      * @since 2.1-alpha-2
127      */
128     private boolean failOnMissingWebXml = true;
129 
130     /**
131      * Whether classes (that is the content of the WEB-INF/classes directory) should be attached to the
132      * project as an additional artifact.
133      * <p>By default the
134      * classifier for the additional artifact is 'classes'. 
135      * You can change it with the
136      * <code><![CDATA[<classesClassifier>someclassifier</classesClassifier>]]></code>
137      * parameter.
138      * </p><p>
139      * If this parameter true, another project can depend on the classes
140      * by writing something like:
141      * <pre><![CDATA[<dependency>
142      *   <groupId>myGroup</groupId>
143      *   <artifactId>myArtifact</artifactId>
144      *   <version>myVersion</myVersion>
145      *   <classifier>classes</classifier>
146      * </dependency>]]></pre></p>
147      * @parameter default-value="false"
148      * @since 2.1-alpha-2
149      */
150     private boolean attachClasses = false;
151 
152     /**
153      * The classifier to use for the attached classes artifact.
154      *
155      * @parameter default-value="classes"
156      * @since 2.1-alpha-2
157      */
158     private String classesClassifier = "classes";
159 
160     // ----------------------------------------------------------------------
161     // Implementation
162     // ----------------------------------------------------------------------
163 
164 
165     /**
166      * Executes the WarMojo on the current project.
167      *
168      * @throws MojoExecutionException if an error occurred while building the webapp
169      */
170     public void execute()
171         throws MojoExecutionException, MojoFailureException
172     {
173         File warFile = getTargetWarFile();
174 
175         try
176         {
177             performPackaging( warFile );
178         }
179         catch ( DependencyResolutionRequiredException e )
180         {
181             throw new MojoExecutionException( "Error assembling WAR: " + e.getMessage(), e );
182         }
183         catch ( ManifestException e )
184         {
185             throw new MojoExecutionException( "Error assembling WAR", e );
186         }
187         catch ( IOException e )
188         {
189             throw new MojoExecutionException( "Error assembling WAR", e );
190         }
191         catch ( ArchiverException e )
192         {
193             throw new MojoExecutionException( "Error assembling WAR: " + e.getMessage(), e );
194         }
195     }
196 
197     /**
198      * Generates the webapp according to the <tt>mode</tt> attribute.
199      *
200      * @param warFile the target WAR file
201      * @throws IOException            if an error occurred while copying files
202      * @throws ArchiverException      if the archive could not be created
203      * @throws ManifestException      if the manifest could not be created
204      * @throws DependencyResolutionRequiredException
205      *                                if an error occurred while resolving the dependencies
206      * @throws MojoExecutionException if the execution failed
207      * @throws MojoFailureException   if a fatal exception occurred
208      */
209     private void performPackaging( File warFile )
210         throws IOException, ArchiverException, ManifestException, DependencyResolutionRequiredException,
211         MojoExecutionException, MojoFailureException
212     {
213         getLog().info( "Packaging webapp" );
214 
215         buildExplodedWebapp( getWebappDirectory() );
216 
217         MavenArchiver archiver = new MavenArchiver();
218 
219         archiver.setArchiver( warArchiver );
220 
221         archiver.setOutputFile( warFile );
222 
223         getLog().debug(
224             "Excluding " + Arrays.asList( getPackagingExcludes() ) + " from the generated webapp archive." );
225         getLog().debug(
226             "Including " + Arrays.asList( getPackagingIncludes() ) + " in the generated webapp archive." );
227 
228         warArchiver.addDirectory( getWebappDirectory(), getPackagingIncludes(), getPackagingExcludes() );
229 
230         final File webXmlFile = new File( getWebappDirectory(), "WEB-INF/web.xml" );
231         if ( webXmlFile.exists() )
232         {
233             warArchiver.setWebxml( webXmlFile );
234         }
235         if ( !failOnMissingWebXml )
236         {
237             getLog().debug( "Build won't fail if web.xml file is missing." );
238             // The flag is wrong in plexus-archiver so it will need to be fixed at some point
239             warArchiver.setIgnoreWebxml( false );
240         }
241 
242         // create archive
243         archiver.createArchive( getSession(), getProject(), getArchive() );
244 
245         // create the classes to be attached if necessary
246         if ( isAttachClasses() )
247         {
248             if ( isArchiveClasses() && getJarArchiver().getDestFile() != null )
249             {
250                 // special handling in case of archived classes: MWAR-240
251                 File targetClassesFile = getTargetClassesFile();
252                 FileUtils.copyFile(getJarArchiver().getDestFile(), targetClassesFile);
253                 projectHelper.attachArtifact( getProject(), "jar", getClassesClassifier(), targetClassesFile );
254             }
255             else
256             {
257                 ClassesPackager packager = new ClassesPackager();
258                 final File classesDirectory = packager.getClassesDirectory( getWebappDirectory() );
259                 if ( classesDirectory.exists() )
260                 {
261                     getLog().info( "Packaging classes" );
262                     packager.packageClasses( classesDirectory, getTargetClassesFile(), getJarArchiver(), getSession(),
263                                              getProject(), getArchive() );
264                     projectHelper.attachArtifact( getProject(), "jar", getClassesClassifier(), getTargetClassesFile() );
265                 }
266             }
267         }
268 
269         String classifier = this.classifier;
270         if ( classifier != null )
271         {
272             projectHelper.attachArtifact( getProject(), "war", classifier, warFile );
273         }
274         else
275         {
276             Artifact artifact = getProject().getArtifact();
277             if ( primaryArtifact )
278             {
279                 artifact.setFile( warFile );
280             }
281             else if ( artifact.getFile() == null || artifact.getFile().isDirectory() )
282             {
283                 artifact.setFile( warFile );
284             }
285         }
286     }
287 
288 
289     protected static File getTargetFile( File basedir, String finalName, String classifier, String type )
290     {
291         if ( classifier == null )
292         {
293             classifier = "";
294         }
295         else if ( classifier.trim().length() > 0 && !classifier.startsWith( "-" ) )
296         {
297             classifier = "-" + classifier;
298         }
299 
300         return new File( basedir, finalName + classifier + "." + type );
301     }
302 
303 
304     protected File getTargetWarFile()
305     {
306         return getTargetFile( new File( getOutputDirectory() ), getWarName(), getClassifier(), "war" );
307 
308     }
309 
310     protected File getTargetClassesFile()
311     {
312         return getTargetFile( new File( getOutputDirectory() ), getWarName(), getClassesClassifier(), "jar" );
313     }
314 
315     // Getters and Setters
316 
317     public String getClassifier()
318     {
319         return classifier;
320     }
321 
322     public void setClassifier( String classifier )
323     {
324         this.classifier = classifier;
325     }
326 
327     public String[] getPackagingExcludes()
328     {
329         if ( StringUtils.isEmpty( packagingExcludes ) )
330         {
331             return new String[0];
332         }
333         else
334         {
335             return StringUtils.split( packagingExcludes, "," );
336         }
337     }
338 
339     public void setPackagingExcludes( String packagingExcludes )
340     {
341         this.packagingExcludes = packagingExcludes;
342     }
343 
344     public String[] getPackagingIncludes()
345     {
346         if ( StringUtils.isEmpty( packagingIncludes ) )
347         {
348             return new String[]{"**"};
349         }
350         else
351         {
352             return StringUtils.split( packagingIncludes, "," );
353         }
354     }
355 
356     public void setPackagingIncludes( String packagingIncludes )
357     {
358         this.packagingIncludes = packagingIncludes;
359     }
360 
361     public String getOutputDirectory()
362     {
363         return outputDirectory;
364     }
365 
366     public void setOutputDirectory( String outputDirectory )
367     {
368         this.outputDirectory = outputDirectory;
369     }
370 
371     public String getWarName()
372     {
373         return warName;
374     }
375 
376     public void setWarName( String warName )
377     {
378         this.warName = warName;
379     }
380 
381     public WarArchiver getWarArchiver()
382     {
383         return warArchiver;
384     }
385 
386     public void setWarArchiver( WarArchiver warArchiver )
387     {
388         this.warArchiver = warArchiver;
389     }
390 
391     public MavenProjectHelper getProjectHelper()
392     {
393         return projectHelper;
394     }
395 
396     public void setProjectHelper( MavenProjectHelper projectHelper )
397     {
398         this.projectHelper = projectHelper;
399     }
400 
401     public boolean isPrimaryArtifact()
402     {
403         return primaryArtifact;
404     }
405 
406     public void setPrimaryArtifact( boolean primaryArtifact )
407     {
408         this.primaryArtifact = primaryArtifact;
409     }
410 
411     public boolean isAttachClasses()
412     {
413         return attachClasses;
414     }
415 
416     public void setAttachClasses( boolean attachClasses )
417     {
418         this.attachClasses = attachClasses;
419     }
420 
421     public String getClassesClassifier()
422     {
423         return classesClassifier;
424     }
425 
426     public void setClassesClassifier( String classesClassifier )
427     {
428         this.classesClassifier = classesClassifier;
429     }
430 
431     public boolean isFailOnMissingWebXml()
432     {
433         return failOnMissingWebXml;
434     }
435 
436     public void setFailOnMissingWebXml( boolean failOnMissingWebXml )
437     {
438         this.failOnMissingWebXml = failOnMissingWebXml;
439     }
440 }