Upgrade ietf-{inet,yang}-types to 2013-07-15
[bgpcep.git] / bgp / bmp-impl / src / main / java / org / opendaylight / protocol / bmp / impl / BmpDispatcherImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.protocol.bmp.impl;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import io.netty.bootstrap.Bootstrap;
14 import io.netty.bootstrap.ServerBootstrap;
15 import io.netty.buffer.PooledByteBufAllocator;
16 import io.netty.channel.Channel;
17 import io.netty.channel.ChannelFuture;
18 import io.netty.channel.ChannelFutureListener;
19 import io.netty.channel.ChannelInitializer;
20 import io.netty.channel.ChannelOption;
21 import io.netty.channel.EventLoop;
22 import io.netty.channel.EventLoopGroup;
23 import io.netty.channel.nio.NioEventLoopGroup;
24 import io.netty.channel.socket.nio.NioServerSocketChannel;
25 import io.netty.channel.socket.nio.NioSocketChannel;
26 import java.net.InetSocketAddress;
27 import java.util.concurrent.TimeUnit;
28 import org.opendaylight.protocol.bmp.api.BmpDispatcher;
29 import org.opendaylight.protocol.bmp.api.BmpSessionFactory;
30 import org.opendaylight.protocol.bmp.api.BmpSessionListenerFactory;
31 import org.opendaylight.protocol.bmp.spi.registry.BmpMessageRegistry;
32 import org.opendaylight.tcpmd5.api.KeyMapping;
33 import org.opendaylight.tcpmd5.netty.MD5ChannelFactory;
34 import org.opendaylight.tcpmd5.netty.MD5ChannelOption;
35 import org.opendaylight.tcpmd5.netty.MD5NioSocketChannel;
36 import org.opendaylight.tcpmd5.netty.MD5ServerChannelFactory;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 public class BmpDispatcherImpl implements BmpDispatcher {
41
42     private static final Logger LOG = LoggerFactory.getLogger(BmpDispatcherImpl.class);
43
44     private static final int MAX_CONNECTIONS_COUNT = 128;
45
46     private static final int CONNECT_TIMEOUT = 5000;
47     private static final int INITIAL_BACKOFF = 30_000;
48     private static final int MAXIMUM_BACKOFF = 720_000;
49
50     private final BmpHandlerFactory hf;
51     private final EventLoopGroup bossGroup;
52     private final EventLoopGroup workerGroup;
53     private final BmpSessionFactory sessionFactory;
54     private final Optional<MD5ServerChannelFactory<?>> md5ServerChFactory;
55     private final Optional<MD5ChannelFactory<?>> md5ChannelFactory;
56
57     public BmpDispatcherImpl(final EventLoopGroup bossGroup, final EventLoopGroup workerGroup,
58             final BmpMessageRegistry registry, final BmpSessionFactory sessionFactory) {
59         this(bossGroup, workerGroup, registry, sessionFactory, Optional.<MD5ChannelFactory<?>>absent(), Optional.<MD5ServerChannelFactory<?>>absent());
60     }
61
62     public BmpDispatcherImpl(final EventLoopGroup bossGroup, final EventLoopGroup workerGroup,
63             final BmpMessageRegistry registry, final BmpSessionFactory sessionFactory,
64             final Optional<MD5ChannelFactory<?>> cf, final Optional<MD5ServerChannelFactory<?>> scf) {
65         this.bossGroup = Preconditions.checkNotNull(bossGroup);
66         this.workerGroup = Preconditions.checkNotNull(workerGroup);
67         this.hf = new BmpHandlerFactory(Preconditions.checkNotNull(registry));
68         this.sessionFactory = Preconditions.checkNotNull(sessionFactory);
69         this.md5ServerChFactory = Preconditions.checkNotNull(scf);
70         this.md5ChannelFactory  = Preconditions.checkNotNull(cf);
71     }
72
73     @Override
74     public ChannelFuture createClient(final InetSocketAddress address, final BmpSessionListenerFactory slf, final Optional<KeyMapping> keys) {
75
76         final NioEventLoopGroup workergroup = new NioEventLoopGroup();
77         final Bootstrap b = new Bootstrap();
78
79         Preconditions.checkNotNull(address);
80
81         if ( keys.isPresent() ) {
82             b.channel(MD5NioSocketChannel.class);
83             b.option(MD5ChannelOption.TCP_MD5SIG, keys.get());
84         } else {
85             LOG.info("no md5 key is not found. continue with bootstrap setup.");
86             b.channel(NioSocketChannel.class);
87         }
88         b.option(ChannelOption.SO_KEEPALIVE, true);
89         b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECT_TIMEOUT);
90         b.group(workergroup);
91
92         b.handler(new ChannelInitializer<NioSocketChannel>() {
93             @Override
94             protected void initChannel(final NioSocketChannel ch) throws Exception {
95                 ch.pipeline().addLast(BmpDispatcherImpl.this.hf.getDecoders());
96                 ch.pipeline().addLast(BmpDispatcherImpl.this.sessionFactory.getSession(ch, slf));
97             }
98         });
99
100         b.remoteAddress(address);
101         final ChannelFuture channelPromise = b.connect();
102         channelPromise.addListener(new BmpDispatcherImpl.BootstrapListener(b, address));
103         return channelPromise;
104     }
105
106     @Override
107     public ChannelFuture createServer(final InetSocketAddress address, final BmpSessionListenerFactory slf, final Optional<KeyMapping> keys) {
108         Preconditions.checkNotNull(address);
109         Preconditions.checkNotNull(slf);
110
111         final ServerBootstrap b = new ServerBootstrap();
112         b.childHandler(new ChannelInitializer<Channel>() {
113             @Override
114             protected void initChannel(final Channel ch) throws Exception {
115                 ch.pipeline().addLast(BmpDispatcherImpl.this.hf.getDecoders());
116                 ch.pipeline().addLast(BmpDispatcherImpl.this.sessionFactory.getSession(ch, slf));
117             }
118         });
119
120         b.option(ChannelOption.SO_BACKLOG, MAX_CONNECTIONS_COUNT);
121         b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
122
123         if (keys.isPresent()) {
124             Preconditions.checkState(this.md5ServerChFactory.isPresent(), "No server channel factory instance available,  cannot use key mapping.");
125             b.channelFactory(this.md5ServerChFactory.get());
126             final KeyMapping key = keys.get();
127             b.option(MD5ChannelOption.TCP_MD5SIG, key);
128             LOG.debug("Adding MD5 keys {} to boostrap {}", key, b);
129         } else {
130             b.channel(NioServerSocketChannel.class);
131         }
132         b.group(this.bossGroup, this.workerGroup);
133         final ChannelFuture f = b.bind(address);
134
135         LOG.debug("Initiated BMP server {} at {}.", f, address);
136         return f;
137     }
138
139     @Override
140     public void close() {
141     }
142
143     private class BootstrapListener implements ChannelFutureListener {
144
145         private final Bootstrap bootstrap;
146
147         private long delay;
148
149         private final InetSocketAddress address;
150
151         public BootstrapListener(final Bootstrap bootstrap, final InetSocketAddress address) {
152             this.bootstrap = bootstrap;
153             this.address = address;
154             this.delay = INITIAL_BACKOFF;
155         }
156
157         @Override
158         public void operationComplete(final ChannelFuture cf) throws Exception {
159             if (cf.isCancelled()) {
160                 LOG.debug("Connection {} cancelled!", cf);
161             } else if (cf.isSuccess()) {
162                 LOG.debug("Connection {} succeeded!", cf);
163             } else {
164                 if (delay > MAXIMUM_BACKOFF) {
165                     LOG.warn("The time of maximum backoff has been exceeded. No further connection attempts with BMP router {}.", this.address);
166                     cf.cancel(false);
167                     return;
168                 }
169                 final EventLoop loop = cf.channel().eventLoop();
170                 loop.schedule(new Runnable() {
171                     @Override
172                     public void run() {
173                         BootstrapListener.this.bootstrap.connect().addListener(BootstrapListener.this);
174                     }
175                 }, this.delay, TimeUnit.MILLISECONDS);
176                 LOG.info("The connection try to BMP router {} failed. Next reconnection attempt in {} milliseconds.", this.address, this.delay);
177                 this.delay *= 2;
178             }
179         }
180     }
181 }