/** * */ package test.org.helios.apmrouter.netty; import org.apache.log4j.BasicConfigurator; import org.helios.apmrouter.OpCode; import org.helios.apmrouter.metric.IMetric; import org.helios.apmrouter.metric.catalog.ICEMetricCatalog; import org.helios.apmrouter.metric.catalog.IMetricCatalog; import org.helios.apmrouter.sender.netty.UDPSender; import org.helios.apmrouter.sender.netty.handler.ChannelStateAware; import org.helios.apmrouter.sender.netty.handler.ChannelStateListener; import org.helios.apmrouter.trace.DirectMetricCollection; import org.helios.apmrouter.util.SystemClock; import org.jboss.netty.bootstrap.ConnectionlessBootstrap; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.buffer.DirectChannelBufferFactory; import org.jboss.netty.channel.*; import org.jboss.netty.channel.socket.nio.NioDatagramChannel; import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory; import org.jboss.netty.handler.logging.LoggingHandler; import org.jboss.netty.logging.InternalLogLevel; import org.jboss.netty.logging.InternalLoggerFactory; import org.jboss.netty.logging.Log4JLoggerFactory; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * <p>Title: UDPListener</p> * <p>Description: A test UDP listener for counting received messages</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>test.org.helios.apmrouter.netty.UDPListener</code></p> */ public class UDPListener implements ChannelPipelineFactory, ChannelStateAware { private final InetSocketAddress isock = new InetSocketAddress(2094); private final NioDatagramChannelFactory channelFactory = new NioDatagramChannelFactory(Executors.newCachedThreadPool()); private final ConnectionlessBootstrap bstrap = new ConnectionlessBootstrap(channelFactory); private final ChannelHandler handler = new TestHandler(); private NioDatagramChannel serverChannel; private LoggingHandler loggingHandler; private final ChannelStateListener channelStateListener = new ChannelStateListener(); private AtomicLong receivedBytes = new AtomicLong(0); private AtomicLong receivedMetrics = new AtomicLong(0); private final IMetricCatalog metricCatalog; /** * Creates a new UDPListener */ public UDPListener() { channelStateListener.addChannelStateAware(this); bstrap.setOption("broadcast", false); BasicConfigurator.configure(); InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory()); bstrap.setOption("receiveBufferSizePredictorFactory", new FixedReceiveBufferSizePredictorFactory(UDPSender.MAXSIZE)); loggingHandler = new LoggingHandler(InternalLogLevel.DEBUG, true); bstrap.setPipelineFactory(this); metricCatalog = ICEMetricCatalog.getInstance(); } /** * {@inheritDoc} * @see org.helios.apmrouter.sender.netty.handler.ChannelStateAware#getInterestedChannelStates() */ @Override public ChannelState[] getInterestedChannelStates() { return new ChannelState[]{ChannelState.CONNECTED}; } /** * {@inheritDoc} * @see org.helios.apmrouter.sender.netty.handler.ChannelStateAware#onChannelStateEvent(boolean, org.jboss.netty.channel.ChannelStateEvent) */ @Override public void onChannelStateEvent(boolean upstream, ChannelStateEvent stateEvent) { if(upstream && stateEvent.getValue().equals(Boolean.FALSE)) { } } public void start() { serverChannel = (NioDatagramChannel)bstrap.bind(isock); serverChannel.getConfig().setBufferFactory(new DirectChannelBufferFactory()); new Thread("ActivityPrinter") { long lastBytes = 0, lastMetrics = 0, bytes = 0, metrics = 0; public void run() { while(true) { try { for(int i = 0; i < 12; i++) { SystemClock.sleep(5, TimeUnit.SECONDS); bytes = receivedBytes.get(); metrics = receivedMetrics.get(); if(bytes!=lastBytes || metrics!=lastMetrics) { log("Totals: Bytes:" + bytes + " Metrics:" + metrics); } lastBytes = bytes; lastMetrics = metrics; } log("\n\t====\n\tTotals: Bytes:" + bytes + " Metrics:" + metrics + "\n\t===="); } catch (Exception e) {} } } }.start(); log("UDP Test Listener Started on [" + isock + "]"); } /** * Boots the UDP listener * @param args None */ public static void main(String[] args) { log("UDP Test Listener"); new UDPListener().start(); } /** * Out log * @param msg the message to log */ public static void log(Object msg) { System.out.println(msg); } @Override public ChannelPipeline getPipeline() throws Exception { // return Channels.pipeline(loggingHandler, channelStateListener, handler); return Channels.pipeline(channelStateListener, handler); } private class TestHandler extends SimpleChannelUpstreamHandler { @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { e.getCause().printStackTrace(System.err); super.exceptionCaught(ctx, e); } @SuppressWarnings("unused") @Override public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent me) throws Exception { Object msg = me.getMessage(); if(msg instanceof ChannelBuffer) { ChannelBuffer buff = (ChannelBuffer)msg; //log("Received Channel Buffer:" + buff.isDirect()); DirectMetricCollection dmc = DirectMetricCollection.fromChannelBuffer(buff); OpCode opCode = dmc.getOpCode(); //if(buff.readableBytes()<length) return null; receivedBytes.addAndGet(dmc.getSize()); receivedMetrics.addAndGet(dmc.getMetricCount()); int byteOrder = buff.getByte(1); int totalSize = buff.getInt(2); IMetric[] metrics = null; try { metrics = dmc.decode(); } catch (Exception e) { e.printStackTrace(System.err); } //dmc.destroy(); for(final IMetric metric: metrics) { if(opCode==OpCode.SEND_METRIC_DIRECT) { sendConfirm(me.getChannel(), me.getRemoteAddress(), metric); } if(metric.getToken()==-1) { sendToken(me.getChannel(), me.getRemoteAddress(), metric); } } } } } /** * Confirms the receipt of a direct metric * @param channel The channel on which the metric was received * @param address The remote address of the sender * @param metric The direct metric */ protected static void sendConfirm(Channel channel, SocketAddress remoteAddress, final IMetric metric) { String key = new StringBuilder(metric.getFQN()).append(metric.getTime()).toString(); byte[] bytes = key.getBytes(); // Buffer size: OpCode, key size, key bytes ChannelBuffer cb = ChannelBuffers.directBuffer(1 + 4 + bytes.length); cb.writeByte(OpCode.CONFIRM_METRIC.op()); cb.writeInt(bytes.length); cb.writeBytes(bytes); if(!channel.isConnected()) channel.connect(remoteAddress).awaitUninterruptibly(); channel.write(cb, remoteAddress).addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { if(future.isSuccess()) { //System.out.println("Sent Token for [" + metric.getFQN() + "]"); } else { System.err.println("Failed to send confirm for direct metric [" + metric + "]"); future.getCause().printStackTrace(System.err); } } }); } /** * When an untokenized metric is received, the token is generated from the metric catalog * and returned to the caller in this protocol:<ol> * <li>0 for the op-code (1 byte)</li> * <li>The size of the metric FQN (1 int)</li> * <li>The metric FQN's bytes (n bytes)</li> * <li>The metric token (1 long)</li> * </ol> * @param channel The channel on which the untokenized metric was received * @param address The remote address of the sender * @param metric The untokenized metric */ protected void sendToken(final Channel channel, final SocketAddress address, final IMetric metric) { final long token = metricCatalog.setToken(metric); byte[] bytes = metric.getFQN().getBytes(); // Buffer size: OpCode, fqn size, fqn bytes, token ChannelBuffer cb = ChannelBuffers.directBuffer(1 + 4 + bytes.length + 8 ); cb.writeByte(OpCode.SEND_METRIC_TOKEN.op()); cb.writeInt(bytes.length); cb.writeBytes(bytes); cb.writeLong(token); if(!channel.isConnected()) channel.connect(address).awaitUninterruptibly(); channel.write(cb, address).addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { if(future.isSuccess()) { //System.out.println("Sent Token for [" + metric.getFQN() + "]"); } else { System.err.println("Failed to send token for [" + metric.getFQN() + "]"); future.getCause().printStackTrace(System.err); } } }); } }