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); } }