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