/* Open Source Java Caching Service
* Copyright (C) 2002 Frank Karlstr�m
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* The author can be contacted by email: fjankk@users.sourceforge.net
*/
package org.fjank.jcache.persistence;
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.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import javax.util.jcache.DiskCacheException;
import org.fjank.jcache.CacheObject;
import org.fjank.jcache.DiskCacheObject;
/**
* Provides thread safe access to the underlying random access file.
*/
public class CacheFileAdapter {
/** the path to the files */
private final String filepath;
/** an accessor to the file */
private RandomAccessFile raf;
/**
* Creates a new CacheFileAdapter object.
*
* @param file the file to adapt to
*
* @throws DiskCacheException if any exceptioins occur.
*/
public CacheFileAdapter(final File file) {
this.filepath = file.getAbsolutePath();
try {
raf = new RandomAccessFile(filepath, "rw");
} catch (FileNotFoundException e) {
throw new IllegalStateException(
"The disk cache could not be initialized.");
}
}
/**
* returns a String representation of this object.
*
* @return a String representation of this object.
*/
public String toString() {
return getClass().getName() + ", file:" + filepath;
}
/**
* reads from the diskfile, and return the raw bytes
* contained at that pos. Its up to the client to deSerialize.
* @param pos the position to start at.
* @return a byte[] containing the raw bytes for the object.
*/
public byte[] read(long pos) {
byte[] data = null;
boolean corrupted = false;
try {
synchronized (this) {
raf.seek(pos);
int datalen = raf.readInt();
if (datalen > raf.length()) {
corrupted = true;
} else {
raf.readFully(data = new byte[datalen]);
}
}
if (corrupted) {
throw new IllegalStateException("The Cache file is corrupted.");
}
return data;
} catch (IOException e) {
throw new IllegalStateException(e.getMessage());
}
}
/**
* reads an object starting at the position,.
*
* @param pos the position to start at
*
* @return a Serializable object read from the diskcache.
* @throws DiskCacheException
*
* @throws DiskCacheException if any exceptions occur.
*/
public Serializable readObject(long pos) throws DiskCacheException {
byte[] data = read(pos);
return deSerialize(data);
}
public synchronized void append(byte[] data) throws DiskCacheException {
try {
write(data, raf.length());
} catch (IOException e) {
throw new DiskCacheException(e);
}
}
public synchronized void write(byte[] data, long pos) {
try {
raf.seek(pos);
raf.writeInt(data.length);
raf.write(data);
} catch (IOException e) {
throw new IllegalStateException(e.getMessage());
}
}
/**
* writes an object down to the cachefile
*
* @param obj the object to write
* @param pos the position to start at.
*
* @throws DiskCacheException if any exception occur.
*/
public void writeObject(final Serializable obj, final long pos) {
write(serialize(obj), pos);
}
/**
* adds an object to the diskcache.
*
* @param obj the object to add.
*
* @return a disk element descriptor used to locate the object
*
* @throws DiskCacheException if any exceptions occur.
*/
public DiskElementDescriptor appendObject(CacheObject obj) {
long pos = -1;
DiskElementDescriptor ded = new DiskElementDescriptor();
byte[] data = serialize((Serializable) obj);
synchronized (this) {
pos = length();
ded.init(pos, data);
write(data, pos);
}
return ded;
}
public synchronized long length() {
try {
return raf.length();
} catch (IOException e) {
throw new IllegalStateException(e.getMessage());
}
}
public synchronized void close() {
try {
raf.close();
} catch (IOException e) {
throw new IllegalStateException(e.getMessage());
}
}
public synchronized void reset() {
close();
File f = new File(filepath);
//boolean deleted = f.delete();
int retries=10;
while (!f.delete()) {
retries--;
if(retries<=0) {
throw new IllegalStateException("Failed to delete " + f.getName());
}
// todo busy wait.
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
//interrupted.
}
}
try {
raf = new RandomAccessFile(filepath, "rw");
} catch (FileNotFoundException e) {
throw new IllegalStateException(e.getMessage());
}
}
public static byte[] serialize(Serializable obj) {
ObjectOutputStream oos = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return baos.toByteArray();
} catch (NotSerializableException e) {
throw new IllegalStateException(e.getMessage());
} catch (IOException e) {
throw new IllegalStateException(e.getMessage());
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
throw new IllegalStateException(
"Failed to close stream to DiskCache, "
+ "cache may be corrupted. Reason: " + e.getMessage());
}
}
}
}
public static DiskCacheObject deSerialize(byte[] data) throws DiskCacheException {
ObjectInputStream ois = null;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
BufferedInputStream bis = new BufferedInputStream(bais);
ois=new ObjectInputStream(bis);
DiskCacheObject obj = (DiskCacheObject) ois.readObject();
obj.getCacheObject().resetRefCount();
return obj;
} catch (IOException e) {
throw new IllegalStateException("An exception occured when reading from the disk cache." +
" Reason: " + e.getMessage());
} catch (ClassNotFoundException e) {
throw new IllegalStateException("A class could not be found when deSerializing." +
" Reason: " + e.getMessage());
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
throw new IllegalStateException(
"Failed to close stream to DiskCache, "
+ "cache may be corrupted. Reason: " + e.getMessage());
}
}
}
}
}