001: package com.jofti.store;
002:
003: import java.io.IOException;
004: import java.io.ObjectInputStream;
005: import java.io.ObjectOutputStream;
006: import java.nio.ByteBuffer;
007: import java.util.HashMap;
008: import java.util.Iterator;
009: import java.util.Map;
010:
011: import org.apache.commons.logging.Log;
012: import org.apache.commons.logging.LogFactory;
013:
014: import com.jofti.btree.IPage;
015: import com.jofti.btree.KeyValueObject;
016: import com.jofti.btree.LeafNodeEntry;
017: import com.jofti.btree.MaxLeafNodeEntry;
018: import com.jofti.btree.ValueObject;
019: import com.jofti.core.IStoreManager;
020: import com.jofti.exception.JoftiException;
021: import com.jofti.util.ByteBufferArrayInputStream;
022: import com.jofti.util.FastByteArrayOutputStream;
023:
024: /**
025: *
026: * Helper to aid in the serialization and deserialization of Stored Leaf Nodes.
027: * <p>
028: * @author xenephon (xenephon@jofti.com)
029: *
030: */
031: public class ExternalisableHelper {
032:
033: private static final byte STRING = 0;
034:
035: private static final byte LONG = 1;
036:
037: private static final byte INTEGER = 2;
038:
039: private static final byte BOOLEAN = 3;
040:
041: private static final byte OTHER = 4;
042:
043: private static final byte MAX_ENTRY = 0;
044:
045: private static final byte LEAF_ENTRY = 1;
046:
047: private static final byte NULL_ENTRY = 2;
048:
049: private static final byte KEY_VALUE = 1;
050:
051: private static final byte VALUE_OBJECT = 2;
052:
053: private static Log log = LogFactory
054: .getLog(ExternalisableHelper.class);
055:
056: FastByteArrayOutputStream nodeFbos = null;
057:
058: ByteBufferArrayInputStream bybuf = null;
059:
060: ObjectOutputStream nodeOut = null;
061:
062: AbstractStoreManager manager = null;
063:
064: int nodeSize = 0;
065:
066: public void init(int nodeSize, int blockSize, IStoreManager manager)
067: throws JoftiException {
068: bybuf = new ByteBufferArrayInputStream(null);
069: nodeFbos = new FastByteArrayOutputStream(blockSize);
070: try {
071: nodeOut = new ObjectOutputStream(nodeFbos);
072:
073: nodeOut.useProtocolVersion(1);
074: } catch (Exception e) {
075: throw new JoftiException(e);
076: }
077: this .nodeSize = nodeSize;
078: this .manager = (AbstractStoreManager) manager;
079: }
080:
081: public LeafNodeEntry convertFromStore(ByteBuffer buf)
082: throws JoftiException {
083:
084: ObjectInputStream in = null;
085: LeafNodeEntry entry = null;
086:
087: try {
088:
089: bybuf.resetData(buf);
090: // unfortunately we have to create a new stream here
091: in = new ObjectInputStream(bybuf);
092:
093: // get the type of entry
094: byte type = in.readByte();
095:
096: if (type == NULL_ENTRY) {
097: //we can leave here
098: return null;
099: } else if (type == MAX_ENTRY) {
100:
101: entry = new MaxLeafNodeEntry();
102: } else {
103: if (entry == null) {
104: entry = new LeafNodeEntry();
105: }
106: }
107:
108: // get the value object
109: Object obj = readValue(in);
110:
111: // get value object type
112: type = in.readByte();
113:
114: //get the dimension
115: int dimension = in.readInt();
116:
117: if (type == KEY_VALUE) {
118:
119: // get the number of entries in hashmap
120: // we assume that there are no more than 256 dimensions for a
121: // key value
122: byte j = in.readByte();
123:
124: Map map = new HashMap(j);
125: for (int k = 0; k < j; k++) {
126: map.put(readValue(in), readValue(in));
127: }
128: entry.value = new KeyValueObject(dimension,
129: (Comparable) obj, map);
130:
131: } else {
132:
133: entry.value = new ValueObject(dimension,
134: (Comparable) obj);
135:
136: }
137: // get number of entries in set - mostly will be few
138: int l = in.readInt();
139:
140: for (int k = 0; k < l; k++) {
141: entry.addUniqueId(readValue(in));
142: }
143:
144: } catch (Exception e) {
145: throw new JoftiException("Unable to read entry " + buf, e);
146: } finally {
147: // make sure we close the input stream
148: if (in != null) {
149: try {
150: in.close();
151: } catch (Exception e) {
152: // put a warning here
153: }
154: in = null;
155: }
156: }
157:
158: return entry;
159:
160: }
161:
162: public byte[] convertForStore(LeafNodeEntry entry)
163: throws JoftiException {
164: byte[] res = null;
165: try {
166: if (entry instanceof MaxLeafNodeEntry) {
167: // maxLeafNodeEntry
168: nodeOut.writeByte(MAX_ENTRY);
169:
170: } else {
171: //LeafNodeEntry
172: nodeOut.writeByte(LEAF_ENTRY);
173: }
174: // write out the value
175:
176: ValueObject valObj = (ValueObject) entry.value;
177:
178: writeValue(nodeOut, valObj.getRealValue());
179:
180: //write out the key value
181: if (entry.value instanceof KeyValueObject) {
182: //type for ke yvalue object
183: nodeOut.writeByte(KEY_VALUE);
184:
185: //write the dimension
186: nodeOut.writeInt(((ValueObject) entry.value)
187: .getDimension());
188:
189: // write out the attributes
190: Map map = ((KeyValueObject) valObj).getAttributes();
191:
192: // write out the size of the key value attributes
193: nodeOut.writeByte((byte) map.size());
194: Iterator it = map.entrySet().iterator();
195: for (int i = 0; i < map.size(); i++) {
196: Map.Entry mapEntry = (Map.Entry) it.next();
197: writeValue(nodeOut, mapEntry.getKey());
198: writeValue(nodeOut, mapEntry.getValue());
199:
200: }
201:
202: } else {
203: //write out type of value object
204: nodeOut.writeByte(VALUE_OBJECT);
205:
206: //write out dimension
207: nodeOut.writeInt(((ValueObject) entry.value)
208: .getDimension());
209: }
210:
211: int size = entry.getUniqueIdSize();
212:
213: nodeOut.writeInt(size);
214: if (size > 0) {
215: Iterator it = entry.uniqueIdSet.iterator();
216: for (int i = 0; i < size; i++) {
217: writeValue(nodeOut, it.next());
218: }
219: }
220:
221: nodeOut.flush();
222: size = nodeFbos.getSize();
223: byte[] tempData = nodeFbos.getByteArray();
224: res = new byte[size];
225:
226: System.arraycopy(tempData, 0, res, 0, size);
227:
228: nodeOut.reset();
229: nodeFbos.reset();
230:
231: } catch (Throwable t) {
232: throw new JoftiException("Unable to serialize entry "
233: + entry, t);
234: }
235: return res;
236:
237: }
238:
239: IPage readExternalBuffer(java.nio.ByteBuffer buffer,
240: int numberEntries) throws JoftiException {
241:
242: IPage page = manager.doGetNewPage(buffer.limit());
243:
244: ByteBuffer pBuf = page.getBuffer();
245:
246: int[] pointers = page.getPointers();
247:
248: try {
249: pBuf.clear();
250: pBuf.put(buffer);
251: pBuf.flip();
252: pBuf.mark();
253:
254: // lets loop through the entries
255: for (int i = 0; i < numberEntries; i++) {
256:
257: // first get the size of each entry
258: pointers[i] = pBuf.position();
259: int size = pBuf.getInt();
260: // set the position of the entry
261: pBuf.position(pBuf.position() + size);
262:
263: }
264: pBuf.reset();
265:
266: } catch (Throwable e) {
267: log.fatal("expected to read " + numberEntries + " pos at "
268: + buffer);
269: throw new JoftiException("unable to read block ", e);
270: }
271: return page;
272:
273: }
274:
275: private void writeValue(ObjectOutputStream out, Object obj)
276: throws IOException {
277: Class clazz = obj.getClass();
278: if (clazz == String.class) {
279: out.writeByte(STRING);
280:
281: out.writeUTF((String) obj);
282:
283: } else if (clazz == Integer.class) {
284: out.writeByte(INTEGER);
285: out.writeInt(((Integer) obj).intValue());
286:
287: } else if (clazz == Long.class) {
288: out.writeByte(LONG);
289: out.writeLong(((Long) obj).longValue());
290: } else {
291: out.writeByte(OTHER);
292: out.writeObject(obj);
293: }
294: }
295:
296: private Object readValue(ObjectInputStream in) throws IOException,
297: ClassNotFoundException {
298: int valType = in.readByte();
299: switch (valType) {
300: case STRING:
301: return in.readUTF();
302: case INTEGER:
303: return new Integer(in.readInt());
304:
305: case LONG:
306: return new Long(in.readLong());
307: default:
308: return in.readObject();
309: }
310:
311: }
312: }
|