StoredClassCatalog.java [plain text]
package com.sleepycat.bdb;
import com.sleepycat.bdb.bind.serial.ClassCatalog;
import com.sleepycat.bdb.util.IOExceptionWrapper;
import com.sleepycat.bdb.util.UtfOps;
import com.sleepycat.db.Db;
import com.sleepycat.db.Dbc;
import com.sleepycat.db.DbEnv;
import com.sleepycat.db.DbException;
import com.sleepycat.db.DbTxn;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.HashMap;
public class StoredClassCatalog implements ClassCatalog {
private static final byte REC_LAST_CLASS_ID = (byte) 0;
private static final byte REC_CLASS_FORMAT = (byte) 1;
private static final byte REC_CLASS_INFO = (byte) 2;
private static final byte[] LAST_CLASS_ID_KEY = {REC_LAST_CLASS_ID};
private DataEnvironment env;
private DataDb db;
private HashMap classMap;
private HashMap formatMap;
public StoredClassCatalog(DbEnv env, String file,
String database, int openFlags)
throws FileNotFoundException, DbException {
this.env = DataEnvironment.getEnvironment(env);
DbTxn txn = this.env.getTxn();
if (txn != null)
openFlags &= ~Db.DB_AUTO_COMMIT;
Db db = new Db(env, 0);
db.open(txn, file, database, Db.DB_BTREE, openFlags, 0);
this.db = new DataDb(db);
classMap = new HashMap();
formatMap = new HashMap();
}
public synchronized void close()
throws IOException {
try {
if (db != null) {
db.close();
db = null;
}
} catch (DbException e) {
throw new IOExceptionWrapper(e);
}
db = null;
formatMap = null;
classMap = null;
}
public synchronized byte[] getClassID(String className)
throws IOException, ClassNotFoundException {
try {
ClassInfo classInfo = getClassInfo(className);
return classInfo.getClassID();
} catch (DbException e) {
throw new IOExceptionWrapper(e);
}
}
public synchronized ObjectStreamClass getClassFormat(String className)
throws IOException, ClassNotFoundException {
try {
ClassInfo classInfo = getClassInfo(className);
return classInfo.getClassFormat();
} catch (DbException e) {
throw new IOExceptionWrapper(e);
}
}
public ObjectStreamClass getClassFormat(byte[] classID)
throws IOException, ClassNotFoundException {
try {
return getClassFormat(classID, newDbt());
} catch (DbException e) {
throw new IOExceptionWrapper(e);
}
}
private synchronized ObjectStreamClass getClassFormat(byte[] classID,
DataThang data)
throws DbException, ClassNotFoundException, IOException {
BigInteger classIDObj = new BigInteger(classID);
ObjectStreamClass classFormat =
(ObjectStreamClass) formatMap.get(classIDObj);
if (classFormat == null) {
byte[] keyBytes = new byte[classID.length + 1];
keyBytes[0] = REC_CLASS_FORMAT;
System.arraycopy(classID, 0, keyBytes, 1, classID.length);
DataThang key = newDbt();
key.setBytes(keyBytes);
int err = db.get(key, data, 0);
if (err != 0) {
throw new ClassNotFoundException("Catalog class ID not found");
}
ObjectInputStream ois =
new ObjectInputStream(data.getByteStream());
classFormat = (ObjectStreamClass) ois.readObject();
formatMap.put(classIDObj, classFormat);
}
return classFormat;
}
private ClassInfo getClassInfo(String className)
throws IOException, ClassNotFoundException, DbException, DbException {
ClassInfo classInfo = (ClassInfo) classMap.get(className);
if (classInfo != null) {
return classInfo;
} else {
Class cls = Class.forName(className);
ObjectStreamClass classFormat = ObjectStreamClass.lookup(cls);
char[] nameChars = className.toCharArray();
byte[] keyBytes = new byte[1 + UtfOps.getByteLength(nameChars)];
keyBytes[0] = REC_CLASS_INFO;
UtfOps.charsToBytes(nameChars, 0, keyBytes, 1, nameChars.length);
DataThang key = newDbt();
key.setBytes(keyBytes);
DataThang data = newDbt();
int err = db.get(key, data, 0);
if (err != 0) {
classInfo = putClassInfo(new ClassInfo(), className, key,
classFormat);
} else {
classInfo = new ClassInfo(data);
DataThang formatData = newDbt();
ObjectStreamClass storedClassFormat =
getClassFormat(classInfo.getClassID(), formatData);
if (!areClassFormatsEqual(storedClassFormat,
formatData.getBytes(),
classFormat)) {
classInfo = putClassInfo(classInfo, className, key,
classFormat);
}
classInfo.setClassFormat(classFormat);
classMap.put(className, classInfo);
}
}
return classInfo;
}
private ClassInfo putClassInfo(ClassInfo classInfo, String className,
DataThang classKey,
ObjectStreamClass classFormat)
throws DbException, ClassNotFoundException {
Dbc cursor = db.openCursor(true);
try {
DataThang key = newDbt();
key.setBytes(LAST_CLASS_ID_KEY);
DataThang data = newDbt();
int putFlag = Db.DB_CURRENT;
int err = cursor.get(key, data,
Db.DB_SET | env.getWriteLockFlag());
if (err != 0) {
data.setBytes(new byte[1]); putFlag = Db.DB_KEYLAST;
}
byte[] idBytes = data.getBytes();
Object anotherClassInfo = classMap.get(className);
if (anotherClassInfo != null)
return (ClassInfo) anotherClassInfo;
idBytes = incrementID(idBytes);
data.setBytes(idBytes);
cursor.put(key, data, putFlag);
byte[] keyBytes = new byte[1 + idBytes.length];
keyBytes[0] = REC_CLASS_FORMAT;
System.arraycopy(idBytes, 0, keyBytes, 1, idBytes.length);
key.setBytes(keyBytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos;
try {
oos = new ObjectOutputStream(baos);
oos.writeObject(classFormat);
}
catch (IOException e) {}
data.setBytes(baos.toByteArray());
db.put(key, data, 0);
classInfo.setClassID(idBytes);
classInfo.toDbt(data);
db.put(classKey, data, 0);
classInfo.setClassFormat(classFormat);
classMap.put(className, classInfo);
formatMap.put(new BigInteger(idBytes), classFormat);
return classInfo;
} finally {
db.closeCursor(cursor);
}
}
private static byte[] incrementID(byte[] key) {
BigInteger id = new BigInteger(key);
id = id.add(BigInteger.valueOf(1));
return id.toByteArray();
}
private static class ClassInfo implements Serializable {
private byte[] classID;
private transient ObjectStreamClass classFormat;
ClassInfo() {
}
ClassInfo(DataThang dbt) {
byte[] data = dbt.getDataBytes();
int len = data[0];
classID = new byte[len];
System.arraycopy(data, 1, classID, 0, len);
}
void toDbt(DataThang dbt) {
byte[] data = new byte[1 + classID.length];
data[0] = (byte) classID.length;
System.arraycopy(classID, 0, data, 1, classID.length);
dbt.setData(data, 0, data.length);
}
void setClassID(byte[] classID) {
this.classID = classID;
}
byte[] getClassID() {
return classID;
}
ObjectStreamClass getClassFormat() {
return classFormat;
}
void setClassFormat(ObjectStreamClass classFormat) {
this.classFormat = classFormat;
}
}
private static boolean areClassFormatsEqual(ObjectStreamClass format1,
byte[] format1Bytes,
ObjectStreamClass format2) {
try {
if (format1Bytes == null) { format1Bytes = getObjectBytes(format1);
}
byte[] format2Bytes = getObjectBytes(format2);
return java.util.Arrays.equals(format2Bytes, format1Bytes);
} catch (IOException e) { return false; }
}
private static byte[] getObjectBytes(Object o)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
return baos.toByteArray();
}
private DataThang newDbt() {
return new DataThang();
}
}