/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.sender.netty.codec;
import org.helios.apmrouter.metric.ICEMetric;
import org.helios.apmrouter.metric.IMetric;
import org.helios.apmrouter.metric.MetricType;
import org.helios.apmrouter.metric.catalog.ICEMetricCatalog;
import org.helios.apmrouter.metric.catalog.IDelegateMetric;
import org.helios.apmrouter.metric.catalog.IMetricCatalog;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static org.helios.apmrouter.sender.netty.codec.IMetricDecodePhase.*;
/**
* <p>Title: IMetricDecoder</p>
* <p>Description: A replaying decoder for {@link IMetric} instances.</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.sender.netty.codec.IMetricDecoder</code></p>
* FIXME: The IMetricRebuilder will be very inneficient since it has to be created once per incoming metric.
* We should re-create it as a ChannelLocal or extend Channel and add the state there.
*/
@ChannelHandler.Sharable
public class IMetricDecoder extends ReplayingDecoder<IMetricDecodePhase> {
/** The metric catalog for tokenization */
private final IMetricCatalog metricCatalog;
/**
* Creates a new IMetricDecoder
*/
public IMetricDecoder() {
metricCatalog = ICEMetricCatalog.getInstance();
}
/**
* {@inheritDoc}
* @see org.jboss.netty.handler.codec.replay.ReplayingDecoder#decode(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.Channel, org.jboss.netty.buffer.ChannelBuffer, java.lang.Enum)
*/
@Override
protected IMetric decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, IMetricDecodePhase state) throws Exception {
IMetricRebuilder rebuilder = (IMetricRebuilder)ctx.getAttachment();
if(rebuilder==null) {
rebuilder = new IMetricRebuilder();
ctx.setAttachment(rebuilder);
state = BYTE_ORDER;
}
switch (state) {
case BYTE_ORDER:
rebuilder.setByteOrder(buffer.readByte()==0 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
checkpoint(ISTOKENIZED);
//$FALL-THROUGH$
case ISTOKENIZED:
IDelegateMetric dmetric = null;
long token = buffer.readLong();
if(token!=-1) {
dmetric = metricCatalog.get(token);
if(dmetric!=null) {
rebuilder.setDelegateMetric(dmetric);
checkpoint(TIMESTAMP);
break;
}
token = -1;
}
checkpoint(TYPE);
//$FALL-THROUGH$
case TYPE:
rebuilder.setType(MetricType.valueOf(buffer.readByte()));
checkpoint(FQNLENGTH);
//$FALL-THROUGH$
case FQNLENGTH:
rebuilder.setFqnLength(buffer.readInt());
checkpoint(FQN);
//$FALL-THROUGH$
case FQN:
byte[] fqnbytes = new byte[rebuilder.getFqnLength()];
buffer.readBytes(fqnbytes);
rebuilder.setFqn(new String(fqnbytes));
checkpoint(TIMESTAMP);
//$FALL-THROUGH$
case TIMESTAMP:
rebuilder.setTs(buffer.readLong());
checkpoint(LONGVALUE);
//$FALL-THROUGH$
case LONGVALUE:
if(rebuilder.getType().isLong()) {
rebuilder.setLvalue(buffer.readLong());
return rebuilder.buildMetric();
}
checkpoint(VLENGTH);
//$FALL-THROUGH$
case VLENGTH:
rebuilder.setVlength(buffer.readInt());
checkpoint(VALUE);
//$FALL-THROUGH$
case VALUE:
buffer.readBytes(rebuilder.getValue());
return rebuilder.buildMetric();
}
return null;
}
/**
* <p>Title: IMetricRebuilder</p>
* <p>Description: Container class that serves as the replaying decoder's state during the decode, and rebuilds the received metric when decoding is complete.</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.sender.netty.codec.IMetricDecoder.IMetricRebuilder</code></p>
*/
private class IMetricRebuilder {
/** The byte order of the incoming bytes */
private ByteOrder byteOrder;
/** The decoded delegate metric ID */
private IDelegateMetric delegateMetric;
/** The length of the metric FQN */
private int fqnLength = -1;
/** The metric's FQN */
private String fqn = null;
/** The metric type */
private MetricType type = null;
/** The metric timestamp */
private long ts;
/** The metric's long value */
private long lvalue;
/** The metric's non-long value bytes */
private ByteBuffer value;
/**
* Returns the length of the FQN in bytes
* @return the fqn length
*/
public int getFqnLength() {
return fqnLength;
}
/**
* Sets the FQN length
* @param fqnLength the fqn length to set
*/
public void setFqnLength(int fqnLength) {
this.fqnLength = fqnLength;
}
/**
* Returns the metric type
* @return the type
*/
public MetricType getType() {
return type;
}
/**
* Sets the type
* @param type the type to set
*/
public void setType(MetricType type) {
this.type = type;
}
/**
* @param vlength the vlength to set
*/
public void setVlength(int vlength) {
this.value = MetricType.allocate(vlength);
}
/**
* Returns the value container byte buffer
* @return the value container byte buffer
*/
public ByteBuffer getValue() {
return value;
}
/**
* Sets the byte order for the submitted metric
* @param byteOrder the byte order to set
*/
public void setByteOrder(ByteOrder byteOrder) {
this.byteOrder = byteOrder;
}
/**
* Sets the delegate metric when the submission is tokenized
* @param delegateMetric the delegate metric to set
*/
public void setDelegateMetric(IDelegateMetric delegateMetric) {
this.delegateMetric = delegateMetric;
this.type = this.delegateMetric.getType();
}
/**
* Sets the FQN
* @param fqn the fqn to set
*/
public void setFqn(String fqn) {
this.fqn = fqn;
setDelegateMetric(metricCatalog.build(fqn, type));
}
/**
* Sets the metric timestamp
* @param ts the ts to set
*/
public void setTs(long ts) {
this.ts = ts;
}
/**
* Sets the value when the metric type is a long
* @param lvalue the long value to set
*/
public void setLvalue(long lvalue) {
this.lvalue = lvalue;
}
/**
* Builds an IMetric
* @return an IMetric
*/
IMetric buildMetric() {
return type.isLong() ? ICEMetric.newMetric(ts, lvalue, type, delegateMetric) : ICEMetric.newMetric(ts, value, type, delegateMetric);
}
}
}