001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2000,2008 Oracle. All rights reserved.
005: *
006: * $Id: SerialBinding.java,v 1.28.2.4 2008/01/07 15:14:05 cwl Exp $
007: */
008:
009: package com.sleepycat.bind.serial;
010:
011: import java.io.IOException;
012:
013: import com.sleepycat.bind.EntryBinding;
014: import com.sleepycat.je.DatabaseEntry;
015: import com.sleepycat.util.FastInputStream;
016: import com.sleepycat.util.FastOutputStream;
017: import com.sleepycat.util.RuntimeExceptionWrapper;
018:
019: /**
020: * A concrete <code>EntryBinding</code> that treats a key or data entry as
021: * a serialized object.
022: *
023: * <p>This binding stores objects in serialized object format. The
024: * deserialized objects are returned by the binding, and their
025: * <code>Class</code> must implement the <code>Serializable</code>
026: * interface.</p>
027: *
028: * <p>For key bindings, a tuple binding is usually a better choice than a
029: * serial binding. A tuple binding gives a reasonable sort order, and works
030: * with comparators in all cases -- see below.</p>
031: *
032: * <p><em>WARNING:</em> SerialBinding should not be used with Berkeley DB Java
033: * Edition for key bindings, when a custom comparator is used. In JE,
034: * comparators are instantiated and called internally at times when databases
035: * are not accessible. Because serial bindings depend on the class catalog
036: * database, a serial binding cannot be used during these times. An attempt
037: * to use a serial binding with a custom comparator will result in a
038: * NullPointerException during environment open or close.</p>
039: *
040: * @author Mark Hayes
041: */
042: public class SerialBinding extends SerialBase implements EntryBinding {
043:
044: private ClassCatalog classCatalog;
045: private Class baseClass;
046:
047: /**
048: * Creates a serial binding.
049: *
050: * @param classCatalog is the catalog to hold shared class information and
051: * for a database should be a {@link StoredClassCatalog}.
052: *
053: * @param baseClass is the base class for serialized objects stored using
054: * this binding -- all objects using this binding must be an instance of
055: * this class.
056: */
057: public SerialBinding(ClassCatalog classCatalog, Class baseClass) {
058:
059: if (classCatalog == null) {
060: throw new NullPointerException(
061: "classCatalog must be non-null");
062: }
063: this .classCatalog = classCatalog;
064: this .baseClass = baseClass;
065: }
066:
067: /**
068: * Returns the base class for this binding.
069: *
070: * @return the base class for this binding.
071: */
072: public final Class getBaseClass() {
073:
074: return baseClass;
075: }
076:
077: /**
078: * Returns the class loader to be used during deserialization, or null if
079: * a default class loader should be used. The default implementation of
080: * this method returns
081: * <code>Thread.currentThread().getContextClassLoader()</code> to use the
082: * context class loader for the current thread.
083: *
084: * <p>This method may be overriden to return a dynamically determined class
085: * loader. For example, <code>getBaseClass().getClassLoader()</code> could
086: * be called to use the class loader for the base class, assuming that a
087: * base class has been specified.</p>
088: *
089: * <p>If this method returns null, a default class loader will be used as
090: * determined by the <code>java.io.ObjectInputStream.resolveClass</code>
091: * method.</p>
092: */
093: public ClassLoader getClassLoader() {
094:
095: return Thread.currentThread().getContextClassLoader();
096: }
097:
098: /**
099: * Deserialize an object from an entry buffer. May only be called for data
100: * that was serialized using {@link #objectToEntry}, since the fixed
101: * serialization header is assumed to not be included in the input data.
102: * {@link SerialInput} is used to deserialize the object.
103: *
104: * @param entry is the input serialized entry.
105: *
106: * @return the output deserialized object.
107: */
108: public Object entryToObject(DatabaseEntry entry) {
109:
110: int length = entry.getSize();
111: byte[] hdr = SerialOutput.getStreamHeader();
112: byte[] bufWithHeader = new byte[length + hdr.length];
113:
114: System.arraycopy(hdr, 0, bufWithHeader, 0, hdr.length);
115: System.arraycopy(entry.getData(), entry.getOffset(),
116: bufWithHeader, hdr.length, length);
117:
118: try {
119: SerialInput jin = new SerialInput(new FastInputStream(
120: bufWithHeader, 0, bufWithHeader.length),
121: classCatalog, getClassLoader());
122: return jin.readObject();
123: } catch (IOException e) {
124: throw new RuntimeExceptionWrapper(e);
125: } catch (ClassNotFoundException e) {
126: throw new RuntimeExceptionWrapper(e);
127: }
128: }
129:
130: /**
131: * Serialize an object into an entry buffer. The fixed serialization
132: * header is not included in the output data to save space, and therefore
133: * to deserialize the data the complementary {@link #entryToObject} method
134: * must be used. {@link SerialOutput} is used to serialize the object.
135: *
136: * <p>Note that this method sets the DatabaseEntry offset property to a
137: * non-zero value and the size property to a value less than the length of
138: * the byte array.</p>
139: *
140: * @param object is the input deserialized object.
141: *
142: * @param entry is the output serialized entry.
143: *
144: * @throws IllegalArgumentException if the object is not an instance of the
145: * base class for this binding.
146: */
147: public void objectToEntry(Object object, DatabaseEntry entry) {
148:
149: if (baseClass != null && !baseClass.isInstance(object)) {
150: throw new IllegalArgumentException("Data object class ("
151: + object.getClass()
152: + ") not an instance of binding's base class ("
153: + baseClass + ')');
154: }
155: FastOutputStream fo = getSerialOutput(object);
156: try {
157: SerialOutput jos = new SerialOutput(fo, classCatalog);
158: jos.writeObject(object);
159: } catch (IOException e) {
160: throw new RuntimeExceptionWrapper(e);
161: }
162:
163: byte[] hdr = SerialOutput.getStreamHeader();
164: entry.setData(fo.getBufferBytes(), hdr.length, fo
165: .getBufferLength()
166: - hdr.length);
167: }
168: }
|