package com.sleepycat.bind.serial;
import java.io.IOException;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.util.FastInputStream;
import com.sleepycat.util.FastOutputStream;
import com.sleepycat.util.RuntimeExceptionWrapper;
import de.ovgu.cide.jakutil.*;
/**
* A concrete <code>EntryBinding</code> that treats a key or data entry as a
* serialized object.
* <p>
* This binding stores objects in serialized object format. The deserialized
* objects are returned by the binding, and their <code>Class</code> must
* implement the <code>Serializable</code> interface.
* </p>
* <p>
* For key bindings, a tuple binding is usually a better choice than a serial
* binding. A tuple binding gives a reasonable sort order, and works with
* comparators in all cases -- see below.
* </p>
* <p>
* <em>WARNING:</em> SerialBinding should not be used with Berkeley DB Java
* Edition for key bindings, when a custom comparator is used. In JE,
* comparators are instantiated and called internally at times when databases
* are not accessible. Because serial bindings depend on the class catalog
* database, a serial binding cannot be used during these times. An attempt to
* use a serial binding with a custom comparator will result in a
* NullPointerException during environment open or close.
* </p>
* @author Mark Hayes
*/
public class SerialBinding extends SerialBase implements EntryBinding {
private ClassCatalog classCatalog;
private Class baseClass;
/**
* Creates a serial binding.
* @param classCatalogis the catalog to hold shared class information and for a
* database should be a {@link StoredClassCatalog}.
* @param baseClassis the base class for serialized objects stored using this
* binding -- all objects using this binding must be an instance
* of this class.
*/
public SerialBinding( ClassCatalog classCatalog, Class baseClass){
if (classCatalog == null) {
throw new NullPointerException("classCatalog must be non-null");
}
this.classCatalog=classCatalog;
this.baseClass=baseClass;
}
/**
* Returns the base class for this binding.
* @return the base class for this binding.
*/
public final Class getBaseClass(){
return baseClass;
}
/**
* Returns the class loader to be used during deserialization, or null if a
* default class loader should be used. The default implementation of this
* method returns null.
* <p>
* This method may be overriden to return a dynamically determined class
* loader. For example,
* <code>Thread.currentThread().getContextClassLoader()</code> could be
* called to use the context class loader for the curren thread. Or
* <code>getBaseClass().getClassLoader()</code> could be called to use the
* class loader for the base class, assuming that a base class has been
* specified.
* </p>
* <p>
* If this method returns null, a default class loader will be used as
* determined by the <code>java.io.ObjectInputStream.resolveClass</code>
* method.
* </p>
*/
public ClassLoader getClassLoader(){
return null;
}
/**
* Deserialize an object from an entry buffer. May only be called for data
* that was serialized using {@link #objectToEntry}, since the fixed
* serialization header is assumed to not be included in the input data.{@link SerialInput} is used to deserialize the object.
* @param entryis the input serialized entry.
* @return the output deserialized object.
*/
public Object entryToObject( DatabaseEntry entry){
int length=entry.getSize();
byte[] hdr=SerialOutput.getStreamHeader();
byte[] bufWithHeader=new byte[length + hdr.length];
System.arraycopy(hdr,0,bufWithHeader,0,hdr.length);
System.arraycopy(entry.getData(),entry.getOffset(),bufWithHeader,hdr.length,length);
try {
SerialInput jin=new SerialInput(new FastInputStream(bufWithHeader,0,bufWithHeader.length),classCatalog,getClassLoader());
return jin.readObject();
}
catch ( IOException e) {
throw new RuntimeExceptionWrapper(e);
}
catch ( ClassNotFoundException e) {
throw new RuntimeExceptionWrapper(e);
}
}
/**
* Serialize an object into an entry buffer. The fixed serialization header
* is not included in the output data to save space, and therefore to
* deserialize the data the complementary {@link #entryToObject} method must
* be used. {@link SerialOutput} is used to serialize the object.
* <p>
* Note that this method sets the DatabaseEntry offset property to a
* non-zero value and the size property to a value less than the length of
* the byte array.
* </p>
* @param objectis the input deserialized object.
* @param entryis the output serialized entry.
* @throws IllegalArgumentExceptionif the object is not an instance of the base class for this
* binding.
*/
public void objectToEntry( Object object, DatabaseEntry entry){
if (baseClass != null && !baseClass.isInstance(object)) {
throw new IllegalArgumentException("Data object class (" + object.getClass() + ") not an instance of binding's base class ("+ baseClass+ ')');
}
FastOutputStream fo=getSerialOutput(object);
try {
SerialOutput jos=new SerialOutput(fo,classCatalog);
jos.writeObject(object);
}
catch ( IOException e) {
throw new RuntimeExceptionWrapper(e);
}
byte[] hdr=SerialOutput.getStreamHeader();
entry.setData(fo.getBufferBytes(),hdr.length,fo.getBufferLength() - hdr.length);
}
}