/**
* edu.utexas.GeDBIT.util.MckoiObjectIOManager 2003.07.17
*
* Copyright Information:
*
* Change Log:
* 2003.07.17: Created by Rui Mao
* 2003.07.25: Add iterator(), modify open(), close(), by Rui Mao
* 2003.07.27: Add another constructor, construct from filename, by Rui Mao
* 2003.07.28: Modify the argument of constructor, only provide whole file name, no path, by Rui Mao
* 2003.07.29: Modify the iterator, now its next() return the actual Object stored, by Rui Mao
*/
package GeDBIT.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import GeDBIT.mckoi.store.Store;
import GeDBIT.mckoi.store.AbstractStore;
import GeDBIT.mckoi.store.Area;
import GeDBIT.mckoi.store.ScatteringFileStore;
import GeDBIT.mckoi.store.BufferManager;
/**
* An implementation of {@link ObjectIOManager} based on the
* Mckoi(http://www.mckoi.com/database) I/O mechanism. It is backed by a
* {@link Store} (defined in Mckoi), and is able to perform cached page-based
* I/O on a file on disk or in memory. The serialized form of an object is read
* from/written to the stream. In Mckoi, a <code>Store</code> manages a
* collection of {@link Area}s. In this class, each Area saves the serialized
* format of an {@link Object}. Therefore, an Object that can be read/written by
* this class should be serializable. For example, the following code creates a
* {@link MckoiObjectIOManager} that works on multiple disk files, using the new
* java.nio package, with cache and page-based access mechanism.
* <p>
* <code>
* //Create a buffer manager, using java.nio mechanism, max page number:65536, page size:4096 bytes,
* you can also use java IO mechsnism, by providing "java IO", which is recommended:<p>
* BufferManager myBM = new BufferManager("Java NIO", 65536, 4096);<p>
*
* //Create a ScatteringFileStore, the disk files are on current directory, file names start with "test"
* if one file grows too big, it will be split, all files have extension names start from "0",
* max file size is 1G. The buffer manager is the one just created, and the files are in read/write mode
* If the last argument is true, then the file is read only:<p>
* Store myStore = new ScatteringFileStore("", "test", "0", 1024*1024*1024, myBM, false);<p>
*
* // create a MckoiObjectIOManager with the store instance just created<p>
* ObjectIOManager myManager = new MckoiObjectIOManager( mystore); <br>
* if (myManger.open() == false)<br>
* System.out.println("Error opening ObjectIOManager: myManager!");<br>
* </code>
*
* @author Rui Mao
* @version 2005.11.01
*/
public class MckoiObjectIOManager implements ObjectIOManager {
private final Store store;
private HashMap<Long, Object> hm;
/**
* Constructor.
*
* @param store
* the mckoi store to work on
*/
public MckoiObjectIOManager(Store store) {
if (store == null)
throw new IllegalArgumentException("Store is null!");
this.store = store;
hm = new HashMap<Long, Object>();
// this.total = 0;
// this.writeCounter = 0;
}
/**
* A new constructor, construct from file name and page size.
*
* @param fileHeader
* header of back bone file names, may include the path
* @param fileExt
* extension name of back bone files, normally use "000"
* @param fileSize
* max size of a back bone file, if a file is not enough, more
* files will be used
* @param bufferType
* type of buffer, should be one of "Java NIO" or "Java IO"
* @param cacheSize
* max number of pages in the buffer, could be 2 if only iterate
* the data once.
* @param pageSize
* size of a page, in number of bytes. normally use "4096", could
* be larger for sequential read / write, for example: 4M
* (4*1024*1024)
* @param readOnly
* if true, the back bone files are read only
*/
public MckoiObjectIOManager(String fileHeader, String fileExt,
long fileSize, String bufferType, int cacheSize, int pageSize,
boolean readOnly) {
final BufferManager BM = new BufferManager(bufferType, cacheSize,
pageSize);
final File path = new File(fileHeader);
String pathName = path.getParent();
if (pathName == null)
pathName = ".";
final ScatteringFileStore store = new ScatteringFileStore(new File(
pathName), path.getName(), fileExt, fileSize, BM, readOnly);
this.store = store;
hm = new HashMap<Long, Object>();
// this.total = 0;
// this.writeCounter = 0;
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#open()
*/
public boolean open() {
boolean b = true;
try {
b = !((AbstractStore) store).open();
} catch (Exception e) {
e.printStackTrace();
}
return b;
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#flush()
*/
public void flush() throws java.io.IOException {
store.flush();
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#close()
*/
public void close() {
// no such method in Mckoi.Store, so just return
try {
((AbstractStore) store).close();
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#readObject(long)
*/
public Object readObject(final long pointer) throws java.io.IOException,
java.lang.ClassNotFoundException, InstantiationException,
IllegalAccessException {
Object object = hm.get(pointer);
if (object == null)
return readUnhashedObject(pointer);
return object;
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#readPersistObject(long)
*/
public Object readPersistObject(final long pointer) throws IOException,
ClassNotFoundException, InstantiationException,
IllegalAccessException {
Object object = readUnhashedObject(pointer);
// TODO: only put object if not already there.
if (!hm.containsKey(pointer))
hm.put(pointer, object);
return object;
}
private Object readUnhashedObject(final long pointer)
throws java.io.IOException, java.lang.ClassNotFoundException,
InstantiationException, IllegalAccessException {
// step1. read the serializaed format (byte stream) out of the store
Area area = store.getArea(pointer);
final int s = area.capacity();
byte[] buffer = new byte[s];
area.position(0);
area.get(buffer, 0, s);
// step2, deserialized the object from the byte stream
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
buffer));
Object o = ois.readObject();
ois.close();
return o;
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#writeObject(java.lang.Object, long)
*/
public Object writeObject(Object object, final long pointer)
throws java.io.IOException, java.lang.ClassNotFoundException {
// step1. read the original serializaed format (byte stream) out of the
// store
Area area = store.getArea(pointer);
final int s = area.capacity();
byte[] buffer = new byte[s];
area.position(0);
area.get(buffer, 0, s);
// step2, deserialized the original object from the byte stream
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
buffer));
Object o = ois.readObject();
// step3. generate the serialized form of the new object
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ObjectOutputStream oOut = new ObjectOutputStream(bOut);
oOut.writeObject(object);
// step 4. write the new object into Area
area.position(0);
byte[] newBuffer = bOut.toByteArray();
// for debug
System.out.println("original size=" + s + ", new size="
+ newBuffer.length);
area.put(newBuffer, 0, newBuffer.length);
// release memory for garbage collector
area = null;
buffer = null;
newBuffer = null;
ois.close();
oOut.close();
ois = null;
bOut = null;
oOut = null;
return o;
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#writeObject(java.lang.Object)
*/
public long writeObject(Object object) throws java.io.IOException {
// step1. generate the serialized format, get the size
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ObjectOutputStream oOut = new ObjectOutputStream(bOut);
oOut.writeObject(object);
oOut.close();
byte[] buffer = bOut.toByteArray();
final int s = buffer.length;
// statistics
// writeCounter ++;
// total += s;
// step2. allocate an Area in Store, write it.
final long p = store.alloc(s);
Area area = store.getArea(p);
area.position(0);
area.put(buffer, 0, s);
// release memory for garbage collector
bOut = null;
oOut = null;
buffer = null;
area = null;
return p;
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#removeObject(long)
*/
public Object removeObject(final long pointer) throws java.io.IOException,
java.lang.ClassNotFoundException, InstantiationException,
IllegalAccessException {
Object o = readObject(pointer);
store.free(pointer);
return o;
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#size()
*/
public long size() throws java.io.IOException {
return store.getAllAreas().size();
}
/*
* (non-Javadoc)
*
* @see GeDBIT.util.ObjectIOManager#iterator()
*/
@SuppressWarnings("rawtypes")
public Iterator iterator() {
return new MckoiObjectIOManagerIterator();
}
// ------------------- inner class MckoiObjectIOManagerIterator
// ---------------------------//
@SuppressWarnings("rawtypes")
class MckoiObjectIOManagerIterator implements Iterator {
Iterator p;
MckoiObjectIOManagerIterator() {
p = ((AbstractStore) store).iterator();
}
public boolean hasNext() {
return p.hasNext();
}
public Object next() throws java.util.NoSuchElementException {
Object result = null;
try {
Area area = (Area) p.next();
final int s = area.capacity();
byte[] buffer = new byte[s];
area.position(0);
area.get(buffer, 0, s);
// step2, deserialized the object from the byte stream
ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(buffer));
// release memory for garbage collector
area = null;
result = ois.readObject();
} catch (Exception e) {
System.out
.println("Exception in calling MckoiObjectIOManagerIterator.next():"
+ e.toString());
e.printStackTrace();
}
return result;
}
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException(
"remove() in StoreIterator is not supported yet!");
}
}
}