All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.grizzly.memcached.BufferWrapper Maven / Gradle / Ivy

There is a newer version: 1.3.18
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.grizzly.memcached;

import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Cacheable;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.memory.Buffers;
import org.glassfish.grizzly.memory.MemoryManager;
import org.glassfish.grizzly.utils.BufferInputStream;
import org.glassfish.grizzly.utils.BufferOutputStream;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * Simple wrapper class for {@link Buffer}, which has original message {@code origin}, original object type {@link BufferType} and converted {@link Buffer}
 * 

* Messages which should be sent to the remote peer via network will be always converted into {@link Buffer} instance by {@link #wrap}, * and the received packet will be always restored to its original messages by {@link #unwrap} * * @author Bongjae Chang */ public class BufferWrapper implements Cacheable { private static final Logger logger = Grizzly.logger(BufferWrapper.class); private static final ThreadCache.CachedTypeIndex CACHE_IDX = ThreadCache.obtainIndex(BufferWrapper.class, 16); private static final String DEFAULT_CHARSET = "UTF-8"; // if the size of packets is more than 16K, packets will be compressed by GZIP public static final int DEFAULT_COMPRESSION_THRESHOLD = 16 * 1024; // 16K private Buffer buffer; private BufferType type; private T origin; public static enum BufferType { NONE(0), STRING(1), STRING_COMPRESSED(2), BYTE_ARRAY(3), BYTE_ARRAY_COMPRESSED(4), BYTE_BUFFER(5), BYTE_BUFFER_COMPRESSED(6), BYTE(7), BOOLEAN(8), SHORT(9), INTEGER(10), FLOAT(11), DOUBLE(12), LONG(13), DATE(14), OBJECT(15), OBJECT_COMPRESSED(16); public final int flags; private BufferType(final int typeFlags) { this.flags = typeFlags; } public static BufferType getBufferType(final int flags) { // use only 2 bytes in flags final short typeFlags = (short) (flags & 0xFFFF); switch (typeFlags) { case 0: return NONE; case 1: return STRING; case 2: return STRING_COMPRESSED; case 3: return BYTE_ARRAY; case 4: return BYTE_ARRAY_COMPRESSED; case 5: return BYTE_BUFFER; case 6: return BYTE_BUFFER_COMPRESSED; case 7: return BYTE; case 8: return BOOLEAN; case 9: return SHORT; case 10: return INTEGER; case 11: return FLOAT; case 12: return DOUBLE; case 13: return LONG; case 14: return DATE; case 15: return OBJECT; case 16: return OBJECT_COMPRESSED; default: throw new IllegalArgumentException("invalid type"); } } } private BufferWrapper(final T origin, final Buffer buffer, final BufferType type) { initialize(origin, buffer, type); } private void initialize(final T origin, final Buffer buffer, final BufferType type) { this.origin = origin; this.buffer = buffer; this.type = type; } /** * Return original object * * @return original object */ public T getOrigin() { return origin; } /** * Return buffer corresponding to original object * * @return the converted buffer */ public Buffer getBuffer() { return buffer; } /** * Return original object's type * * @return buffer type */ public BufferType getType() { return type; } @Override public void recycle() { origin = null; buffer = null; type = null; ThreadCache.putToCache(CACHE_IDX, this); } @SuppressWarnings("unchecked") private static BufferWrapper create(final T origin, final Buffer buffer, final BufferType type) { final BufferWrapper bufferWrapper = ThreadCache.takeFromCache(CACHE_IDX); if (bufferWrapper != null) { bufferWrapper.initialize(origin, buffer, type); return bufferWrapper; } return new BufferWrapper(origin, buffer, type); } /** * Return {@code BufferWrapper} instance from original object * * @param origin original object * @param memoryManager the memory manager for allocating {@link Buffer} * @return the buffer wrapper which included origin, buffer type and buffer corresponding to {@code origin} * @throws IllegalArgumentException if given parameters are not valid */ public static BufferWrapper wrap(final T origin, MemoryManager memoryManager) throws IllegalArgumentException { if (origin == null) { throw new IllegalArgumentException("object must not be null"); } if (origin instanceof Buffer) { return create(origin, (Buffer) origin, BufferWrapper.BufferType.NONE); } if (memoryManager == null) { memoryManager = MemoryManager.DEFAULT_MEMORY_MANAGER; } final Buffer buffer; final BufferWrapper bufferWrapper; if (origin instanceof String) { buffer = Buffers.wrap(memoryManager, (String) origin); if (buffer.remaining() > DEFAULT_COMPRESSION_THRESHOLD) { bufferWrapper = create(origin, compressBuffer(buffer, memoryManager), BufferType.STRING_COMPRESSED); } else { bufferWrapper = create(origin, buffer, BufferWrapper.BufferType.STRING); } } else if (origin instanceof byte[]) { final byte[] originBytes = (byte[]) origin; if (originBytes.length > DEFAULT_COMPRESSION_THRESHOLD) { buffer = Buffers.wrap(memoryManager, compress(originBytes)); bufferWrapper = create(origin, buffer, BufferType.BYTE_ARRAY_COMPRESSED); } else { buffer = Buffers.wrap(memoryManager, originBytes); bufferWrapper = create(origin, buffer, BufferType.BYTE_ARRAY); } } else if (origin instanceof ByteBuffer) { buffer = Buffers.wrap(memoryManager, (ByteBuffer) origin); if (buffer.remaining() > DEFAULT_COMPRESSION_THRESHOLD) { bufferWrapper = create(origin, compressBuffer(buffer, memoryManager), BufferType.BYTE_BUFFER_COMPRESSED); } else { bufferWrapper = create(origin, buffer, BufferWrapper.BufferType.BYTE_BUFFER); } } else if (origin instanceof Byte) { buffer = memoryManager.allocate(1); buffer.put((Byte) origin); buffer.flip(); bufferWrapper = create(origin, buffer, BufferType.BYTE); } else if (origin instanceof Boolean) { buffer = memoryManager.allocate(1); if ((Boolean) origin) { buffer.put((byte) '1'); } else { buffer.put((byte) '0'); } buffer.flip(); bufferWrapper = create(origin, buffer, BufferType.BOOLEAN); } else if (origin instanceof Short) { buffer = memoryManager.allocate(2); buffer.putShort((Short) origin); buffer.flip(); bufferWrapper = create(origin, buffer, BufferType.SHORT); } else if (origin instanceof Integer) { buffer = memoryManager.allocate(4); buffer.putInt((Integer) origin); buffer.flip(); bufferWrapper = create(origin, buffer, BufferType.INTEGER); } else if (origin instanceof Float) { buffer = memoryManager.allocate(4); buffer.putFloat((Float) origin); buffer.flip(); bufferWrapper = create(origin, buffer, BufferType.FLOAT); } else if (origin instanceof Double) { buffer = memoryManager.allocate(8); buffer.putDouble((Double) origin); buffer.flip(); bufferWrapper = create(origin, buffer, BufferType.DOUBLE); } else if (origin instanceof Long) { buffer = memoryManager.allocate(8); buffer.putLong((Long) origin); buffer.flip(); bufferWrapper = create(origin, buffer, BufferType.LONG); } else if (origin instanceof Date) { buffer = memoryManager.allocate(8); buffer.putLong(((Date) origin).getTime()); buffer.flip(); bufferWrapper = create(origin, buffer, BufferType.DATE); } else { BufferOutputStream bos = null; ObjectOutputStream oos = null; try { bos = new BufferOutputStream(memoryManager) { @Override protected Buffer allocateNewBuffer( final MemoryManager memoryManager, final int size) { final Buffer b = memoryManager.allocate(size); b.allowBufferDispose(true); return b; } }; oos = new ObjectOutputStream(bos); oos.writeObject(origin); buffer = bos.getBuffer(); buffer.flip(); if (buffer.remaining() > DEFAULT_COMPRESSION_THRESHOLD) { bufferWrapper = create(origin, compressBuffer(buffer, memoryManager), BufferType.OBJECT_COMPRESSED); } else { bufferWrapper = create(origin, buffer, BufferType.OBJECT); } } catch (IOException ie) { throw new IllegalArgumentException("Non-serializable object", ie); } finally { if (oos != null) { try { oos.close(); } catch (IOException ignore) { } } if (bos != null) { try { bos.close(); } catch (IOException ignore) { } } } } if (buffer != null) { buffer.allowBufferDispose(true); } return bufferWrapper; } /** * Return the original instance from {@code buffer} * * @param buffer the {@link Buffer} for being restored * @param type the type of original object * @param memoryManager the memory manager which will be used for decompressing packets * @return the restored object */ public static Object unwrap(final Buffer buffer, final BufferWrapper.BufferType type, final MemoryManager memoryManager) { if (buffer == null) { return null; } return unwrap(buffer, buffer.position(), buffer.limit(), type, memoryManager); } /** * Return the original instance from {@code buffer} * * @param buffer the {@link Buffer} for being restored * @param position the position of buffer to convert {@code buffer} into origin * @param limit the limit of buffer to convert {@code buffer} into origin * @param type the type of original object * @param memoryManager the memory manager which will be used for decompressing packets * @return the restored object */ public static Object unwrap(final Buffer buffer, final int position, final int limit, final BufferWrapper.BufferType type, final MemoryManager memoryManager) { if (buffer == null || position > limit || type == null) { return null; } switch (type) { case NONE: return buffer; case STRING: final int length = limit - position; final byte[] bytes = new byte[length]; buffer.get(bytes, 0, length); try { return new String(bytes, DEFAULT_CHARSET); } catch (UnsupportedEncodingException uee) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, "failed to decode the buffer", uee); } return null; } case STRING_COMPRESSED: final int length2 = limit - position; final byte[] bytes2 = new byte[length2]; buffer.get(bytes2, 0, length2); final byte[] decompressedBytes = decompress(bytes2); if (decompressedBytes == null) { return null; } try { return new String(decompressedBytes, DEFAULT_CHARSET); } catch (UnsupportedEncodingException uee) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, "failed to decode the buffer", uee); } return null; } case BYTE_ARRAY: final int length3 = limit - position; final byte[] bytes3 = new byte[length3]; buffer.get(bytes3, 0, length3); return bytes3; case BYTE_ARRAY_COMPRESSED: final int length4 = limit - position; final ByteBuffer byteBuffer = buffer.toByteBuffer(position, limit); return decompress(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), length4); case BYTE_BUFFER: return buffer.toByteBuffer(position, limit); case BYTE_BUFFER_COMPRESSED: final Buffer decompressedBuffer2 = decompressBuffer(buffer, position, limit, memoryManager); if (decompressedBuffer2 == null) { return null; } final ByteBuffer result = decompressedBuffer2.toByteBuffer(); decompressedBuffer2.tryDispose(); return result; case BYTE: return buffer.get(); case BOOLEAN: return buffer.get() == '1'; case SHORT: return buffer.getShort(); case INTEGER: return buffer.getInt(); case FLOAT: return buffer.getFloat(); case DOUBLE: return buffer.getDouble(); case LONG: return buffer.getLong(); case DATE: long dateLong = buffer.getLong(); return new Date(dateLong); case OBJECT: BufferInputStream bis = null; ObjectInputStream ois = null; Object obj = null; try { bis = new BufferInputStream(buffer, position, limit); ois = new ObjectInputStream(bis); obj = ois.readObject(); } catch (IOException ie) { if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, "failed to read the object", ie); } } catch (ClassNotFoundException cnfe) { if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, "failed to read the object", cnfe); } } finally { if (ois != null) { try { ois.close(); } catch (IOException ignore) { } } if (bis != null) { try { bis.close(); } catch (IOException ignore) { } } } return obj; case OBJECT_COMPRESSED: final Buffer decompressedBuffer3 = decompressBuffer(buffer, position, limit, memoryManager); if (decompressedBuffer3 == null) { return null; } BufferInputStream bis2 = null; ObjectInputStream ois2 = null; Object obj2 = null; try { bis2 = new BufferInputStream(decompressedBuffer3); ois2 = new ObjectInputStream(bis2); obj2 = ois2.readObject(); } catch (IOException ie) { if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, "failed to read the object", ie); } } catch (ClassNotFoundException cnfe) { if (logger.isLoggable(Level.SEVERE)) { logger.log(Level.SEVERE, "failed to read the object", cnfe); } } finally { if (ois2 != null) { try { ois2.close(); } catch (IOException ignore) { } } if (bis2 != null) { try { bis2.close(); } catch (IOException ignore) { } } } decompressedBuffer3.tryDispose(); return obj2; default: return null; } } private static Buffer compressBuffer(final Buffer buffer, final MemoryManager memoryManager) { if (buffer == null) { throw new IllegalArgumentException("failed to compress the buffer. buffer should be not null"); } final ByteBuffer byteBuffer = buffer.toByteBuffer(); final byte[] compressed = compress(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), byteBuffer.remaining()); buffer.tryDispose(); return Buffers.wrap(memoryManager, compressed); } private static byte[] compress(final byte[] in) { return compress(in, 0, in.length); } private static byte[] compress(final byte[] in, final int offset, final int length) { if (in == null) { throw new IllegalArgumentException("Can't compress null"); } final ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream gz = null; try { gz = new GZIPOutputStream(bos); gz.write(in, offset, length); } catch (IOException ie) { throw new RuntimeException("IO exception compressing data", ie); } finally { if (gz != null) { try { gz.close(); } catch (IOException ignore) { } } try { bos.close(); } catch (IOException ignore) { } } return bos.toByteArray(); } private static Buffer decompressBuffer(final Buffer buffer, final int position, final int limit, final MemoryManager memoryManager) { if (buffer == null || position > limit) { return null; } final int length = limit - position; final ByteBuffer byteBuffer = buffer.toByteBuffer(position, limit); final byte[] decompressed = decompress(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), length); if (decompressed == null) { return null; } return Buffers.wrap(memoryManager, decompressed); } private static byte[] decompress(final byte[] in) { if (in == null) { return null; } return decompress(in, 0, in.length); } private static byte[] decompress(final byte[] in, final int offset, final int length) { ByteArrayOutputStream bos; byte[] result = null; if (in != null) { ByteArrayInputStream bis = new ByteArrayInputStream(in, offset, length); bos = new ByteArrayOutputStream(); GZIPInputStream gis = null; try { gis = new GZIPInputStream(bis); byte[] buf = new byte[8192]; int r; while ((r = gis.read(buf)) > 0) { bos.write(buf, 0, r); } result = bos.toByteArray(); } catch (IOException ie) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, "failed to decompress bytes", ie); } bos = null; } finally { if (gis != null) { try { gis.close(); } catch (IOException ignore) { } } try { bis.close(); } catch (IOException ignore) { } if (bos != null) { try { bos.close(); } catch (IOException ignore) { } } } } return result; } @Override public String toString() { return "BufferWrapper{" + "buffer=" + buffer + ", type=" + type + ", origin=" + origin + '}'; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy