/** * 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.server.unification.pipeline2; import java.net.SocketAddress; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import org.apache.log4j.Logger; import org.helios.apmrouter.logging.APMLogLevel; import org.helios.apmrouter.server.unification.protocol.ProtocolInitiator; import org.helios.apmrouter.util.NettyUtil; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.buffer.ReadOnlyChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.LifeCycleAwareChannelHandler; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.jboss.netty.channel.UpstreamMessageEvent; import org.jboss.netty.handler.codec.replay.ReplayingDecoder; import org.springframework.jmx.export.annotation.ManagedAttribute; /** * <p>Title: ProtocolSwitchDecoder</p> * <p>Description: A {@link ReplayingDecoder} implementation that manages the port unification protocol switch</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.server.unification.pipeline2.ProtocolSwitchDecoder</code></p> */ public class ProtocolSwitchDecoder extends SimpleChannelUpstreamHandler implements LifeCycleAwareChannelHandler { // JO NESB0 // protocol initiators: http / gzip / bzip // protocol decoders: gzip / bzip // content classifiers: this xml handler, or that binary fluff handler // /** The port protocol switch context */ // protected final ChannelLocal<ProtocolSwitchContext> context = new ChannelLocal<ProtocolSwitchContext>(true); /** Instance logger */ protected final Logger log = Logger.getLogger(getClass()); /** The logger level */ protected APMLogLevel level = APMLogLevel.pCode(log.getEffectiveLevel().toInt()); /** The registered protocol initiators */ protected final Set<Initiator> pInitiators = new CopyOnWriteArraySet<Initiator>(); /** The maximum number of bytes allowed for protocol/content negotiation */ protected int maxInitiatorBytes = 1024; /** An empty buffer */ protected static final ChannelBuffer EMPTY_BUFFER = new ReadOnlyChannelBuffer(ChannelBuffers.buffer(0)); /** * Adds a set of {@link Initiator}s to be considered in the protocol switch * @param initiators a set of {@link Initiator}s */ public void setInitiators(Set<Initiator> initiators) { if(initiators!=null) { pInitiators.addAll(initiators); } } /** The name of this decoder in the pipeline */ public static final String PIPE_NAME = "psd"; /** * Creates a new ProtocolSwitchDecoder */ public ProtocolSwitchDecoder() { super(); } /** * {@inheritDoc} * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#messageReceived(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) */ @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { Object msg = e.getMessage(); if(msg instanceof ChannelBuffer) { ChannelBuffer buffer = (ChannelBuffer)msg; processChannel(ctx, buffer, (UpstreamMessageEvent)e); } super.messageReceived(ctx, e); } /** * Processes incoming channel buffers and delegates to the correct {@link ProtocolInitiator}s to interpret the protocol. * @param ctx The channel handler context * @param buffer The incoming buffer * @param event the original message event */ protected void processChannel(ChannelHandlerContext ctx, final ChannelBuffer buffer, final UpstreamMessageEvent event) { final int bytesAvailable = buffer.readableBytes(); final Channel channel = ctx.getChannel(); ProtocolSwitchContext portContext = (ProtocolSwitchContext)ctx.getAttachment(); if(portContext==null) { portContext = new ProtocolSwitchContext(ctx, channel, buffer, bytesAvailable); } else { portContext.update(ctx, buffer, bytesAvailable); } boolean insufBytes = false; for(Initiator pi: pInitiators) { int requiredBytes = pi.requiredBytes(); if(bytesAvailable < requiredBytes && requiredBytes > 0) { insufBytes = true; portContext.failInitiator(pi); continue; } if(!portContext.hasInitiatorFailed(pi)) { try { Object matchKey = pi.match(buffer); if(matchKey==null) { portContext.failInitiator(pi); log.info("PI [" + pi.getName() + "] failed"); continue; } log.info("PI [" + pi.getName() + "] MATCHED"); log.info(NettyUtil.formatBuffer(buffer, 100)); pi.process(portContext, matchKey); return; } catch (Exception ex) { portContext.failInitiator(pi); log.info("PI [" + pi.getName() + "] failed"); continue; } } } if(insufBytes) { // this means that no initiator matched, but at least 1 initiator // reported insufficient bytes to declare a match or miss. // therefore we need to try to keep reading bytes until we get a match, // or, if we determine that the max number of bytes [or all of the bytes] // have been read, we throw a content not recognized error } } /** * Returns the level of this instance's logger * @return the level of this instance's logger */ @ManagedAttribute(description="The logging level of this component") public String getLevel() { return level.name(); } /** * Sets the logging level for this instance * @param levelName the name of the logging level for this instance */ @ManagedAttribute(description="The logging level of this component") public void setLevel(String levelName) { level = APMLogLevel.valueOfName(levelName); log.setLevel(level.getLevel()); log.info("Set Logger to level [" + log.getLevel().toString() + "]"); } public static String toString(ChannelBuffer buffer) { return toString(buffer, 100); } public static String toString(ChannelBuffer buffer, int maxLength) { if(buffer==null || buffer.readableBytes()<1) return ""; byte[] bytes = new byte[buffer.readableBytes()>maxLength ? maxLength : buffer.readableBytes()]; buffer.getBytes(0, bytes); return new String(bytes); } /** * Returns the maximum number of bytes allowed for protocol/content negotiation * @return the maximum number of bytes allowed for protocol/content negotiation */ @ManagedAttribute(description="The maximum number of bytes allowed for protocol/content negotiation") public int getMaxInitiatorBytes() { return maxInitiatorBytes; } /** * Sets the maximum number of bytes allowed for protocol/content negotiation * @param maxInitiatorBytes the maximum number of bytes allowed for protocol/content negotiation */ @ManagedAttribute(description="The maximum number of bytes allowed for protocol/content negotiation") public void setMaxInitiatorBytes(int maxInitiatorBytes) { this.maxInitiatorBytes = maxInitiatorBytes; } public void afterAdd(ChannelHandlerContext ctx) throws Exception { log.info("HANDLER EVENT: AfterAdd [" + ctx.getName() + "]"); } public void beforeAdd(ChannelHandlerContext ctx) throws Exception { log.info("HANDLER EVENT: BeforeAdd [" + ctx.getName() + "]"); } public void afterRemove(ChannelHandlerContext ctx) throws Exception { log.info("HANDLER EVENT: AfterRemove[" + ctx.getName() + "]"); } public void beforeRemove(ChannelHandlerContext ctx) throws Exception { log.info("HANDLER EVENT: beforeRemove[" + ctx.getName() + "]"); } }