com.heliosapm.streams.onramp.ConnectionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.heliosapm.streams.onramp.ConnectionManager.java

Source

/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.heliosapm.streams.onramp;

import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.heliosapm.streams.common.metrics.SharedMetricsRegistry;
import com.heliosapm.utils.jmx.JMXHelper;

import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.IllegalReferenceCountException;
import io.netty.util.ReferenceCountUtil;
//import io.netty.handler.codec.embedder.CodecEmbedderException;
import io.netty.util.concurrent.DefaultEventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

/**
 * <p>Title: ConnectionManager</p>
 * <p>Description: </p> 
 * <p>Company: Helios Development Group LLC</p>
 * @author Whitehead (nwhitehead AT heliosdev DOT org)
 * <p><code>com.heliosapm.streams.onramp.ConnectionManager</code></p>
 */
@ChannelHandler.Sharable
public class ConnectionManager extends ChannelDuplexHandler implements ConnectionManagerMBean {
    /** The singleton instance */
    private static volatile ConnectionManager instance = null;
    /** The singleton instance ctor lock */
    private static final Object lock = new Object();

    /** Instance logger */
    private final Logger log = LogManager.getLogger(getClass());
    /** A gauge of established connections */
    private final AtomicLong connections_established = new AtomicLong();
    /** The shared metric publication of the connections established gauge */
    @SuppressWarnings("unused")
    private final Gauge<Long> connections_established_gauge = SharedMetricsRegistry.getInstance()
            .gauge("connmgr.connections.estalished", new Callable<Long>() {
                @Override
                public Long call() {
                    return connections_established.get();
                }
            });
    /** A counter of connection instances */
    private final Counter connections = SharedMetricsRegistry.getInstance().counter("connmgr.connections");

    /** A counter of unknown exception types */
    private final Counter exceptions_unknown = SharedMetricsRegistry.getInstance()
            .counter("connmgr.exceptions.unknown");
    /** A counter of client side closed exceptions */
    private final Counter exceptions_closed = SharedMetricsRegistry.getInstance()
            .counter("connmgr.exceptions.closed");
    /** A counter of reset exceptions */
    private final Counter exceptions_reset = SharedMetricsRegistry.getInstance()
            .counter("connmgr.exceptions.reset");
    /** A counter of timeout exceptions */
    private final Counter exceptions_timeout = SharedMetricsRegistry.getInstance()
            .counter("connmgr.exceptions.timeout");
    /** A counter of idle session timeouts */
    private final Counter idle_timeout = SharedMetricsRegistry.getInstance().counter("connmgr.idle.timeout");

    private final DefaultChannelGroup channels = new DefaultChannelGroup("all-channels",
            new DefaultEventExecutor(new ThreadFactory() {
                final AtomicInteger serial = new AtomicInteger();

                @Override
                public Thread newThread(final Runnable r) {
                    final Thread t = new Thread(r, "ChannelGroupThread#" + serial.incrementAndGet());
                    return t;
                }
            }));

    /** The http client channel initializer */
    private final ChannelInitializer<SocketChannel> channelInitializer = new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(final SocketChannel sc) throws Exception {
            ChannelPipeline p = sc.pipeline();
            p.addLast(new HttpContentCompressor());
            p.addLast(new HttpClientCodec());
            p.addLast(new HttpContentDecompressor());
            p.addLast(new HttpObjectAggregator(1048576));
        }
    };

    private ConnectionManager() {
        if (JMXHelper.isRegistered(OBJECT_NAME)) {
            try {
                JMXHelper.unregisterMBean(OBJECT_NAME);
            } catch (Exception x) {
                /* No Op */}
        }
        try {
            JMXHelper.registerMBean(this, OBJECT_NAME);
        } catch (Exception ex) {
            log.warn("Failed to register the ConnectionManager JMX MBean. Continuing without.", ex);
        }
    }

    /**
     * Acquires and returns the ConnectionManager singleton instance
     * @return the ConnectionManager singleton instance
     */
    public static ConnectionManager getInstance() {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new ConnectionManager();
                }
            }
        }
        return instance;
    }

    /**
     * {@inheritDoc}
     * @see io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext)
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        final Channel channel = ctx.channel();
        log.info("Channel Activated [{}]", channel);
        channels.add(channel);
        channel.closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() {
            public void operationComplete(Future<? super Void> future) throws Exception {
                connections_established.decrementAndGet();
            }
        });
        connections_established.incrementAndGet();
        connections.inc();
        super.channelActive(ctx);
    }

    /**
     * {@inheritDoc}
     * @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
     */
    @Override
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
        ReferenceCountUtil.retain(msg);
        super.channelRead(ctx, msg);
    }

    /**
     * {@inheritDoc}
     * @see io.netty.channel.ChannelInboundHandlerAdapter#userEventTriggered(io.netty.channel.ChannelHandlerContext, java.lang.Object)
     */
    public void userEventTriggered(final ChannelHandlerContext ctx, final Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            log.info("Session Timeout on channel [{}]", ctx.channel());
            idle_timeout.inc();
            ctx.channel().close();

        }
    }

    /**
     * {@inheritDoc}
     * @see io.netty.channel.ChannelInboundHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext, java.lang.Throwable)
     */
    @Override
    public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
        final Channel chan = ctx.channel();
        if (cause instanceof ClosedChannelException) {
            exceptions_closed.inc();
            log.warn("Attempt to write to closed channel " + chan);
            return;
        } else if (cause instanceof IllegalReferenceCountException) {
            log.warn("BadRefCount: [{}]", cause.getMessage());
        } else if (cause instanceof IOException) {
            final String message = cause.getMessage();
            if ("Connection reset by peer".equals(message)) {
                exceptions_reset.inc();
                return;
            } else if ("Connection timed out".equals(message)) {
                exceptions_timeout.inc();
                // Do nothing.  A client disconnecting isn't really our problem.  Oh,
                // and I'm not kidding you, there's no better way to detect ECONNRESET
                // in Java.  Like, people have been bitching about errno for years,
                // and Java managed to do something *far* worse.  That's quite a feat.
                return;
            }
        }
        // FIXME: not sure what the netty 4 is for this
        //if (cause instanceof CodecEmbedderException) {
        //   // payload was not compressed as it was announced to be
        //   log.warn("Http codec error : " + cause.getMessage());
        //   e.getChannel().close();
        //   return;
        //}
        exceptions_unknown.inc();
        log.error("Unexpected exception from downstream for " + chan, cause);
        chan.close();
    }

    /**
     * Returns the number of currently established connections
     * @return the number of currently established connections
     */
    public long getConnectionsEstablished() {
        return connections_established.get();
    }

    /**
        
    /**
     * Returns the cummulative number of connections established 
     * @return the cummulative number of connections established
     */
    public long getConnections() {
        return connections.getCount();
    }

    /**
     * Returns the cummulative number of unknow caused exceptions
     * @return the cummulative number of unknow caused exceptions
     */
    public long getExceptionsUnknown() {
        return exceptions_unknown.getCount();
    }

    /**
     * Returns the cummulative number of closed caused exceptions
     * @return the cummulative number of closed caused exceptions
     */
    public long getExceptionsClosed() {
        return exceptions_closed.getCount();
    }

    /**
     * Returns the cummulative number of connection reset caused exceptions
     * @return the cummulative number of connection reset caused exceptions
     */
    public long getExceptionsReset() {
        return exceptions_reset.getCount();
    }

    /**
     * Returns the cummulative number of connection timeout caused exceptions
     * @return the cummulative number of connection timeout caused exceptions
     */
    public long getExceptionsTimeout() {
        return exceptions_timeout.getCount();
    }

    /**
     * Returns the cummulative number of idle connections that were reaped 
     * @return the cummulative number of idle connections that were reaped
     */
    public long getIdleTimeout() {
        return idle_timeout.getCount();
    }

    /**
     * Returns the number of registered channels
     * @return the number of registered channels
     */
    public int getChannels() {
        return channels.size();
    }

    /**
     * Terminates all established connections
     */
    public void terminateAllConnections() {
        channels.close().syncUninterruptibly();
    }

}