Coverage Report - org.apache.maven.plugin.war.packaging.AbstractWarPackagingTask
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractWarPackagingTask
70%
50/71
73%
25/34
2,733
AbstractWarPackagingTask$1
100%
13/13
N/A
2,733
 
 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  
 import java.util.Iterator;
 25  
 
 26  
 import org.apache.maven.artifact.Artifact;
 27  
 import org.apache.maven.plugin.MojoExecutionException;
 28  
 import org.apache.maven.plugin.war.AbstractWarMojo;
 29  
 import org.apache.maven.plugin.war.util.MappingUtils;
 30  
 import org.apache.maven.plugin.war.util.PathSet;
 31  
 import org.apache.maven.plugin.war.util.WebappStructure;
 32  
 import org.apache.maven.shared.filtering.MavenFilteringException;
 33  
 import org.codehaus.plexus.archiver.ArchiverException;
 34  
 import org.codehaus.plexus.archiver.UnArchiver;
 35  
 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
 36  
 import org.codehaus.plexus.interpolation.InterpolationException;
 37  
 import org.codehaus.plexus.util.DirectoryScanner;
 38  
 import org.codehaus.plexus.util.FileUtils;
 39  
 import org.codehaus.plexus.util.IOUtil;
 40  
 import org.codehaus.plexus.util.xml.XmlStreamReader;
 41  
 
 42  
 /**
 43  
  * @author Stephane Nicoll
 44  
  * @version $Id: AbstractWarPackagingTask.java 1388368 2012-09-21 08:23:24Z dennisl $
 45  
  */
 46  232
 public abstract class AbstractWarPackagingTask
 47  
     implements WarPackagingTask
 48  
 {
 49  1
     public static final String[] DEFAULT_INCLUDES = {"**/**"};
 50  
 
 51  
     public static final String WEB_INF_PATH = "WEB-INF";
 52  
 
 53  
     public static final String META_INF_PATH = "META-INF";
 54  
 
 55  
     public static final String CLASSES_PATH = "WEB-INF/classes/";
 56  
 
 57  
     public static final String LIB_PATH = "WEB-INF/lib/";
 58  
 
 59  
     /**
 60  
      * Copies the files if possible with an optional target prefix.
 61  
      * <p/>
 62  
      * Copy uses a first-win strategy: files that have already been copied by previous
 63  
      * tasks are ignored. This method makes sure to update the list of protected files
 64  
      * which gives the list of files that have already been copied.
 65  
      * <p/>
 66  
      * If the structure of the source directory is not the same as the root of the
 67  
      * webapp, use the <tt>targetPrefix</tt> parameter to specify in which particular
 68  
      * directory the files should be copied. Use <tt>null</tt> to copy the files with
 69  
      * the same structure
 70  
      *
 71  
      * @param sourceId       the source id
 72  
      * @param context        the context to use
 73  
      * @param sourceBaseDir  the base directory from which the <tt>sourceFilesSet</tt> will be copied
 74  
      * @param sourceFilesSet the files to be copied
 75  
      * @param targetPrefix   the prefix to add to the target file name
 76  
      * @throws IOException if an error occurred while copying the files
 77  
      */
 78  
     protected void copyFiles( String sourceId, WarPackagingContext context, File sourceBaseDir, PathSet sourceFilesSet,
 79  
                               String targetPrefix, boolean filtered )
 80  
         throws IOException, MojoExecutionException
 81  
     {
 82  156
         for ( Iterator iter = sourceFilesSet.iterator(); iter.hasNext(); )
 83  
         {
 84  338
             final String fileToCopyName = (String) iter.next();
 85  338
             final File sourceFile = new File( sourceBaseDir, fileToCopyName );
 86  
 
 87  
             String destinationFileName;
 88  338
             if ( targetPrefix == null )
 89  
             {
 90  327
                 destinationFileName = fileToCopyName;
 91  
             }
 92  
             else
 93  
             {
 94  11
                 destinationFileName = targetPrefix + fileToCopyName;
 95  
             }
 96  
             
 97  
 
 98  338
             if ( filtered
 99  
                 && !context.isNonFilteredExtension( sourceFile.getName() ) ) 
 100  
             {
 101  0
                 copyFilteredFile( sourceId, context, sourceFile, destinationFileName );
 102  
             }
 103  
             else
 104  
             {
 105  338
                 copyFile( sourceId, context, sourceFile, destinationFileName );
 106  
             }
 107  338
         }
 108  156
     }
 109  
 
 110  
     /**
 111  
      * Copies the files if possible as is.
 112  
      * <p/>
 113  
      * Copy uses a first-win strategy: files that have already been copied by previous
 114  
      * tasks are ignored. This method makes sure to update the list of protected files
 115  
      * which gives the list of files that have already been copied.
 116  
      *
 117  
      * @param sourceId       the source id
 118  
      * @param context        the context to use
 119  
      * @param sourceBaseDir  the base directory from which the <tt>sourceFilesSet</tt> will be copied
 120  
      * @param sourceFilesSet the files to be copied
 121  
      * @throws IOException if an error occurred while copying the files
 122  
      */
 123  
     protected void copyFiles( String sourceId, WarPackagingContext context, File sourceBaseDir, PathSet sourceFilesSet,
 124  
                               boolean filtered )
 125  
         throws IOException, MojoExecutionException
 126  
     {
 127  93
         copyFiles( sourceId, context, sourceBaseDir, sourceFilesSet, null, filtered );
 128  93
     }
 129  
 
 130  
     /**
 131  
      * Copy the specified file if the target location has not yet already been used.
 132  
      * <p/>
 133  
      * The <tt>targetFileName</tt> is the relative path according to the root of
 134  
      * the generated web application.
 135  
      *
 136  
      * @param sourceId       the source id
 137  
      * @param context        the context to use
 138  
      * @param file           the file to copy
 139  
      * @param targetFilename the relative path according to the root of the webapp
 140  
      * @throws IOException if an error occurred while copying
 141  
      */
 142  
     protected void copyFile( String sourceId, final WarPackagingContext context, final File file,
 143  
                              String targetFilename )
 144  
         throws IOException
 145  
     {
 146  362
         final File targetFile = new File( context.getWebappDirectory(), targetFilename );
 147  362
         context.getWebappStructure().registerFile( sourceId, targetFilename, new WebappStructure.RegistrationCallback()
 148  
         {
 149  
             public void registered( String ownerId, String targetFilename )
 150  
                 throws IOException
 151  
             {
 152  216
                 copyFile( context, file, targetFile, targetFilename, false );
 153  216
             }
 154  
 
 155  
             public void alreadyRegistered( String ownerId, String targetFilename )
 156  
                 throws IOException
 157  
             {
 158  23
                 copyFile( context, file, targetFile, targetFilename, true );
 159  23
             }
 160  
 
 161  
             public void refused( String ownerId, String targetFilename, String actualOwnerId )
 162  
                 throws IOException
 163  
             {
 164  115
                 context.getLog().debug( " - " + targetFilename + " wasn't copied because it has "
 165  
                     + "already been packaged for overlay [" + actualOwnerId + "]." );
 166  115
             }
 167  
 
 168  
             public void superseded( String ownerId, String targetFilename, String deprecatedOwnerId )
 169  
                 throws IOException
 170  
             {
 171  6
                 context.getLog().info( "File [" + targetFilename + "] belonged to overlay [" + deprecatedOwnerId
 172  
                     + "] so it will be overwritten." );
 173  6
                 copyFile( context, file, targetFile, targetFilename, false );
 174  6
             }
 175  
 
 176  362
             public void supersededUnknownOwner( String ownerId, String targetFilename, String unknownOwnerId )
 177  
                 throws IOException
 178  
             {
 179  2
                 context.getLog()
 180  
                     .warn( "File [" + targetFilename + "] belonged to overlay [" + unknownOwnerId
 181  
                         + "] which does not exist anymore in the current project. It is recommended to invoke "
 182  
                         + "clean if the dependencies of the project changed." );
 183  2
                 copyFile( context, file, targetFile, targetFilename, false );
 184  2
             }
 185  
         } );
 186  362
     }
 187  
 
 188  
     /**
 189  
      * Copy the specified file if the target location has not yet already been
 190  
      * used and filter its content with the configured filter properties.
 191  
      * <p/>
 192  
      * The <tt>targetFileName</tt> is the relative path according to the root of
 193  
      * the generated web application.
 194  
      *
 195  
      * @param sourceId       the source id
 196  
      * @param context        the context to use
 197  
      * @param file           the file to copy
 198  
      * @param targetFilename the relative path according to the root of the webapp
 199  
      * @return true if the file has been copied, false otherwise
 200  
      * @throws IOException            if an error occurred while copying
 201  
      * @throws MojoExecutionException if an error occurred while retrieving the filter properties
 202  
      */
 203  
     protected boolean copyFilteredFile( String sourceId, final WarPackagingContext context, File file,
 204  
                                         String targetFilename )
 205  
         throws IOException, MojoExecutionException
 206  
     {
 207  
 
 208  6
         if ( context.getWebappStructure().registerFile( sourceId, targetFilename ) )
 209  
         {
 210  6
             final File targetFile = new File( context.getWebappDirectory(), targetFilename );
 211  
             final String encoding;
 212  
             try
 213  
             {
 214  6
                 if ( isXmlFile( file ) )
 215  
                 {
 216  
                     // For xml-files we extract the encoding from the files
 217  0
                     encoding = getEncoding( file );
 218  
                 }
 219  
                 else
 220  
                 {
 221  
                     // For all others we use the configured encoding
 222  6
                     encoding = context.getResourceEncoding();
 223  
                 }
 224  
                 // fix for MWAR-36, ensures that the parent dir are created first
 225  6
                 targetFile.getParentFile().mkdirs();
 226  6
                 context.getMavenFileFilter().copyFile( file, targetFile, true, context.getFilterWrappers(), encoding );
 227  
             }
 228  0
             catch ( MavenFilteringException e )
 229  
             {
 230  0
                 throw new MojoExecutionException( e.getMessage(), e );
 231  6
             }
 232  
             // Add the file to the protected list
 233  6
             context.getLog().debug( " + " + targetFilename + " has been copied (filtered encoding='" + encoding + "')." );
 234  6
             return true;
 235  
         }
 236  
         else
 237  
         {
 238  0
             context.getLog().debug(
 239  
                 " - " + targetFilename + " wasn't copied because it has already been packaged (filtered)." );
 240  0
             return false;
 241  
         }
 242  
     }
 243  
 
 244  
     /**
 245  
      * Unpacks the specified file to the specified directory.
 246  
      *
 247  
      * @param context         the packaging context
 248  
      * @param file            the file to unpack
 249  
      * @param unpackDirectory the directory to use for th unpacked file
 250  
      * @throws MojoExecutionException if an error occurred while unpacking the file
 251  
      */
 252  
     protected void doUnpack( WarPackagingContext context, File file, File unpackDirectory )
 253  
         throws MojoExecutionException
 254  
     {
 255  0
         String archiveExt = FileUtils.getExtension( file.getAbsolutePath() ).toLowerCase();
 256  
 
 257  
         try
 258  
         {
 259  0
             UnArchiver unArchiver = context.getArchiverManager().getUnArchiver( archiveExt );
 260  0
             unArchiver.setSourceFile( file );
 261  0
             unArchiver.setDestDirectory( unpackDirectory );
 262  0
             unArchiver.setOverwrite( true );
 263  0
             unArchiver.extract();
 264  
         }
 265  0
         catch ( ArchiverException e )
 266  
         {
 267  0
             throw new MojoExecutionException( "Error unpacking file [" + file.getAbsolutePath() + "]" + "to ["
 268  
                 + unpackDirectory.getAbsolutePath() + "]", e );
 269  
         }
 270  0
         catch ( NoSuchArchiverException e )
 271  
         {
 272  0
             context.getLog().warn( "Skip unpacking dependency file [" + file.getAbsolutePath()
 273  
                 + " with unknown extension [" + archiveExt + "]" );
 274  0
         }
 275  0
     }
 276  
 
 277  
     /**
 278  
      * Copy file from source to destination. The directories up to <code>destination</code>
 279  
      * will be created if they don't already exist. if the <code>onlyIfModified</code> flag
 280  
      * is <tt>false</tt>, <code>destination</code> will be overwritten if it already exists. If the
 281  
      * flag is <tt>true</tt> destination will be overwritten if it's not up to date.
 282  
      * <p/>
 283  
      *
 284  
      * @param context        the packaging context
 285  
      * @param source         an existing non-directory <code>File</code> to copy bytes from
 286  
      * @param destination    a non-directory <code>File</code> to write bytes to (possibly overwriting).
 287  
      * @param targetFilename the relative path of the file from the webapp root directory
 288  
      * @param onlyIfModified if true, copy the file only if the source has changed, always copy otherwise
 289  
      * @return true if the file has been copied/updated, false otherwise
 290  
      * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot
 291  
      *                     be written to, or an IO error occurs during copying
 292  
      */
 293  
     protected boolean copyFile( WarPackagingContext context, File source, File destination, String targetFilename,
 294  
                                 boolean onlyIfModified )
 295  
         throws IOException
 296  
     {
 297  272
         if ( onlyIfModified && destination.lastModified() >= source.lastModified() )
 298  
         {
 299  12
             context.getLog().debug( " * " + targetFilename + " is up to date." );
 300  12
             return false;
 301  
         }
 302  
         else
 303  
         {
 304  260
             FileUtils.copyFile( source.getCanonicalFile(), destination );
 305  
             // preserve timestamp
 306  260
             destination.setLastModified( source.lastModified() );
 307  260
             context.getLog().debug( " + " + targetFilename + " has been copied." );
 308  260
             return true;
 309  
         }
 310  
     }
 311  
 
 312  
     /**
 313  
      * Get the encoding from an XML-file.
 314  
      *
 315  
      * @param webXml the XML-file
 316  
      * @return The encoding of the XML-file, or UTF-8 if it's not specified in the file
 317  
      * @throws java.io.IOException if an error occurred while reading the file
 318  
      */
 319  
     protected String getEncoding( File webXml )
 320  
         throws IOException
 321  
     {
 322  0
         XmlStreamReader xmlReader = new XmlStreamReader( webXml );
 323  
         try
 324  
         {
 325  0
             return xmlReader.getEncoding();
 326  
         }
 327  
         finally
 328  
         {
 329  0
             IOUtil.close( xmlReader );
 330  
         }
 331  
     }
 332  
 
 333  
     /**
 334  
      * Returns the file to copy. If the includes are <tt>null</tt> or empty, the
 335  
      * default includes are used.
 336  
      *
 337  
      * @param baseDir  the base directory to start from
 338  
      * @param includes the includes
 339  
      * @param excludes the excludes
 340  
      * @return the files to copy
 341  
      */
 342  
     protected PathSet getFilesToIncludes( File baseDir, String[] includes, String[] excludes )
 343  
     {
 344  156
         final DirectoryScanner scanner = new DirectoryScanner();
 345  156
         scanner.setBasedir( baseDir );
 346  
 
 347  156
         if ( excludes != null )
 348  
         {
 349  94
             scanner.setExcludes( excludes );
 350  
         }
 351  156
         scanner.addDefaultExcludes();
 352  
 
 353  156
         if ( includes != null && includes.length > 0 )
 354  
         {
 355  34
             scanner.setIncludes( includes );
 356  
         }
 357  
         else
 358  
         {
 359  122
             scanner.setIncludes( DEFAULT_INCLUDES );
 360  
         }
 361  
 
 362  156
         scanner.scan();
 363  
 
 364  156
         return new PathSet( scanner.getIncludedFiles() );
 365  
 
 366  
     }
 367  
 
 368  
     /**
 369  
      * Returns the final name of the specified artifact.
 370  
      * <p/>
 371  
      * If the <tt>outputFileNameMapping</tt> is set, it is used, otherwise
 372  
      * the standard naming scheme is used.
 373  
      *
 374  
      * @param context  the packaging context
 375  
      * @param artifact the artifact
 376  
      * @return the converted filename of the artifact
 377  
      */
 378  
     protected String getArtifactFinalName( WarPackagingContext context, Artifact artifact )
 379  
         throws InterpolationException
 380  
     {
 381  110
         if ( context.getOutputFileNameMapping() != null )
 382  
         {
 383  6
             return MappingUtils.evaluateFileNameMapping( context.getOutputFileNameMapping(), artifact );
 384  
         }
 385  
 
 386  104
         String classifier = artifact.getClassifier();
 387  104
         if ( ( classifier != null ) && !( "".equals( classifier.trim() ) ) )
 388  
         {
 389  4
             return MappingUtils.evaluateFileNameMapping( AbstractWarMojo.DEFAULT_FILE_NAME_MAPPING_CLASSIFIER,
 390  
                                                          artifact );
 391  
         }
 392  
         else
 393  
         {
 394  100
             return MappingUtils.evaluateFileNameMapping( AbstractWarMojo.DEFAULT_FILE_NAME_MAPPING, artifact );
 395  
         }
 396  
 
 397  
     }
 398  
 
 399  
     /**
 400  
      * Returns <code>true</code> if the <code>File</code>-object is a file (not
 401  
      * a directory) that is not <code>null</code> and has a file name that ends
 402  
      * in ".xml".
 403  
      *
 404  
      * @param file The file to check
 405  
      * @return <code>true</code> if the file is an xml-file, otherwise <code>false</code>
 406  
      * @since 2.3
 407  
      */
 408  
     private boolean isXmlFile( File file )
 409  
     {
 410  6
         return file != null && file.isFile() && file.getName().endsWith( ".xml" );
 411  
     }
 412  
 }