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