/*
* TaggedObjectDecoder.java
*
* Created on Feb 10, 2010, 4:43:37 PM
*
* Description: Provides a tagged object decoder.
*
* Copyright (C) Feb 10, 2010 reed.
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.texai.network.netty.handler;
import java.io.IOException;
import java.io.StreamCorruptedException;
import net.jcip.annotations.NotThreadSafe;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import org.texai.network.netty.NetworkConstants;
/** Provides a tagged object decoder. A tagged object is a serialized object with a protocol identification
* byte prepended.
*
* @author reed
*/
@NotThreadSafe
public class TaggedObjectDecoder extends FrameDecoder {
/** the logger */
private static final Logger LOGGER = Logger.getLogger(TaggedObjectDecoder.class);
/** the maximum object size */
private final int maxObjectSize;
/** Creates a new TaggedObjectDecoder whose maximum object size is104857 bytes.
* If the size of the received object is greater than 1048576 bytes, a StreamCorruptedException
* will be raised.
*/
public TaggedObjectDecoder() {
this(1048576);
}
/** Creates a new decoder with the specified maximum object size.
*
* @param maxObjectSize the maximum byte length of the serialized object.
* if the length of the received object is greater
* than this value, {@link StreamCorruptedException}
* will be raised.
*/
public TaggedObjectDecoder(final int maxObjectSize) {
//Preconditions
if (maxObjectSize <= 0) {
throw new IllegalArgumentException("maxObjectSize: " + maxObjectSize);
}
this.maxObjectSize = maxObjectSize;
}
/** Decodes the received packets so far into an object.
*
* @param channelHandlerContext the context of this handler
* @param channel the current channel
* @param channelBuffer the cumulative buffer of received packets so far.
* Note that the buffer might be empty, which means you
* should not make an assumption that the buffer contains
* at least one byte in your decoder implementation.
*
* @return the object if all its bytes were contained in the buffer.
* null if there's not enough data in the buffer to decode an object.
* @throws IOException if an input/output error occurs
* @throws ClassNotFoundException if the serialized object's class cannot be found
*/
@Override
protected Object decode(
final ChannelHandlerContext channelHandlerContext,
final Channel channel,
final ChannelBuffer channelBuffer) throws IOException, ClassNotFoundException {
//Preconditions
assert channelHandlerContext != null : "channelHandlerContext must not be null";
assert channel != null : "channel must not be null";
assert channelBuffer != null : "channelBuffer must not be null";
if (channelBuffer.readableBytes() < 5) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("not enough bytes received in the buffer to decode");
}
return null;
}
final byte protocolByte = channelBuffer.readByte();
assert protocolByte == NetworkConstants.OBJECT_SERIALIZATION_PROTOCOL : "protocolByte: " + protocolByte;
final int dataLen = channelBuffer.getInt(channelBuffer.readerIndex());
if (dataLen <= 0) {
throw new StreamCorruptedException("invalid data length: " + dataLen);
}
if (dataLen > maxObjectSize) {
throw new StreamCorruptedException(
"data length too big: " + dataLen + " (max: " + maxObjectSize + ')');
}
if (channelBuffer.readableBytes() < dataLen + 4) {
return null;
}
channelBuffer.skipBytes(4);
return new CompactObjectInputStream(new ChannelBufferInputStream(channelBuffer, dataLen)).readObject();
}
}