001: // $Id: DataConverter.java 1546 2007-07-23 06:07:56Z grro $
002:
003: /*
004: * Copyright (c) xcache.org, 2007. All rights reserved.
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
021: * The latest copy of this software may be found on http://www.xcache.org/
022: */
023: package org.xcache;
024:
025: import java.io.ByteArrayInputStream;
026: import java.io.ByteArrayOutputStream;
027: import java.io.IOException;
028: import java.io.ObjectInputStream;
029: import java.io.ObjectOutputStream;
030: import java.io.Serializable;
031: import java.nio.ByteBuffer;
032: import java.util.logging.Level;
033: import java.util.logging.Logger;
034: import java.util.zip.DataFormatException;
035: import java.util.zip.Deflater;
036: import java.util.zip.Inflater;
037:
038: import org.xsocket.IDataSink;
039: import org.xsocket.IDataSource;
040:
041: /**
042: *
043: *
044: * @author grro@xcache.org
045: */
046: final class Item {
047:
048: private static final Logger LOG = Logger.getLogger(Item.class
049: .getName());
050:
051: private static final int PACK_THRESHOLD = 16;
052:
053: private static final byte PLAIN = 90;
054: private static final byte PACKED = 91;
055:
056: private static final String ENCODING = "UTF-8";
057:
058: private static final byte TYPE_NULL = 0;
059: private static final byte TYPE_STRING = 1;
060: private static final byte TYPE_BYTEARRAY = 6;
061: private static final byte TYPE_SERIALIZED_JAVA_OBJECT = 11;
062:
063: private boolean isResolved = true;
064: private Integer valueLength = null;
065:
066: private Object value = null;
067: private byte type = TYPE_STRING;
068:
069: Item(Object value) {
070: if (value == null) {
071: setValueWithNull();
072: return;
073: }
074:
075: if (value instanceof String) {
076: setValue((String) value);
077:
078: }
079: if (value instanceof byte[]) {
080: setValue((byte[]) value);
081:
082: } else {
083: setValue((Serializable) value);
084: }
085: }
086:
087: private Item(Object value, byte type) {
088: this .value = value;
089: this .type = type;
090: }
091:
092: private Item(ByteBuffer[] value, byte type, Integer valueLength) {
093: isResolved = false;
094: this .valueLength = valueLength;
095:
096: this .value = value;
097: this .type = type;
098: }
099:
100: private void setValueWithNull() {
101: this .type = TYPE_NULL;
102: }
103:
104: private void setValue(String value) {
105: this .value = value;
106: this .type = TYPE_STRING;
107: }
108:
109: private void setValue(byte[] value) {
110: this .value = value;
111: this .type = TYPE_BYTEARRAY;
112: }
113:
114: private void setValue(Serializable value) {
115: this .value = value;
116: this .type = TYPE_SERIALIZED_JAVA_OBJECT;
117: }
118:
119: Object getValue() {
120: return value;
121: }
122:
123: int writeTo(IDataSink dataSink) throws IOException {
124: int written = 0;
125:
126: written += dataSink.write(type);
127:
128: // value is resolved
129: if (isResolved) {
130: switch (type) {
131:
132: case TYPE_NULL:
133: break;
134:
135: case TYPE_STRING:
136: byte[] serializedString = ((String) value)
137: .getBytes(ENCODING);
138: written += writeItem(dataSink, serializedString);
139: break;
140:
141: case TYPE_SERIALIZED_JAVA_OBJECT:
142: byte[] serializedData = Serializer
143: .serialize((Serializable) value);
144: written += writeItem(dataSink, serializedData);
145: break;
146:
147: default:
148: throw new IOException("unknown data type " + type);
149: }
150:
151: // unresolved 8value contains packed field & data)
152: } else {
153: ByteBuffer[] bufs = (ByteBuffer[]) value;
154: ByteBuffer[] copy = new ByteBuffer[bufs.length];
155: for (int i = 0; i < bufs.length; i++) {
156: copy[i] = bufs[i].duplicate();
157: }
158:
159: written += dataSink.write((int) valueLength);
160: written += dataSink.write((ByteBuffer[]) copy);
161: }
162:
163: return written;
164: }
165:
166: private int writeItem(IDataSink dataSink, byte[] data)
167: throws IOException {
168:
169: int written = 0;
170:
171: if (data.length > PACK_THRESHOLD) {
172: byte[] compressedData = compress(data);
173:
174: if (compressedData.length < data.length) {
175: if (LOG.isLoggable(Level.FINER)) {
176: LOG
177: .finer("item compressed (compressed size="
178: + (compressedData.length * 100 / data.length)
179: + "%)");
180: }
181:
182: written += dataSink.write(compressedData.length);
183: written += dataSink.write(PACKED);
184: written += dataSink.write(compressedData);
185:
186: return written;
187: }
188: }
189:
190: // plain
191: written += dataSink.write(data.length);
192: written += dataSink.write(PLAIN);
193: written += dataSink.write(data);
194:
195: return written;
196: }
197:
198: private static byte[] compress(byte[] plainData) {
199: Deflater compressor = new Deflater();
200: compressor.setLevel(Deflater.BEST_COMPRESSION);
201:
202: compressor.setInput(plainData);
203: compressor.finish();
204:
205: ByteArrayOutputStream bos = new ByteArrayOutputStream(
206: plainData.length);
207:
208: byte[] buf = new byte[1024];
209: while (!compressor.finished()) {
210: int count = compressor.deflate(buf);
211: bos.write(buf, 0, count);
212: }
213:
214: try {
215: bos.close();
216: } catch (IOException e) {
217: }
218:
219: return bos.toByteArray();
220: }
221:
222: private static byte[] decompress(byte[] compressedData) {
223: Inflater decompressor = new Inflater();
224: decompressor.setInput(compressedData);
225:
226: ByteArrayOutputStream bos = new ByteArrayOutputStream(
227: compressedData.length);
228:
229: // Decompress the data
230: byte[] buf = new byte[1024];
231: while (!decompressor.finished()) {
232: try {
233: int count = decompressor.inflate(buf);
234: bos.write(buf, 0, count);
235: } catch (DataFormatException e) {
236: }
237: }
238:
239: try {
240: bos.close();
241: } catch (IOException e) {
242: }
243:
244: byte[] data = bos.toByteArray();
245: if (LOG.isLoggable(Level.FINER)) {
246: LOG.finer("item decompressed (compressed size="
247: + (compressedData.length * 100 / data.length)
248: + "%)");
249: }
250:
251: return data;
252: }
253:
254: public static Item readFrom(IDataSource dataSource)
255: throws IOException {
256:
257: Object value = null;
258: byte type = dataSource.readByte();
259:
260: int length = dataSource.readInt();
261: boolean isPacked = (dataSource.readByte() == PACKED);
262:
263: switch (type) {
264:
265: case TYPE_NULL:
266: break;
267:
268: case TYPE_STRING:
269: if (isPacked) {
270: byte[] data = decompress(dataSource
271: .readBytesByLength(length));
272: value = new String(data, ENCODING);
273: } else {
274: value = dataSource.readStringByLength(length, ENCODING);
275: }
276: break;
277:
278: case TYPE_SERIALIZED_JAVA_OBJECT:
279: byte[] data = null;
280: if (isPacked) {
281: data = decompress(dataSource.readBytesByLength(length));
282: } else {
283: data = dataSource.readBytesByLength(length);
284: }
285: value = Serializer.deserialize(data);
286: break;
287:
288: default:
289: throw new IOException("unknown data type " + type);
290: }
291:
292: return new Item(value, type);
293: }
294:
295: public static Item readUnresolvedFrom(IDataSource dataSource)
296: throws IOException {
297:
298: byte type = dataSource.readByte();
299:
300: int length = dataSource.readInt();
301: ByteBuffer[] buf = dataSource
302: .readByteBufferByLength(length + 1);
303:
304: Item item = new Item(buf, type, length);
305: return item;
306: }
307:
308: @Override
309: public int hashCode() {
310: return value.hashCode();
311: }
312:
313: @Override
314: public boolean equals(Object other) {
315:
316: if (!(other instanceof Item)) {
317: return false;
318: }
319:
320: Item otherItem = (Item) other;
321: return ((otherItem.type == this .type) && (otherItem.value
322: .equals(this .value)));
323: }
324:
325: @Override
326: public String toString() {
327: return "[" + type + "] " + value;
328: }
329:
330: private static final class Serializer {
331:
332: static byte[] serialize(Serializable obj) throws IOException {
333: ByteArrayOutputStream os = new ByteArrayOutputStream();
334: new ObjectOutputStream(os).writeObject(obj);
335:
336: return os.toByteArray();
337: }
338:
339: static Serializable deserialize(byte[] serialized)
340: throws IOException {
341: try {
342: ByteArrayInputStream is = new ByteArrayInputStream(
343: serialized);
344: ObjectInputStream ois = new ObjectInputStream(is);
345:
346: return (Serializable) ois.readObject();
347: } catch (ClassNotFoundException cnf) {
348: throw new IOException(cnf.toString());
349: }
350: }
351:
352: }
353: }
|