package org.kairosdb.plugin.carbon; import com.google.inject.Inject; import com.google.inject.name.Named; import org.jboss.netty.bootstrap.ConnectionlessBootstrap; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.*; import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder; import org.jboss.netty.handler.codec.frame.Delimiters; import org.jboss.netty.handler.codec.frame.FrameDecoder; import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder; import org.jboss.netty.handler.codec.string.StringEncoder; import org.kairosdb.core.DataPoint; import org.kairosdb.core.KairosDBService; import org.kairosdb.core.datapoints.DoubleDataPointFactory; import org.kairosdb.core.datapoints.DoubleDataPointFactoryImpl; import org.kairosdb.core.datapoints.LongDataPointFactory; import org.kairosdb.core.datapoints.LongDataPointFactoryImpl; import org.kairosdb.core.exception.KairosDBException; import org.kairosdb.core.telnet.WordSplitter; import org.kairosdb.eventbus.FilterEventBus; import org.kairosdb.eventbus.Publisher; import org.kairosdb.events.DataPointEvent; import org.kairosdb.util.ValidationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.concurrent.Executors; import static com.google.common.base.Preconditions.checkNotNull; /** Created with IntelliJ IDEA. User: bhawkins Date: 9/30/13 Time: 4:09 PM To change this template use File | Settings | File Templates. */ public class CarbonTextServer extends SimpleChannelUpstreamHandler implements ChannelPipelineFactory, KairosDBService { public static final Logger logger = LoggerFactory.getLogger(CarbonTextServer.class); private final int m_port; private InetAddress m_address; private final Publisher<DataPointEvent> m_publisher; private final TagParser m_tagParser; private ServerBootstrap m_serverBootstrap; @Inject @Named("kairosdb.carbon.text.max_size") private int m_maxSize = 2048; @Inject private LongDataPointFactory m_longDataPointFactory = new LongDataPointFactoryImpl(); @Inject private DoubleDataPointFactory m_doubleDataPointFactory = new DoubleDataPointFactoryImpl(); private ConnectionlessBootstrap m_udpBootstrap; public CarbonTextServer(FilterEventBus eventBus, TagParser tagParser, @Named("kairosdb.carbon.text.port") int port) { this(eventBus, tagParser, port, null); } @Inject public CarbonTextServer(FilterEventBus eventBus, TagParser tagParser, @Named("kairosdb.carbon.text.port") int port, @Named("kairosdb.carbon.text.address") String address) { m_port = port; m_publisher = checkNotNull(eventBus).createPublisher(DataPointEvent.class); m_tagParser = tagParser; m_address = null; try { m_address = InetAddress.getByName(address); } catch (UnknownHostException e) { logger.error("Unknown host name " + address + ", will bind to 0.0.0.0"); } } @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); // Add the text line codec combination first, FrameDecoder frameDecoder = new LineBasedFrameDecoder( m_maxSize, true, true); pipeline.addLast("framer", frameDecoder); pipeline.addLast("decoder", new WordSplitter()); pipeline.addLast("encoder", new StringEncoder()); // and then business logic. pipeline.addLast("handler", this); return pipeline; } @Override public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent msgevent) { final Object message = msgevent.getMessage(); if (message instanceof String[]) { try { String[] msgArr = (String[])message; //TODO: Validate data CarbonMetric carbonMetric = m_tagParser.parseMetricName(msgArr[0]); //Bail out if no data point set is returned if (carbonMetric == null) return; //validate dps has at least one tag if (carbonMetric.getTags().size() == 0) { logger.warn("Metric "+msgArr[0]+" is missing a tag"); return; } if ("NaN".equalsIgnoreCase(msgArr[2])) { logger.info("Metric {} has a timetamp of 'NaN'. Not sending to Kairos", msgArr[0]); return; } long timestamp = Long.parseLong(msgArr[2]) * 1000; //Converting to milliseconds DataPoint dp; if ("NaN".equalsIgnoreCase(msgArr[1])) { logger.info("Metric {} has a value of 'NaN'. Not sending to Kairos", msgArr[0]); return; } if (msgArr[1].toLowerCase().contains("infinity")) { logger.info("Metric {} has a value of Infinity/-Infinity. Not sending to Kairos", msgArr[0]); return; } if (msgArr[1].contains(".")) dp = m_doubleDataPointFactory.createDataPoint(timestamp, Double.parseDouble(msgArr[1])); else dp = m_longDataPointFactory.createDataPoint(timestamp, Long.parseLong(msgArr[1])); m_publisher.post(new DataPointEvent(carbonMetric.getName(), carbonMetric.getTags(), dp, carbonMetric.getTtl())); } catch (Exception e) { logger.error("Carbon text error", e); } } else { log("Invalid message. Must be of type String."); } } private static void log(String message) { log(message, null); } private static void log(String message, Exception e) { if (logger.isDebugEnabled()) if (e != null) logger.debug(message, e); else logger.debug(message); else { if (e instanceof ValidationException) message = message + " Reason: " + e.getMessage(); logger.warn(message); } } @Override public void start() throws KairosDBException { // Configure the server. m_serverBootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Configure the pipeline factory. m_serverBootstrap.setPipelineFactory(this); m_serverBootstrap.setOption("child.tcpNoDelay", true); m_serverBootstrap.setOption("child.keepAlive", true); m_serverBootstrap.setOption("reuseAddress", true); // Bind and start to accept incoming connections. m_serverBootstrap.bind(new InetSocketAddress(m_address, m_port)); m_udpBootstrap = new ConnectionlessBootstrap( new NioDatagramChannelFactory()); m_udpBootstrap.setOption("receiveBufferSizePredictorFactory", new FixedReceiveBufferSizePredictorFactory(m_maxSize)); m_udpBootstrap.setPipelineFactory(this); m_udpBootstrap.bind(new InetSocketAddress(m_port)); } @Override public void stop() { if (m_serverBootstrap != null) m_serverBootstrap.shutdown(); if (m_udpBootstrap != null) m_udpBootstrap.shutdown(); } }