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