/**
* 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.net.listener.netty.handlers.udp;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Set;
import org.helios.apmrouter.OpCode;
import org.helios.apmrouter.jmx.mbeanserver.AgentMBeanServerConnectionFactory;
import org.helios.apmrouter.server.net.listener.netty.handlers.AbstractAgentRequestHandler;
import org.helios.apmrouter.server.net.listener.netty.handlers.AgentRequestHandler;
import org.helios.apmrouter.server.services.session.DecoratedChannel;
import org.helios.apmrouter.server.services.session.SharedChannelGroup;
import org.helios.apmrouter.util.ValueFilteredTimeoutListener;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelUpstreamHandler;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.logging.LoggingHandler;
import org.jboss.netty.logging.InternalLogLevel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jmx.export.annotation.ManagedMetric;
import org.springframework.jmx.support.MetricType;
/**
* <p>Title: UDPAgentOperationRouter</p>
* <p>Description: Routes agent requests to the correct service in accordance with the op-code in the request message</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.server.net.listener.netty.handlers.udp.UDPAgentOperationRouter</code></p>
*/
public class UDPAgentOperationRouter extends AbstractAgentRequestHandler implements ChannelUpstreamHandler, ValueFilteredTimeoutListener<String, OpCode> { //ChannelGroupAware
// /** The channel group */
// protected ManagedChannelGroupMXBean channelGroup = null;
/** A map of agent request handlers keyed by the opcode */
protected final EnumMap<OpCode, AgentRequestHandler> handlers = new EnumMap<OpCode, AgentRequestHandler>(OpCode.class);
/**
* Sets the agent request handlers
* @param agentRequestHandlers a collection of agent request handlers
*/
@Autowired(required=true)
public void setAgentRequestHandlers(Collection<AgentRequestHandler> agentRequestHandlers) {
for(AgentRequestHandler arh: agentRequestHandlers) {
for(OpCode soc: arh.getHandledOpCodes()) {
handlers.put(soc, arh);
info("Added AgentRequestHandler [", arh.getClass().getSimpleName(), "] for Op [", soc , "]");
}
}
handlers.put(OpCode.WHO_RESPONSE, this);
handlers.put(OpCode.BYE, this);
handlers.put(OpCode.HELLO, this);
handlers.put(OpCode.HELLO_CONFIRM, this);
handlers.put(OpCode.JMX_MBS_INQUIRY_RESPONSE, this);
}
/**
* {@inheritDoc}
* @see org.jboss.netty.channel.ChannelUpstreamHandler#handleUpstream(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent)
*/
@Override
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
if(e instanceof MessageEvent) {
Object msg = ((MessageEvent)e).getMessage();
if(msg instanceof ChannelBuffer) {
SocketAddress remoteAddress = ((MessageEvent)e).getRemoteAddress();
ChannelBuffer buff = (ChannelBuffer)msg;
incr("RequestsReceived");
OpCode opCode = OpCode.valueOf(buff);
if(opCode==OpCode.BYE) {
info("Processing BYE for client at [" + remoteAddress + "]");
DecoratedChannel dc = (DecoratedChannel) SharedChannelGroup.getInstance().getByRemote(remoteAddress);
if(dc!=null) {
dc.close();
}
return;
}
if(!containsPendingOp(remoteAddress, OpCode.WHO)) {
Channel remoteChannel = SharedChannelGroup.getInstance().getByRemote(remoteAddress);
if(remoteChannel==null) {
try {
remoteChannel = getChannelForRemote(e.getChannel(), remoteAddress);
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
}
try {
AgentRequestHandler handler = handlers.get(opCode);
if(handler==null) {
error("No handler registered for OpCode [", opCode, "]");
return;
}
handler.processAgentRequest(opCode,
buff,
((MessageEvent) e).getRemoteAddress(),
e.getChannel());
incr("RequestsCompleted");
} catch (Throwable t) {
incr("RequestsFailed");
error("Failed to handle [", opCode, "]", t );
}
return;
}
}
ctx.sendUpstream(e);
}
/** Logging handler */
private static final LoggingHandler clientConnLogHandler = new LoggingHandler("org.helios.UDPAgentOperationRouter", InternalLogLevel.INFO, true);
/**
* Returns the total number of agent operations received
* @return the total number of agent operations received
*/
@ManagedMetric(category="UDPOpRequests", metricType=MetricType.COUNTER, description="total number of agent operations received")
public long getRequestsReceived() {
return getMetricValue("RequestsReceived");
}
/**
* Returns the total number of agent operations completed
* @return the total number of agent operations completed
*/
@ManagedMetric(category="UDPOpRequests", metricType=MetricType.COUNTER, description="total number of agent operations completed")
public long getRequestsCompleted() {
return getMetricValue("RequestsCompleted");
}
/**
* Returns the total number of agent operations failed
* @return the total number of agent operations failed
*/
@ManagedMetric(category="UDPOpRequests", metricType=MetricType.COUNTER, description="total number of agent operations failed")
public long getRequestsFailed() {
return getMetricValue("RequestsFailed");
}
/**
* Returns the total number of timed out {@link OpCode#WHO} requests
* @return the total number of timed out {@link OpCode#WHO} requests
*/
@ManagedMetric(category="UDPOpRequests", metricType=MetricType.COUNTER, description="total number of timed out WHO requests")
public long getTimedOutWhos() {
return getMetricValue("TimedOutWhoOps");
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.ServerComponent#getSupportedMetricNames()
*/
@Override
public Set<String> getSupportedMetricNames() {
Set<String> metrics = new HashSet<String>(super.getSupportedMetricNames());
metrics.add("RequestsReceived");
metrics.add("RequestsCompleted");
metrics.add("RequestsFailed");
metrics.add("TimedOutWhoOps");
return metrics;
}
// /**
// * Sets the channel group
// * @param channelGroup the injected channel group
// */
// public void setChannelGroup(ManagedChannelGroup channelGroup) {
// this.channelGroup = channelGroup;
// for(AgentRequestHandler arh: handlers.values()) {
// if(arh instanceof ChannelGroupAware) {
// ((ChannelGroupAware)arh).setChannelGroup(channelGroup);
// }
// }
// }
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.net.listener.netty.handlers.AgentRequestHandler#processAgentRequest(org.helios.apmrouter.OpCode, org.jboss.netty.buffer.ChannelBuffer, java.net.SocketAddress, org.jboss.netty.channel.Channel)
*/
@Override
public void processAgentRequest(OpCode opCode, ChannelBuffer buff, SocketAddress remoteAddress, Channel channel) {
if(opCode==OpCode.WHO_RESPONSE) {
removePendingOp(remoteAddress, OpCode.WHO);
buff.readByte();
int hostLength = buff.readInt();
byte[] hostBytes = new byte[hostLength];
buff.readBytes(hostBytes);
int agentLength = buff.readInt();
byte[] agentBytes = new byte[agentLength];
buff.readBytes(agentBytes);
String host = new String(hostBytes);
String agent = new String(agentBytes);
DecoratedChannel dc = (DecoratedChannel) SharedChannelGroup.getInstance().getByRemote(remoteAddress);
dc.setWho(host, agent);
SharedChannelGroup.getInstance().sendIdentifiedChannelEvent(dc);
info("Agent at [", remoteAddress, "] identified itself as [", host, "/", agent, "]");
} else if(opCode==OpCode.HELLO) {
getChannelForRemote(channel, remoteAddress);
info("Agent at [", remoteAddress, "] sent HELLO");
ChannelBuffer cb = ChannelBuffers.directBuffer(1);
cb.writeByte(OpCode.HELLO_CONFIRM.op());
channel.write(cb, remoteAddress);
sendWho(channel, remoteAddress);
} else if(opCode==OpCode.HELLO_CONFIRM) {
warn("Received HELLO_CONFIRM ????");
} else if(opCode==OpCode.JMX_MBS_INQUIRY_RESPONSE) {
buff.readByte();
int arrSize = buff.readInt();
String[] domains = new String[arrSize];
for(int i = 0; i < arrSize; i++) {
byte[] bytes = new byte[buff.readInt()];
buff.readBytes(bytes);
domains[i] = new String(bytes);
}
DecoratedChannel dc = (DecoratedChannel) SharedChannelGroup.getInstance().getByRemote(remoteAddress);
String agent = dc.getAgent();
String host = dc.getHost();
AgentMBeanServerConnectionFactory.registerRemoteMBeanServerConnections(channel, remoteAddress, host, agent, "udp", domains);
}
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.server.net.listener.netty.handlers.AgentRequestHandler#getHandledOpCodes()
*/
@Override
public OpCode[] getHandledOpCodes() {
return new OpCode[]{OpCode.WHO_RESPONSE, OpCode.BYE, OpCode.HELLO, OpCode.JMX_MBS_INQUIRY_RESPONSE};
}
/**
* <p>Callback when a {@link OpCode#WHO} op times out.
* {@inheritDoc}
* @see org.helios.apmrouter.util.TimeoutListener#onTimeout(java.lang.Object, java.lang.Object)
*/
@Override
public void onTimeout(String key, OpCode value) {
incr("TimedOutWhoOps");
debug("Who op timed out [", key, "]");
}
/**
* <p>Specifies interest in {@link OpCode#WHO} ops only.
* {@inheritDoc}
* @see org.helios.apmrouter.util.ValueFilteredTimeoutListener#include(java.lang.Object)
*/
@Override
public boolean include(OpCode value) {
return OpCode.WHO==value;
}
}