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