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