org.opendaylight.protocol.bmp.impl.BmpDispatcherImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.protocol.bmp.impl.BmpDispatcherImpl.java

Source

/*
 * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

package org.opendaylight.protocol.bmp.impl;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.AbstractChannel;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import org.opendaylight.protocol.bmp.api.BmpDispatcher;
import org.opendaylight.protocol.bmp.api.BmpSessionFactory;
import org.opendaylight.protocol.bmp.api.BmpSessionListenerFactory;
import org.opendaylight.protocol.bmp.spi.registry.BmpMessageRegistry;
import org.opendaylight.protocol.concepts.KeyMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BmpDispatcherImpl implements BmpDispatcher {

    private static final Logger LOG = LoggerFactory.getLogger(BmpDispatcherImpl.class);

    private static final int MAX_CONNECTIONS_COUNT = 128;

    private static final int CONNECT_TIMEOUT = 5000;
    private static final int INITIAL_BACKOFF = 30_000;
    private static final int MAXIMUM_BACKOFF = 720_000;

    private final BmpHandlerFactory hf;
    private final EventLoopGroup bossGroup;
    private final EventLoopGroup workerGroup;
    private final BmpSessionFactory sessionFactory;

    public BmpDispatcherImpl(final EventLoopGroup bossGroup, final EventLoopGroup workerGroup,
            final BmpMessageRegistry registry, final BmpSessionFactory sessionFactory) {
        if (Epoll.isAvailable()) {
            this.bossGroup = new EpollEventLoopGroup();
            this.workerGroup = new EpollEventLoopGroup();
        } else {
            this.bossGroup = Preconditions.checkNotNull(bossGroup);
            this.workerGroup = Preconditions.checkNotNull(workerGroup);
        }
        this.hf = new BmpHandlerFactory(Preconditions.checkNotNull(registry));
        this.sessionFactory = Preconditions.checkNotNull(sessionFactory);
    }

    @Override
    public ChannelFuture createClient(final InetSocketAddress address, final BmpSessionListenerFactory slf,
            final Optional<KeyMapping> keys) {

        final Bootstrap b = new Bootstrap();

        Preconditions.checkNotNull(address);

        if (Epoll.isAvailable()) {
            b.channel(EpollSocketChannel.class);
        } else {
            b.channel(NioSocketChannel.class);
        }
        if (keys.isPresent()) {
            if (Epoll.isAvailable()) {
                b.option(EpollChannelOption.TCP_MD5SIG, keys.get());
            } else {
                throw new UnsupportedOperationException(Epoll.unavailabilityCause().getCause());
            }
        }
        b.option(ChannelOption.SO_KEEPALIVE, true);
        b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT);
        b.group(this.workerGroup);

        b.handler(new ChannelInitializer<AbstractChannel>() {
            @Override
            protected void initChannel(final AbstractChannel ch) throws Exception {
                ch.pipeline().addLast(BmpDispatcherImpl.this.hf.getDecoders());
                ch.pipeline().addLast(BmpDispatcherImpl.this.sessionFactory.getSession(ch, slf));
            }
        });

        b.remoteAddress(address);
        final ChannelFuture channelPromise = b.connect();
        channelPromise.addListener(new BmpDispatcherImpl.BootstrapListener(b, address));
        return channelPromise;
    }

    @Override
    public ChannelFuture createServer(final InetSocketAddress address, final BmpSessionListenerFactory slf,
            final Optional<KeyMapping> keys) {
        Preconditions.checkNotNull(address);
        Preconditions.checkNotNull(slf);

        final ServerBootstrap b = new ServerBootstrap();
        b.childHandler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(final Channel ch) throws Exception {
                ch.pipeline().addLast(BmpDispatcherImpl.this.hf.getDecoders());
                ch.pipeline().addLast(BmpDispatcherImpl.this.sessionFactory.getSession(ch, slf));
            }
        });

        b.option(ChannelOption.SO_BACKLOG, MAX_CONNECTIONS_COUNT);
        b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

        if (Epoll.isAvailable()) {
            b.channel(EpollServerSocketChannel.class);
        } else {
            b.channel(NioServerSocketChannel.class);
        }

        if (keys.isPresent()) {
            if (Epoll.isAvailable()) {
                b.option(EpollChannelOption.TCP_MD5SIG, keys.get());
            } else {
                throw new UnsupportedOperationException(Epoll.unavailabilityCause().getCause());
            }
        }
        b.group(this.bossGroup, this.workerGroup);
        final ChannelFuture f = b.bind(address);

        LOG.debug("Initiated BMP server {} at {}.", f, address);
        return f;
    }

    @Override
    public void close() {
        if (Epoll.isAvailable()) {
            this.workerGroup.shutdownGracefully().awaitUninterruptibly();
            this.bossGroup.shutdownGracefully().awaitUninterruptibly();
        }
    }

    private class BootstrapListener implements ChannelFutureListener {

        private final Bootstrap bootstrap;

        private long delay;

        private final InetSocketAddress address;

        public BootstrapListener(final Bootstrap bootstrap, final InetSocketAddress address) {
            this.bootstrap = bootstrap;
            this.address = address;
            this.delay = INITIAL_BACKOFF;
        }

        @Override
        public void operationComplete(final ChannelFuture cf) throws Exception {
            if (cf.isCancelled()) {
                LOG.debug("Connection {} cancelled!", cf);
            } else if (cf.isSuccess()) {
                LOG.debug("Connection {} succeeded!", cf);
            } else {
                if (this.delay > MAXIMUM_BACKOFF) {
                    LOG.warn(
                            "The time of maximum backoff has been exceeded. No further connection attempts with BMP router {}.",
                            this.address);
                    cf.cancel(false);
                    return;
                }
                final EventLoop loop = cf.channel().eventLoop();
                loop.schedule(() -> BootstrapListener.this.bootstrap.connect().addListener(BootstrapListener.this),
                        this.delay, TimeUnit.MILLISECONDS);
                LOG.info(
                        "The connection try to BMP router {} failed. Next reconnection attempt in {} milliseconds.",
                        this.address, this.delay);
                this.delay *= 2;
            }
        }
    }
}