Coverage Report - org.apache.maven.plugin.clean.Cleaner
 
Classes in this File Line Coverage Branch Coverage Complexity
Cleaner
88%
53/60
67%
47/70
3,778
Cleaner$1
0%
0/3
N/A
3,778
Cleaner$2
100%
3/3
N/A
3,778
Cleaner$3
100%
3/3
N/A
3,778
Cleaner$Logger
N/A
N/A
3,778
Cleaner$Result
100%
4/4
N/A
3,778
 
 1  
 package org.apache.maven.plugin.clean;
 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.plugin.logging.Log;
 26  
 import org.codehaus.plexus.util.Os;
 27  
 
 28  
 /**
 29  
  * Cleans directories.
 30  
  * 
 31  
  * @author Benjamin Bentmann
 32  
  */
 33  
 class Cleaner
 34  
 {
 35  
 
 36  1
     private static final boolean ON_WINDOWS = Os.isFamily( Os.FAMILY_WINDOWS );
 37  
 
 38  
     private final Logger logDebug;
 39  
 
 40  
     private final Logger logInfo;
 41  
 
 42  
     private final Logger logVerbose;
 43  
 
 44  
     private final Logger logWarn;
 45  
 
 46  
     /**
 47  
      * Creates a new cleaner.
 48  
      * 
 49  
      * @param log The logger to use, may be <code>null</code> to disable logging.
 50  
      * @param verbose Whether to perform verbose logging.
 51  
      */
 52  
     public Cleaner( final Log log, boolean verbose )
 53  8
     {
 54  8
         logDebug = ( log == null || !log.isDebugEnabled() ) ? null : new Logger()
 55  
         {
 56  0
             public void log( CharSequence message )
 57  
             {
 58  0
                 log.debug( message );
 59  0
             }
 60  
         };
 61  
 
 62  8
         logInfo = ( log == null || !log.isInfoEnabled() ) ? null : new Logger()
 63  
         {
 64  8
             public void log( CharSequence message )
 65  
             {
 66  30
                 log.info( message );
 67  30
             }
 68  
         };
 69  
 
 70  8
         logWarn = ( log == null || !log.isWarnEnabled() ) ? null : new Logger()
 71  
         {
 72  8
             public void log( CharSequence message )
 73  
             {
 74  2
                 log.warn( message );
 75  2
             }
 76  
         };
 77  
 
 78  8
         logVerbose = verbose ? logInfo : logDebug;
 79  8
     }
 80  
 
 81  
     /**
 82  
      * Deletes the specified directories and its contents.
 83  
      * 
 84  
      * @param basedir The directory to delete, must not be <code>null</code>. Non-existing directories will be silently
 85  
      *            ignored.
 86  
      * @param selector The selector used to determine what contents to delete, may be <code>null</code> to delete
 87  
      *            everything.
 88  
      * @param followSymlinks Whether to follow symlinks.
 89  
      * @param failOnError Whether to abort with an exception in case a selected file/directory could not be deleted.
 90  
      * @throws IOException If a file/directory could not be deleted and <code>failOnError</code> is <code>true</code>.
 91  
      */
 92  
     public void delete( File basedir, Selector selector, boolean followSymlinks, boolean failOnError )
 93  
         throws IOException
 94  
     {
 95  12
         if ( !basedir.isDirectory() )
 96  
         {
 97  4
             if ( !basedir.exists() )
 98  
             {
 99  3
                 if ( logDebug != null )
 100  
                 {
 101  0
                     logDebug.log( "Skipping non-existing directory " + basedir );
 102  
                 }
 103  3
                 return;
 104  
             }
 105  1
             throw new IOException( "Invalid base directory " + basedir );
 106  
         }
 107  
 
 108  8
         if ( logInfo != null )
 109  
         {
 110  8
             logInfo.log( "Deleting " + basedir + ( selector != null ? " (" + selector + ")" : "" ) );
 111  
         }
 112  
 
 113  8
         File file = followSymlinks ? basedir : basedir.getCanonicalFile();
 114  
 
 115  8
         delete( file, "", selector, followSymlinks, failOnError );
 116  7
     }
 117  
 
 118  
     /**
 119  
      * Deletes the specified file or directory.
 120  
      * 
 121  
      * @param file The file/directory to delete, must not be <code>null</code>. If <code>followSymlinks</code> is
 122  
      *            <code>false</code>, it is assumed that the parent file is canonical.
 123  
      * @param pathname The relative pathname of the file, using {@link File#separatorChar}, must not be
 124  
      *            <code>null</code>.
 125  
      * @param selector The selector used to determine what contents to delete, may be <code>null</code> to delete
 126  
      *            everything.
 127  
      * @param followSymlinks Whether to follow symlinks.
 128  
      * @param failOnError Whether to abort with an exception in case a selected file/directory could not be deleted.
 129  
      * @return The result of the cleaning, never <code>null</code>.
 130  
      * @throws IOException If a file/directory could not be deleted and <code>failOnError</code> is <code>true</code>.
 131  
      */
 132  
     private Result delete( File file, String pathname, Selector selector, boolean followSymlinks, boolean failOnError )
 133  
         throws IOException
 134  
     {
 135  28
         Result result = new Result();
 136  
 
 137  28
         boolean isDirectory = file.isDirectory();
 138  
 
 139  28
         if ( isDirectory )
 140  
         {
 141  14
             if ( selector == null || selector.couldHoldSelected( pathname ) )
 142  
             {
 143  14
                 File canonical = followSymlinks ? file : file.getCanonicalFile();
 144  14
                 if ( followSymlinks || file.equals( canonical ) )
 145  
                 {
 146  14
                     String[] filenames = canonical.list();
 147  14
                     if ( filenames != null )
 148  
                     {
 149  14
                         String prefix = ( pathname.length() > 0 ) ? pathname + File.separatorChar : "";
 150  33
                         for ( int i = filenames.length - 1; i >= 0; i-- )
 151  
                         {
 152  20
                             String filename = filenames[i];
 153  20
                             File child = new File( canonical, filename );
 154  20
                             result.update( delete( child, prefix + filename, selector, followSymlinks, failOnError ) );
 155  
                         }
 156  
                     }
 157  13
                 }
 158  0
                 else if ( logDebug != null )
 159  
                 {
 160  0
                     logDebug.log( "Not recursing into symlink " + file );
 161  
                 }
 162  13
             }
 163  0
             else if ( logDebug != null )
 164  
             {
 165  0
                 logDebug.log( "Not recursing into directory without included files " + file );
 166  
             }
 167  
         }
 168  
 
 169  27
         if ( !result.excluded && ( selector == null || selector.isSelected( pathname ) ) )
 170  
         {
 171  22
             if ( logVerbose != null )
 172  
             {
 173  22
                 if ( isDirectory )
 174  
                 {
 175  9
                     logVerbose.log( "Deleting directory " + file );
 176  
                 }
 177  13
                 else if ( file.exists() )
 178  
                 {
 179  13
                     logVerbose.log( "Deleting file " + file );
 180  
                 }
 181  
                 else
 182  
                 {
 183  0
                     logVerbose.log( "Deleting dangling symlink " + file );
 184  
                 }
 185  
             }
 186  22
             result.failures += delete( file, failOnError );
 187  
         }
 188  
         else
 189  
         {
 190  5
             result.excluded = true;
 191  
         }
 192  
 
 193  26
         return result;
 194  
     }
 195  
 
 196  
     /**
 197  
      * Deletes the specified file, directory. If the path denotes a symlink, only the link is removed, its target is
 198  
      * left untouched.
 199  
      * 
 200  
      * @param file The file/directory to delete, must not be <code>null</code>.
 201  
      * @param failOnError Whether to abort with an exception in case the file/directory could not be deleted.
 202  
      * @return <code>0</code> if the file was deleted, <code>1</code> otherwise.
 203  
      * @throws IOException If a file/directory could not be deleted and <code>failOnError</code> is <code>true</code>.
 204  
      */
 205  
     private int delete( File file, boolean failOnError )
 206  
         throws IOException
 207  
     {
 208  22
         if ( !file.delete() )
 209  
         {
 210  3
             if ( ON_WINDOWS )
 211  
             {
 212  
                 // try to release any locks held by non-closed files
 213  3
                 System.gc();
 214  
             }
 215  
             try
 216  
             {
 217  3
                 Thread.sleep( 10 );
 218  
             }
 219  0
             catch ( InterruptedException e )
 220  
             {
 221  
                 // ignore
 222  3
             }
 223  3
             if ( !file.delete() )
 224  
             {
 225  3
                 if ( failOnError )
 226  
                 {
 227  1
                     throw new IOException( "Failed to delete " + file );
 228  
                 }
 229  
                 else
 230  
                 {
 231  2
                     if ( logWarn != null )
 232  
                     {
 233  2
                         logWarn.log( "Failed to delete " + file );
 234  
                     }
 235  2
                     return 1;
 236  
                 }
 237  
             }
 238  
         }
 239  
 
 240  19
         return 0;
 241  
     }
 242  
 
 243  56
     private static class Result
 244  
     {
 245  
 
 246  
         public int failures;
 247  
 
 248  
         public boolean excluded;
 249  
 
 250  
         public void update( Result result )
 251  
         {
 252  19
             failures += result.failures;
 253  19
             excluded |= result.excluded;
 254  19
         }
 255  
 
 256  
     }
 257  
 
 258  
     private static interface Logger
 259  
     {
 260  
 
 261  
         public void log( CharSequence message );
 262  
 
 263  
     }
 264  
 
 265  
 }