View Javadoc

1   package org.apache.maven.plugin.war.packaging;
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.model.Resource;
23  import org.apache.maven.plugin.MojoExecutionException;
24  import org.apache.maven.plugin.MojoFailureException;
25  import org.apache.maven.plugin.war.Overlay;
26  import org.apache.maven.plugin.war.util.PathSet;
27  import org.apache.maven.shared.filtering.MavenFilteringException;
28  import org.codehaus.plexus.util.DirectoryScanner;
29  import org.codehaus.plexus.util.IOUtil;
30  import org.codehaus.plexus.util.StringUtils;
31  import org.codehaus.plexus.util.xml.XmlStreamReader;
32  
33  import java.io.File;
34  import java.io.IOException;
35  import java.util.Iterator;
36  
37  /**
38   * Handles the project own resources, that is:
39   * <ul
40   * <li>The list of web resources, if any</li>
41   * <li>The content of the webapp directory if it exists</li>
42   * <li>The custom deployment descriptor(s), if any</li>
43   * <li>The content of the classes directory if it exists</li>
44   * <li>The dependencies of the project</li>
45   * </ul>
46   *
47   * @author Stephane Nicoll
48   * @version $Id: WarProjectPackagingTask.java 1071576 2011-02-17 11:16:51Z bentmann $
49   */
50  public class WarProjectPackagingTask
51      extends AbstractWarPackagingTask
52  {
53      private final Resource[] webResources;
54  
55      private final File webXml;
56  
57      private final File containerConfigXML;
58  
59      private final String id;
60  
61      private Overlay currentProjectOverlay;
62  
63  
64      public WarProjectPackagingTask( Resource[] webResources, File webXml, File containerConfigXml,
65                                      Overlay currentProjectOverlay )
66      {
67          if ( webResources != null )
68          {
69              this.webResources = webResources;
70          }
71          else
72          {
73              this.webResources = new Resource[0];
74          }
75          this.webXml = webXml;
76          this.containerConfigXML = containerConfigXml;
77          this.currentProjectOverlay = currentProjectOverlay;
78          this.id = currentProjectOverlay.getId();
79      }
80  
81      public void performPackaging( WarPackagingContext context )
82          throws MojoExecutionException, MojoFailureException
83      {
84  
85          context.getLog().info( "Processing war project" );
86          // Prepare the INF directories
87          File webinfDir = new File( context.getWebappDirectory(), WEB_INF_PATH );
88          webinfDir.mkdirs();
89          File metainfDir = new File( context.getWebappDirectory(), META_INF_PATH );
90          metainfDir.mkdirs();
91  
92          handleWebResources( context );
93  
94          handeWebAppSourceDirectory( context );
95  
96          // Debug mode: dump the path set for the current build
97          PathSet pathSet = context.getWebappStructure().getStructure( "currentBuild" );
98          context.getLog().debug( "Dump of the current build pathSet content -->" );
99          for ( Iterator iterator = pathSet.iterator(); iterator.hasNext(); )
100         {
101             context.getLog().debug( "" + iterator.next() );
102         }
103         context.getLog().debug( "-- end of dump --" );
104 
105         handleDeploymentDescriptors( context, webinfDir, metainfDir );
106 
107         handleClassesDirectory( context );
108 
109         handleArtifacts( context );
110     }
111 
112 
113     /**
114      * Handles the web resources.
115      *
116      * @param context the packaging context
117      * @throws MojoExecutionException if a resource could not be copied
118      */
119     protected void handleWebResources( WarPackagingContext context )
120         throws MojoExecutionException
121     {
122         for ( int i = 0; i < webResources.length; i++ )
123         {
124             Resource resource = webResources[i];
125             if ( !( new File( resource.getDirectory() ) ).isAbsolute() )
126             {
127                 resource.setDirectory( context.getProject().getBasedir() + File.separator + resource.getDirectory() );
128             }
129 
130             // Make sure that the resource directory is not the same as the webappDirectory
131             if ( !resource.getDirectory().equals( context.getWebappDirectory().getPath() ) )
132             {
133 
134                 try
135                 {
136                     copyResources( context, resource );
137                 }
138                 catch ( IOException e )
139                 {
140                     throw new MojoExecutionException( "Could not copy resource [" + resource.getDirectory() + "]", e );
141                 }
142             }
143         }
144     }
145 
146     /**
147      * Handles the webapp sources.
148      *
149      * @param context the packaging context
150      * @throws MojoExecutionException if the sources could not be copied
151      */
152     protected void handeWebAppSourceDirectory( WarPackagingContext context )
153         throws MojoExecutionException
154     {
155         if ( !context.getWebappSourceDirectory().exists() )
156         {
157             context.getLog().debug( "webapp sources directory does not exist - skipping." );
158         }
159         else if ( !context.getWebappSourceDirectory().getAbsolutePath().equals(
160             context.getWebappDirectory().getPath() ) )
161         {
162             context.getLog().info( "Copying webapp resources [" + context.getWebappSourceDirectory() + "]" );
163             final PathSet sources =
164                 getFilesToIncludes( context.getWebappSourceDirectory(), context.getWebappSourceIncludes(),
165                                     context.getWebappSourceExcludes() );
166 
167             try
168             {
169                 copyFiles( id, context, context.getWebappSourceDirectory(), sources, false );
170             }
171             catch ( IOException e )
172             {
173                 throw new MojoExecutionException(
174                     "Could not copy webapp sources [" + context.getWebappDirectory().getAbsolutePath() + "]", e );
175             }
176         }
177     }
178 
179     /**
180      * Handles the webapp artifacts.
181      *
182      * @param context the packaging context
183      * @throws MojoExecutionException if the artifacts could not be packaged
184      */
185     protected void handleArtifacts( WarPackagingContext context )
186         throws MojoExecutionException
187     {
188         ArtifactsPackagingTask task = new ArtifactsPackagingTask( context.getProject().getArtifacts(),
189                                                                   currentProjectOverlay );
190         task.performPackaging( context );
191     }
192 
193     /**
194      * Handles the webapp classes.
195      *
196      * @param context the packaging context
197      * @throws MojoExecutionException if the classes could not be packaged
198      */
199     protected void handleClassesDirectory( WarPackagingContext context )
200         throws MojoExecutionException
201     {
202         ClassesPackagingTask task = new ClassesPackagingTask( currentProjectOverlay );
203         task.performPackaging( context );
204     }
205 
206     /**
207      * Handles the deployment descriptors, if specified. Note that the behavior
208      * here is slightly different since the customized entry always win, even if
209      * an overlay has already packaged a web.xml previously.
210      *
211      * @param context    the packaging context
212      * @param webinfDir  the web-inf directory
213      * @param metainfDir the meta-inf directory
214      * @throws MojoFailureException   if the web.xml is specified but does not exist
215      * @throws MojoExecutionException if an error occurred while copying the descriptors
216      */
217     protected void handleDeploymentDescriptors( WarPackagingContext context, File webinfDir, File metainfDir )
218         throws MojoFailureException, MojoExecutionException
219     {
220         try
221         {
222             if ( webXml != null && StringUtils.isNotEmpty( webXml.getName() ) )
223             {
224                 if ( !webXml.exists() )
225                 {
226                     throw new MojoFailureException( "The specified web.xml file '" + webXml + "' does not exist" );
227                 }
228 
229                 // Making sure that it won't get overlayed
230                 context.getWebappStructure().registerFileForced( id, WEB_INF_PATH + "/web.xml" );
231 
232                 if ( context.isFilteringDeploymentDescriptors() )
233                 {
234                     context.getMavenFileFilter().copyFile( webXml, new File( webinfDir, "web.xml" ), true,
235                                                            context.getFilterWrappers(), getEncoding( webXml ) );
236                 }
237                 else
238                 {
239                     copyFile( context, webXml, new File( webinfDir, "web.xml" ), "WEB-INF/web.xml", true );
240                 }
241             }
242             else
243             {
244                 // the webXml can be the default one
245                 File defaultWebXml = new File( context.getWebappSourceDirectory(), WEB_INF_PATH + "/web.xml" );
246                 // if exists we can filter it
247                 if ( defaultWebXml.exists() && context.isFilteringDeploymentDescriptors() )
248                 {
249                     context.getWebappStructure().registerFile( id, WEB_INF_PATH + "/web.xml" );
250                     context.getMavenFileFilter().copyFile( defaultWebXml, new File( webinfDir, "web.xml" ), true,
251                                                            context.getFilterWrappers(), getEncoding( defaultWebXml ) );
252                 }
253             }
254 
255             if ( containerConfigXML != null && StringUtils.isNotEmpty( containerConfigXML.getName() ) )
256             {
257                 String xmlFileName = containerConfigXML.getName();
258 
259                 context.getWebappStructure().registerFileForced( id, META_INF_PATH + "/" + xmlFileName );
260 
261                 if ( context.isFilteringDeploymentDescriptors() )
262                 {
263                     context.getMavenFileFilter().copyFile( containerConfigXML, new File( metainfDir, xmlFileName ),
264                                                            true, context.getFilterWrappers(),
265                                                            getEncoding( containerConfigXML ) );
266                 }
267                 else
268                 {
269                     copyFile( context, containerConfigXML, new File( metainfDir, xmlFileName ),
270                               "META-INF/" + xmlFileName, true );
271                 }
272             }
273         }
274         catch ( IOException e )
275         {
276             throw new MojoExecutionException( "Failed to copy deployment descriptor", e );
277         }
278         catch ( MavenFilteringException e )
279         {
280             throw new MojoExecutionException( "Failed to copy deployment descriptor", e );
281         }
282     }
283 
284     /**
285      * Get the encoding from an XML-file.
286      *
287      * @param webXml the XML-file
288      * @return The encoding of the XML-file, or UTF-8 if it's not specified in the file
289      * @throws IOException if an error occurred while reading the file
290      */
291     private String getEncoding( File webXml )
292         throws IOException
293     {
294         XmlStreamReader xmlReader = new XmlStreamReader( webXml );
295         try
296         {
297             return xmlReader.getEncoding();
298         }
299         finally
300         {
301             IOUtil.close( xmlReader );
302         }
303     }
304 
305     /**
306      * Copies webapp webResources from the specified directory.
307      *
308      * @param context  the WAR packaging context to use
309      * @param resource the resource to copy
310      * @throws IOException            if an error occurred while copying the resources
311      * @throws MojoExecutionException if an error occurred while retrieving the filter properties
312      */
313     public void copyResources( WarPackagingContext context, Resource resource )
314         throws IOException, MojoExecutionException
315     {
316         if ( !context.getWebappDirectory().exists() )
317         {
318             context.getLog().warn(
319                 "Not copying webapp webResources [" + resource.getDirectory() + "]: webapp directory ["
320                     + context.getWebappDirectory().getAbsolutePath() + "] does not exist!" );
321         }
322 
323         context.getLog().info( "Copying webapp webResources [" + resource.getDirectory() + "] to ["
324             + context.getWebappDirectory().getAbsolutePath() + "]" );
325         String[] fileNames = getFilesToCopy( resource );
326         for ( int i = 0; i < fileNames.length; i++ )
327         {
328             String targetFileName = fileNames[i];
329             if ( resource.getTargetPath() != null )
330             {
331                 //TODO make sure this thing is 100% safe
332                 // MWAR-129 if targetPath is only a dot <targetPath>.</targetPath> or ./
333                 // and the Resource is in a part of the warSourceDirectory the file from sources will override this
334                 // that's we don't have to add the targetPath yep not nice but works
335                 if ( !StringUtils.equals( ".", resource.getTargetPath() )
336                     && !StringUtils.equals( "./", resource.getTargetPath() ) )
337                 {
338                     targetFileName = resource.getTargetPath() + File.separator + targetFileName;
339                 }
340             }
341             if ( resource.isFiltering() && !context.isNonFilteredExtension( fileNames[i] ) )
342             {
343                 copyFilteredFile( id, context, new File( resource.getDirectory(), fileNames[i] ), targetFileName );
344             }
345             else
346             {
347                 copyFile( id, context, new File( resource.getDirectory(), fileNames[i] ), targetFileName );
348             }
349         }
350     }
351 
352 
353     /**
354      * Returns a list of filenames that should be copied
355      * over to the destination directory.
356      *
357      * @param resource the resource to be scanned
358      * @return the array of filenames, relative to the sourceDir
359      */
360     private String[] getFilesToCopy( Resource resource )
361     {
362         DirectoryScanner scanner = new DirectoryScanner();
363         scanner.setBasedir( resource.getDirectory() );
364         if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() )
365         {
366             scanner.setIncludes(
367                 (String[]) resource.getIncludes().toArray( new String[resource.getIncludes().size()] ) );
368         }
369         else
370         {
371             scanner.setIncludes( DEFAULT_INCLUDES );
372         }
373         if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() )
374         {
375             scanner.setExcludes(
376                 (String[]) resource.getExcludes().toArray( new String[resource.getExcludes().size()] ) );
377         }
378 
379         scanner.addDefaultExcludes();
380 
381         scanner.scan();
382 
383         return scanner.getIncludedFiles();
384     }
385 }