Coverage report

  %line %branch
org.apache.jcs.auxiliary.disk.indexed.IndexedDisk
75% 
98% 

 1  
 package org.apache.jcs.auxiliary.disk.indexed;
 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.FileNotFoundException;
 24  
 import java.io.IOException;
 25  
 import java.io.RandomAccessFile;
 26  
 import java.io.Serializable;
 27  
 
 28  
 import org.apache.commons.logging.Log;
 29  
 import org.apache.commons.logging.LogFactory;
 30  
 import org.apache.jcs.utils.serialization.StandardSerializer;
 31  
 
 32  
 /**
 33  
  * Provides thread safe access to the underlying random access file.
 34  
  */
 35  
 class IndexedDisk
 36  
 {
 37  
     /**
 38  
      * The size of the header in bytes. The header describes the length of the entry.
 39  
      */
 40  
     public static final int RECORD_HEADER = 4;
 41  
 
 42  73
     private static final StandardSerializer SERIALIZER = new StandardSerializer();
 43  
 
 44  146
     private static final Log log = LogFactory.getLog( IndexedDisk.class );
 45  
 
 46  
     private final String filepath;
 47  
 
 48  
     private RandomAccessFile raf;
 49  
 
 50  730
     private final byte[] buffer = new byte[16384]; // 16K
 51  
 
 52  
     /**
 53  
      * Constructor for the Disk object
 54  
      * <p>
 55  
      * @param file
 56  
      * @exception FileNotFoundException
 57  
      */
 58  
     public IndexedDisk( File file )
 59  
         throws FileNotFoundException
 60  730
     {
 61  730
         this.filepath = file.getAbsolutePath();
 62  730
         raf = new RandomAccessFile( filepath, "rw" );
 63  730
     }
 64  
 
 65  
     /**
 66  
      * This reads an object from the given starting position on the file.
 67  
      * <p>
 68  
      * The first four bytes of the record should tell us how long it is. The data is read into a byte
 69  
      * array and then an object is constructed from the byte array.
 70  
      * <p>
 71  
      * @return Serializable
 72  
      * @param ded
 73  
      * @throws IOException
 74  
      * @throws ClassNotFoundException
 75  
      */
 76  
     protected Serializable readObject( IndexedDiskElementDescriptor ded )
 77  
         throws IOException, ClassNotFoundException
 78  
     {
 79  375074
         byte[] data = null;
 80  375072
         synchronized ( this )
 81  
         {
 82  375085
             String message = null;
 83  375083
             boolean corrupted = false;
 84  374975
             long fileLength = raf.length();
 85  375019
             if ( ded.pos > fileLength )
 86  
             {
 87  0
                 corrupted = true;
 88  0
                 message = "Record " + ded + " starts past EOF.";
 89  0
             }
 90  
             else
 91  
             {
 92  375051
                 raf.seek( ded.pos );
 93  374876
                 int datalen = raf.readInt();
 94  375098
                 if ( ded.len != datalen )
 95  
                 {
 96  0
                     corrupted = true;
 97  0
                     message = "Record " + ded + " does not match data length on disk (" + datalen + ")";
 98  0
                 }
 99  375133
                 else if ( ded.pos + ded.len > fileLength )
 100  
                 {
 101  0
                     corrupted = true;
 102  0
                     message = "Record " + ded + " exceeds file length.";
 103  
                 }
 104  
             }
 105  
 
 106  375023
             if ( corrupted )
 107  
             {
 108  0
                 log.warn( "\n The file is corrupt: " + "\n " + message );
 109  0
                 throw new IOException( "The File Is Corrupt, need to reset" );
 110  
             }
 111  
 
 112  374974
             raf.readFully( data = new byte[ded.len] );
 113  375066
         }
 114  
 
 115  375094
         return (Serializable) SERIALIZER.deSerialize( data );
 116  
     }
 117  
 
 118  
     /**
 119  
      * Moves the data stored from one position to another. The descriptor's position is updated.
 120  
      * <p>
 121  
      * @param ded
 122  
      * @param newPosition
 123  
      * @throws IOException
 124  
      */
 125  
     protected void move( final IndexedDiskElementDescriptor ded, class="keyword">final long newPosition )
 126  
         throws IOException
 127  
     {
 128  100
         synchronized ( this )
 129  
         {
 130  100
             raf.seek( ded.pos );
 131  100
             int length = raf.readInt();
 132  
 
 133  100
             if ( length != ded.len )
 134  
             {
 135  0
                 throw new IOException( "Mismatched memory and disk length (" + length + ") for " + ded );
 136  
             }
 137  
 
 138  
             // TODO: more checks?
 139  
 
 140  100
             long readPos = ded.pos;
 141  100
             long writePos = newPosition;
 142  
 
 143  
             // header len + data len
 144  100
             int remaining = RECORD_HEADER + length;
 145  
 
 146  300
             while ( remaining > 0 )
 147  
             {
 148  
                 // chunk it
 149  200
                 int chunkSize = Math.min( remaining, buffer.length );
 150  200
                 raf.seek( readPos );
 151  200
                 raf.readFully( buffer, 0, chunkSize );
 152  
 
 153  200
                 raf.seek( writePos );
 154  200
                 raf.write( buffer, 0, chunkSize );
 155  
 
 156  200
                 writePos += chunkSize;
 157  200
                 readPos += chunkSize;
 158  200
                 remaining -= chunkSize;
 159  200
             }
 160  
 
 161  100
             ded.pos = newPosition;
 162  100
         }
 163  100
     }
 164  
 
 165  
     /**
 166  
      * Writes the given byte array to the Disk at the specified position.
 167  
      * <p>
 168  
      * @param data
 169  
      * @param ded
 170  
      * @return true if we wrote successfully
 171  
      * @throws IOException
 172  
      */
 173  
     protected boolean write( IndexedDiskElementDescriptor ded, byte[] data )
 174  
         throws IOException
 175  
     {
 176  678625
         long pos = ded.pos;
 177  678650
         if ( log.isTraceEnabled() )
 178  
         {
 179  0
             log.trace( "write> pos=" + pos );
 180  0
             log.trace( raf + " -- data.length = " + data.length );
 181  
         }
 182  
 
 183  678583
         if ( data.length != ded.len )
 184  
         {
 185  0
             throw new IOException( "Mismatched descriptor and data lengths" );
 186  
         }
 187  
 
 188  678651
         synchronized ( this )
 189  
         {
 190  678589
             raf.seek( pos );
 191  678613
             raf.writeInt( data.length );
 192  678645
             raf.write( data, 0, ded.len );
 193  678604
         }
 194  678651
         return true;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Serializes the object and write it out to the given position.
 199  
      * <p>
 200  
      * TODO: make this take a ded as well.
 201  
      * @return
 202  
      * @param obj
 203  
      * @param pos
 204  
      * @throws IOException
 205  
      */
 206  
     protected boolean writeObject( Serializable obj, long pos )
 207  
         throws IOException
 208  
     {
 209  68
         byte[] data = SERIALIZER.serialize( obj );
 210  68
         write( new IndexedDiskElementDescriptor( pos, data.length ), data );
 211  68
         return true;
 212  
     }
 213  
 
 214  
     /**
 215  
      * Returns the raf length.
 216  
      * <p>
 217  
      * @return
 218  
      * @exception IOException
 219  
      */
 220  
     protected long length()
 221  
         throws IOException
 222  
     {
 223  294383
         synchronized ( this )
 224  
         {
 225  294441
             return raf.length();
 226  0
         }
 227  
     }
 228  
 
 229  
     /**
 230  
      * Closes the raf.
 231  
      * <p>
 232  
      * @exception IOException
 233  
      */
 234  
     protected synchronized void close()
 235  
         throws IOException
 236  
     {
 237  686
         raf.close();
 238  686
     }
 239  
 
 240  
     /**
 241  
      * Sets the raf to empty.
 242  
      * <p>
 243  
      * @exception IOException
 244  
      */
 245  
     protected synchronized void reset()
 246  
         throws IOException
 247  
     {
 248  295
         if ( log.isDebugEnabled() )
 249  
         {
 250  0
             log.debug( "Resetting Indexed File [" + filepath + "]" );
 251  
         }
 252  296
         raf.close();
 253  296
         File f = new File( filepath );
 254  296
         int i = 0;
 255  296
         for ( ; i < 10 && !f.delete(); i++ )
 256  
         {
 257  
             try
 258  
             {
 259  0
                 Thread.sleep( 1000 );
 260  
             }
 261  0
             catch ( InterruptedException ex )
 262  
             {
 263  
                 // swallow
 264  0
             }
 265  0
             log.warn( "Failed to delete " + f.getName() + " " + i );
 266  
         }
 267  296
         if ( i == 10 )
 268  
         {
 269  0
             IllegalStateException ex = new IllegalStateException( "Failed to delete " + f.getName() );
 270  0
             log.error( ex );
 271  0
             throw ex;
 272  
         }
 273  296
         raf = new RandomAccessFile( filepath, "rw" );
 274  296
     }
 275  
 
 276  
     /**
 277  
      * Returns the serialized form of the given object in a byte array.
 278  
      * <p>
 279  
      * Use the Serilizer abstraction layer.
 280  
      * <p>
 281  
      * @return a byte array of the serialized object.
 282  
      * @param obj
 283  
      * @exception IOException
 284  
      */
 285  
     protected static byte[] serialize( Serializable obj )
 286  
         throws IOException
 287  
     {
 288  678502
         return SERIALIZER.serialize( obj );
 289  
     }
 290  
 
 291  
     /**
 292  
      * Truncates the file to a given length.
 293  
      * <p>
 294  
      * @param length the new length of the file
 295  
      * @throws IOException
 296  
      */
 297  
     protected void truncate( long length )
 298  
         throws IOException
 299  
     {
 300  105
         if ( log.isInfoEnabled() )
 301  
         {
 302  105
             log.info( "Trucating file [" + filepath + "] to " + length );
 303  
         }
 304  105
         raf.setLength( length );
 305  105
     }
 306  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.