Coverage Report - org.codehaus.plexus.archiver.tar.TarArchiver
 
Classes in this File Line Coverage Branch Coverage Complexity
TarArchiver
70%
90/127
60%
64/106
4.632
TarArchiver$TarCompressionMethod
100%
6/6
N/A
4.632
TarArchiver$TarOptions
42%
8/19
N/A
4.632
 
 1  
 /**
 2  
  *
 3  
  * Copyright 2004 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.codehaus.plexus.archiver.tar;
 18  
 
 19  
 import java.io.File;
 20  
 import java.io.FileOutputStream;
 21  
 import java.io.IOException;
 22  
 import java.io.InputStream;
 23  
 import java.io.OutputStream;
 24  
 import java.util.zip.GZIPOutputStream;
 25  
 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
 26  
 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
 27  
 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
 28  
 import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
 29  
 import org.codehaus.plexus.archiver.AbstractArchiver;
 30  
 import org.codehaus.plexus.archiver.ArchiveEntry;
 31  
 import org.codehaus.plexus.archiver.ArchiverException;
 32  
 import org.codehaus.plexus.archiver.ResourceIterator;
 33  
 import org.codehaus.plexus.archiver.exceptions.EmptyArchiveException;
 34  
 import org.codehaus.plexus.archiver.util.ResourceUtils;
 35  
 import org.codehaus.plexus.archiver.util.Streams;
 36  
 import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
 37  
 import org.codehaus.plexus.components.io.functions.SymlinkDestinationSupplier;
 38  
 import org.codehaus.plexus.components.io.resources.PlexusIoResource;
 39  
 import org.codehaus.plexus.util.IOUtil;
 40  
 import org.codehaus.plexus.util.StringUtils;
 41  
 import org.iq80.snappy.SnappyOutputStream;
 42  
 import static org.codehaus.plexus.archiver.util.Streams.bufferedOutputStream;
 43  
 
 44  
 /**
 45  
  * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse</a>
 46  
  */
 47  34
 public class TarArchiver
 48  
     extends AbstractArchiver
 49  
 {
 50  
 
 51  
     /**
 52  
      * Indicates whether the user has been warned about long files already.
 53  
      */
 54  34
     private boolean longWarningGiven = false;
 55  
 
 56  34
     private TarLongFileMode longFileMode = TarLongFileMode.warn;
 57  
 
 58  34
     private TarCompressionMethod compression = TarCompressionMethod.none;
 59  
 
 60  34
     private TarOptions options = new TarOptions();
 61  
 
 62  
     private TarArchiveOutputStream tOut;
 63  
 
 64  
     /**
 65  
      * Set how to handle long files, those with a path&gt;100 chars.
 66  
      * Optional, default=warn.
 67  
      * <p>
 68  
      * Allowable values are </p>
 69  
      * <ul>
 70  
      * <li> truncate - paths are truncated to the maximum length </li>
 71  
      * <li> fail - paths greater than the maximum cause a build exception </li>
 72  
      * <li> warn - paths greater than the maximum cause a warning and GNU is used </li>
 73  
      * <li> gnu - GNU extensions are used for any paths greater than the maximum. </li>
 74  
      * <li> posix - posix extensions are used for any paths greater than the maximum. </li>
 75  
      * <li> posixwarn - posix extensions are used (with warning) for any paths greater than the maximum. </li>
 76  
      * <li> omit - paths greater than the maximum are omitted from the archive </li>
 77  
      * </ul>
 78  
      *
 79  
      * @param mode the mode to handle long file names.
 80  
      */
 81  
     public void setLongfile( TarLongFileMode mode )
 82  
     {
 83  33
         this.longFileMode = mode;
 84  33
     }
 85  
 
 86  
     /**
 87  
      * Set compression method.
 88  
      * Allowable values are
 89  
      * <ul>
 90  
      * <li> none - no compression
 91  
      * <li> gzip - Gzip compression
 92  
      * <li> bzip2 - Bzip2 compression
 93  
      * </ul>
 94  
      *
 95  
      * @param mode the compression method.
 96  
      */
 97  
     public void setCompression( TarCompressionMethod mode )
 98  
     {
 99  0
         this.compression = mode;
 100  0
     }
 101  
 
 102  
     @Override
 103  
     protected void execute()
 104  
         throws ArchiverException, IOException
 105  
     {
 106  33
         if ( !checkForced() )
 107  
         {
 108  0
             return;
 109  
         }
 110  
 
 111  33
         ResourceIterator iter = getResources();
 112  33
         if ( !iter.hasNext() )
 113  
         {
 114  1
             throw new EmptyArchiveException( "archive cannot be empty" );
 115  
         }
 116  
 
 117  32
         File tarFile = getDestFile();
 118  
 
 119  32
         if ( tarFile == null )
 120  
         {
 121  0
             throw new ArchiverException( "You must set the destination tar file." );
 122  
         }
 123  32
         if ( tarFile.exists() && !tarFile.isFile() )
 124  
         {
 125  0
             throw new ArchiverException( tarFile + " isn't a file." );
 126  
         }
 127  32
         if ( tarFile.exists() && !tarFile.canWrite() )
 128  
         {
 129  0
             throw new ArchiverException( tarFile + " is read-only." );
 130  
         }
 131  
 
 132  32
         getLogger().info( "Building tar: " + tarFile.getAbsolutePath() );
 133  
 
 134  
         try
 135  
         {
 136  32
             tOut = new TarArchiveOutputStream( compress( compression, new FileOutputStream( tarFile ) ), "UTF8" );
 137  32
             if ( longFileMode.isTruncateMode() )
 138  
             {
 139  0
                 tOut.setLongFileMode( TarArchiveOutputStream.LONGFILE_TRUNCATE );
 140  
             }
 141  32
             else if ( longFileMode.isPosixMode() || longFileMode.isPosixWarnMode() )
 142  
             {
 143  31
                 tOut.setLongFileMode( TarArchiveOutputStream.LONGFILE_POSIX );
 144  
                 // Todo: Patch 2.5.1   for this fix. Also make closeable fix on 2.5.1
 145  31
                 tOut.setBigNumberMode( TarArchiveOutputStream.BIGNUMBER_POSIX );
 146  
             }
 147  1
             else if ( longFileMode.isFailMode() || longFileMode.isOmitMode() )
 148  
             {
 149  0
                 tOut.setLongFileMode( TarArchiveOutputStream.LONGFILE_ERROR );
 150  
             }
 151  
             else
 152  
             {
 153  
                 // warn or GNU
 154  1
                 tOut.setLongFileMode( TarArchiveOutputStream.LONGFILE_GNU );
 155  
             }
 156  
 
 157  32
             longWarningGiven = false;
 158  4151
             while ( iter.hasNext() )
 159  
             {
 160  4119
                 ArchiveEntry entry = iter.next();
 161  
                 // Check if we don't add tar file in itself
 162  4119
                 if ( ResourceUtils.isSame( entry.getResource(), tarFile ) )
 163  
                 {
 164  0
                     throw new ArchiverException( "A tar file cannot include itself." );
 165  
                 }
 166  4119
                 String fileName = entry.getName();
 167  4119
                 String name = StringUtils.replace( fileName, File.separatorChar, '/' );
 168  
 
 169  4119
                 tarFile( entry, tOut, name );
 170  4119
             }
 171  
 
 172  32
             tOut.close();
 173  
         }
 174  
         finally
 175  
         {
 176  32
             IOUtil.close( tOut );
 177  32
         }
 178  32
     }
 179  
 
 180  
     /**
 181  
      * tar a file
 182  
      *
 183  
      * @param entry the file to tar
 184  
      * @param tOut the output stream
 185  
      * @param vPath the path name of the file to tar
 186  
      *
 187  
      * @throws IOException on error
 188  
      */
 189  
     protected void tarFile( ArchiveEntry entry, TarArchiveOutputStream tOut, String vPath )
 190  
         throws ArchiverException, IOException
 191  
     {
 192  
 
 193  
         // don't add "" to the archive
 194  4119
         if ( vPath.length() <= 0 )
 195  
         {
 196  14
             return;
 197  
         }
 198  
 
 199  4105
         if ( entry.getResource().isDirectory() && !vPath.endsWith( "/" ) )
 200  
         {
 201  744
             vPath += "/";
 202  
         }
 203  
 
 204  4105
         if ( vPath.startsWith( "/" ) && !options.getPreserveLeadingSlashes() )
 205  
         {
 206  0
             int l = vPath.length();
 207  0
             if ( l <= 1 )
 208  
             {
 209  
                 // we would end up adding "" to the archive
 210  0
                 return;
 211  
             }
 212  0
             vPath = vPath.substring( 1, l );
 213  
         }
 214  
 
 215  4105
         int pathLength = vPath.length();
 216  4105
         InputStream fIn = null;
 217  
 
 218  
         try
 219  
         {
 220  
             TarArchiveEntry te;
 221  4105
             if ( !longFileMode.isGnuMode()
 222  
                      && pathLength >= org.apache.commons.compress.archivers.tar.TarConstants.NAMELEN )
 223  
             {
 224  26
                 int maxPosixPathLen = org.apache.commons.compress.archivers.tar.TarConstants.NAMELEN
 225  
                                           + org.apache.commons.compress.archivers.tar.TarConstants.PREFIXLEN;
 226  26
                 if ( longFileMode.isPosixMode() )
 227  
                 {
 228  
                 }
 229  0
                 else if ( longFileMode.isPosixWarnMode() )
 230  
                 {
 231  0
                     if ( pathLength > maxPosixPathLen )
 232  
                     {
 233  0
                         getLogger().warn( "Entry: " + vPath + " longer than " + maxPosixPathLen + " characters." );
 234  0
                         if ( !longWarningGiven )
 235  
                         {
 236  0
                             getLogger().warn( "Resulting tar file can only be processed "
 237  
                                                   + "successfully by GNU compatible tar commands" );
 238  0
                             longWarningGiven = true;
 239  
                         }
 240  
                     }
 241  
                 }
 242  0
                 else if ( longFileMode.isOmitMode() )
 243  
                 {
 244  0
                     getLogger().info( "Omitting: " + vPath );
 245  
                     return;
 246  
                 }
 247  0
                 else if ( longFileMode.isWarnMode() )
 248  
                 {
 249  0
                     getLogger().warn( "Entry: " + vPath + " longer than "
 250  
                                           + org.apache.commons.compress.archivers.tar.TarConstants.NAMELEN
 251  
                                           + " characters." );
 252  0
                     if ( !longWarningGiven )
 253  
                     {
 254  0
                         getLogger().warn( "Resulting tar file can only be processed "
 255  
                                               + "successfully by GNU compatible tar commands" );
 256  0
                         longWarningGiven = true;
 257  
                     }
 258  
                 }
 259  0
                 else if ( longFileMode.isFailMode() )
 260  
                 {
 261  0
                     throw new ArchiverException( "Entry: " + vPath + " longer than "
 262  
                                                      + org.apache.commons.compress.archivers.tar.TarConstants.NAMELEN
 263  
                                                      + " characters." );
 264  
                 }
 265  
                 else
 266  
                 {
 267  0
                     throw new IllegalStateException( "Non gnu mode should never get here?" );
 268  
                 }
 269  
             }
 270  
 
 271  4105
             if ( entry.getType() == ArchiveEntry.SYMLINK )
 272  
             {
 273  100
                 final SymlinkDestinationSupplier plexusIoSymlinkResource =
 274  
                     (SymlinkDestinationSupplier) entry.getResource();
 275  
 
 276  100
                 te = new TarArchiveEntry( vPath, TarArchiveEntry.LF_SYMLINK );
 277  100
                 te.setLinkName( plexusIoSymlinkResource.getSymlinkDestination() );
 278  100
             }
 279  
             else
 280  
             {
 281  4005
                 te = new TarArchiveEntry( vPath );
 282  
             }
 283  
 
 284  4105
             long teLastModified = entry.getResource().getLastModified();
 285  4105
             te.setModTime( teLastModified == PlexusIoResource.UNKNOWN_MODIFICATION_DATE
 286  
                                ? System.currentTimeMillis()
 287  
                                : teLastModified );
 288  
 
 289  4105
             if ( entry.getType() == ArchiveEntry.SYMLINK )
 290  
             {
 291  100
                 te.setSize( 0 );
 292  
 
 293  
             }
 294  4005
             else if ( !entry.getResource().isDirectory() )
 295  
             {
 296  2942
                 final long size = entry.getResource().getSize();
 297  2942
                 te.setSize( size == PlexusIoResource.UNKNOWN_RESOURCE_SIZE ? 0 : size );
 298  
             }
 299  4105
             te.setMode( entry.getMode() );
 300  
 
 301  4105
             PlexusIoResourceAttributes attributes = entry.getResourceAttributes();
 302  
 
 303  4105
             te.setUserName( ( attributes != null && attributes.getUserName() != null )
 304  
                                 ? attributes.getUserName()
 305  
                                 : options.getUserName() );
 306  4105
             te.setGroupName( ( attributes != null && attributes.getGroupName() != null )
 307  
                                  ? attributes.getGroupName()
 308  
                                  : options.getGroup() );
 309  
 
 310  4105
             final int userId =
 311  
                 ( attributes != null && attributes.getUserId() != null ) ? attributes.getUserId() : options.getUid();
 312  4105
             if ( userId >= 0 )
 313  
             {
 314  4105
                 te.setUserId( userId );
 315  
             }
 316  
 
 317  4105
             final int groupId =
 318  
                 ( attributes != null && attributes.getGroupId() != null ) ? attributes.getGroupId() : options.getGid();
 319  4105
             if ( groupId >= 0 )
 320  
             {
 321  4105
                 te.setGroupId( groupId );
 322  
             }
 323  
 
 324  4105
             tOut.putArchiveEntry( te );
 325  
 
 326  
             try
 327  
             {
 328  4105
                 if ( entry.getResource().isFile() && !( entry.getType() == ArchiveEntry.SYMLINK ) )
 329  
                 {
 330  2942
                     fIn = entry.getInputStream();
 331  
 
 332  2942
                     Streams.copyFullyDontCloseOutput( fIn, tOut, "xAR" );
 333  
                 }
 334  
 
 335  
             }
 336  0
             catch ( Throwable e )
 337  
             {
 338  0
                 getLogger().warn( "When creating tar entry", e );
 339  
             }
 340  
             finally
 341  
             {
 342  4105
                 tOut.closeArchiveEntry();
 343  4105
             }
 344  
         }
 345  
         finally
 346  
         {
 347  4105
             IOUtil.close( fIn );
 348  4105
         }
 349  4105
     }
 350  
 
 351  
     /**
 352  
      * Valid Modes for Compression attribute to Tar Task
 353  
      */
 354  34
     public class TarOptions
 355  
     {
 356  
 
 357  34
         private String userName = "";
 358  
 
 359  34
         private String groupName = "";
 360  
 
 361  
         private int uid;
 362  
 
 363  
         private int gid;
 364  
 
 365  34
         private boolean preserveLeadingSlashes = false;
 366  
 
 367  
         /**
 368  
          * The username for the tar entry
 369  
          * This is not the same as the UID.
 370  
          *
 371  
          * @param userName the user name for the tar entry.
 372  
          */
 373  
         public void setUserName( String userName )
 374  
         {
 375  0
             this.userName = userName;
 376  0
         }
 377  
 
 378  
         /**
 379  
          * @return the user name for the tar entry
 380  
          */
 381  
         public String getUserName()
 382  
         {
 383  3
             return userName;
 384  
         }
 385  
 
 386  
         /**
 387  
          * The uid for the tar entry
 388  
          * This is not the same as the User name.
 389  
          *
 390  
          * @param uid the id of the user for the tar entry.
 391  
          */
 392  
         public void setUid( int uid )
 393  
         {
 394  0
             this.uid = uid;
 395  0
         }
 396  
 
 397  
         /**
 398  
          * @return the uid for the tar entry
 399  
          */
 400  
         public int getUid()
 401  
         {
 402  3
             return uid;
 403  
         }
 404  
 
 405  
         /**
 406  
          * The groupname for the tar entry; optional, default=""
 407  
          * This is not the same as the GID.
 408  
          *
 409  
          * @param groupName the group name string.
 410  
          */
 411  
         public void setGroup( String groupName )
 412  
         {
 413  0
             this.groupName = groupName;
 414  0
         }
 415  
 
 416  
         /**
 417  
          * @return the group name string.
 418  
          */
 419  
         public String getGroup()
 420  
         {
 421  3
             return groupName;
 422  
         }
 423  
 
 424  
         /**
 425  
          * The GID for the tar entry; optional, default="0"
 426  
          * This is not the same as the group name.
 427  
          *
 428  
          * @param gid the group id.
 429  
          */
 430  
         public void setGid( int gid )
 431  
         {
 432  0
             this.gid = gid;
 433  0
         }
 434  
 
 435  
         /**
 436  
          * @return the group identifier.
 437  
          */
 438  
         public int getGid()
 439  
         {
 440  3
             return gid;
 441  
         }
 442  
 
 443  
         /**
 444  
          * @return the leading slashes flag.
 445  
          */
 446  
         public boolean getPreserveLeadingSlashes()
 447  
         {
 448  0
             return preserveLeadingSlashes;
 449  
         }
 450  
 
 451  
         /**
 452  
          * Flag to indicates whether leading `/'s should
 453  
          * be preserved in the file names.
 454  
          * Optional, default is <code>false</code>.
 455  
          *
 456  
          * @param preserveLeadingSlashes the leading slashes flag.
 457  
          */
 458  
         public void setPreserveLeadingSlashes( boolean preserveLeadingSlashes )
 459  
         {
 460  0
             this.preserveLeadingSlashes = preserveLeadingSlashes;
 461  0
         }
 462  
 
 463  
     }
 464  
 
 465  
     /**
 466  
      * Valid Modes for Compression attribute to Tar Task
 467  
      */
 468  34
     public static enum TarCompressionMethod
 469  
     {
 470  
 
 471  1
         none,
 472  1
         gzip,
 473  1
         bzip2,
 474  1
         snappy,
 475  1
         xz
 476  
 
 477  
     }
 478  
 
 479  
     private OutputStream compress( TarCompressionMethod tarCompressionMethod, final OutputStream ostream )
 480  
         throws IOException
 481  
     {
 482  32
         if ( TarCompressionMethod.gzip.equals( tarCompressionMethod ) )
 483  
         {
 484  0
             return Streams.bufferedOutputStream( new GZIPOutputStream( ostream ) );
 485  
         }
 486  32
         else if ( TarCompressionMethod.bzip2.equals( tarCompressionMethod ) )
 487  
         {
 488  0
             return new BZip2CompressorOutputStream( bufferedOutputStream( ostream ) );
 489  
         }
 490  32
         else if ( TarCompressionMethod.snappy.equals( tarCompressionMethod ) )
 491  
         {
 492  0
             return new SnappyOutputStream( bufferedOutputStream( ostream ) );
 493  
         }
 494  32
         else if ( TarCompressionMethod.xz.equals( tarCompressionMethod ) )
 495  
         {
 496  0
             return new XZCompressorOutputStream( bufferedOutputStream( ostream ) );
 497  
         }
 498  
 
 499  32
         return ostream;
 500  
     }
 501  
 
 502  
     @Override
 503  
     public boolean isSupportingForced()
 504  
     {
 505  0
         return true;
 506  
     }
 507  
 
 508  
     @Override
 509  
     protected void cleanUp()
 510  
         throws IOException
 511  
     {
 512  33
         super.cleanUp();
 513  33
         if ( this.tOut != null )
 514  
         {
 515  32
             this.tOut.close();
 516  
         }
 517  33
     }
 518  
 
 519  
     @Override
 520  
     protected void close()
 521  
         throws IOException
 522  
     {
 523  33
         if ( this.tOut != null )
 524  
         {
 525  32
             this.tOut.close();
 526  
         }
 527  33
     }
 528  
 
 529  
     @Override
 530  
     protected String getArchiveType()
 531  
     {
 532  0
         return "TAR";
 533  
     }
 534  
 
 535  
 }