blob: d01d43d94c81209d859c2e481784d3b19b73e10c [file] [log] [blame]
package org.apache.jcs.auxiliary.disk.indexed;
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import org.apache.jcs.engine.CacheElement;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Provides thread safe access to the underlying random access file.
*
*/
class IndexedDisk
{
private static final Log log =
LogFactory.getLog( IndexedDisk.class );
private final String filepath;
private RandomAccessFile raf;
/**
* Constructor for the Disk object
*
* @param file
* @exception FileNotFoundException
*/
IndexedDisk( File file )
throws FileNotFoundException
{
this.filepath = file.getAbsolutePath();
raf = new RandomAccessFile( filepath, "rw" );
}
/**
* This reads an object from the given starting position on the file.
* <p>
* The firt four bytes of the record should tell us how long it is.
* The data is read into a byte array and then an object is constructed
* from the byte array.
*
* @return Serializable
* @param pos
* @throws IOException
*/
Serializable readObject( long pos ) throws IOException
{
String message = null;
byte[] data = null;
boolean corrupted = false;
try
{
synchronized ( this )
{
if ( pos > raf.length() ) {
corrupted = true;
message = "Postion is greater than raf length";
}
else
{
raf.seek(pos);
int datalen = raf.readInt();
if (datalen > raf.length())
{
corrupted = true;
message = "Postion(" + pos + ") + datalen (" + datalen + ") is greater than raf length";
}
else
{
raf.readFully(data = new byte[datalen]);
}
}
}
if ( corrupted )
{
log.warn( "\n The dataFile is corrupted!" +
"\n " + message +
"\n raf.length() = " + raf.length() +
"\n pos = " + pos );
//reset();
throw new IOException( "The Data File Is Corrupt, need to reset" );
// return null;
}
ByteArrayInputStream bais = new ByteArrayInputStream( data );
BufferedInputStream bis = new BufferedInputStream( bais );
ObjectInputStream ois = new ObjectInputStream( bis );
try
{
return ( Serializable ) ois.readObject();
}
finally
{
ois.close();
}
}
catch ( Exception e )
{
log.error( raf, e );
if( e instanceof IOException ) {
throw (IOException)e;
}
}
return null;
}
/**
* Appends byte array to the Disk.
*
* @return
* @param data
*/
boolean append( byte[] data )
{
try
{
synchronized ( this )
{
return write( data, raf.length() );
}
}
catch ( IOException ex )
{
ex.printStackTrace();
}
return false;
}
/**
* Writes the given byte array to the Disk at the specified position.
*
* @return
* @param data
* @param pos
*/
boolean write( byte[] data, long pos )
{
if ( log.isDebugEnabled() )
{
log.debug( "write> pos=" + pos );
log.debug( raf + " -- data.length = " + data.length );
}
try
{
synchronized ( this )
{
raf.seek( pos );
raf.writeInt( data.length );
raf.write( data );
}
return true;
}
catch ( IOException ex )
{
ex.printStackTrace();
}
return false;
}
/**
* Description of the Method
*
* @return
* @param obj
* @param pos
*/
boolean writeObject( Serializable obj, long pos )
{
try
{
return write( serialize( obj ), pos );
}
catch ( IOException ex )
{
ex.printStackTrace();
}
return false;
}
/**
* Description of the Method
*
* @return
* @param obj
*/
IndexedDiskElementDescriptor appendObject( CacheElement obj )
{
long pos = -1;
boolean success = false;
try
{
IndexedDiskElementDescriptor ded = new IndexedDiskElementDescriptor();
byte[] data = serialize( obj );
synchronized ( this )
{
pos = raf.length();
ded.init( pos, data );
success = write( data, pos );
}
//return success ? new DiskElement(pos, data) : null;
return success ? ded : null;
}
catch ( IOException ex )
{
ex.printStackTrace();
}
return null;
}
/**
* Returns the raf length.
*
* @return
* @exception IOException
*/
long length()
throws IOException
{
synchronized ( this )
{
return raf.length();
}
}
/**
* Closes the raf.
*
* @exception IOException
*/
synchronized void close()
throws IOException
{
raf.close();
return;
}
/**
* Sets the raf to empty.
*
* @exception IOException
*/
synchronized void reset()
throws IOException
{
log.warn( "Resetting data file" );
raf.close();
File f = new File( filepath );
int i = 0;
for ( ; i < 10 && !f.delete(); i++ )
{
try
{
Thread.sleep( 1000 );
}
catch ( InterruptedException ex )
{
}
log.warn( "Failed to delete " + f.getName() + " " + i );
}
if ( i == 10 )
{
IllegalStateException ex = new IllegalStateException( "Failed to delete " + f.getName() );
log.error( ex );
throw ex;
}
raf = new RandomAccessFile( filepath, "rw" );
return;
}
/**
* Returns the serialized form of the given object in a byte array.
*
* @return
* @param obj
* @exception IOException
*/
static byte[] serialize( Serializable obj )
throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
try
{
oos.writeObject( obj );
}
finally
{
oos.close();
}
return baos.toByteArray();
}
}