Code clean up
[bgpcep.git] / bmp / 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 static java.util.Objects.requireNonNull;
12 import static org.opendaylight.protocol.bmp.impl.BmpDispatcherUtil.createClientBootstrap;
13 import static org.opendaylight.protocol.bmp.impl.BmpDispatcherUtil.createServerBootstrap;
14
15 import io.netty.bootstrap.Bootstrap;
16 import io.netty.bootstrap.ServerBootstrap;
17 import io.netty.channel.ChannelFuture;
18 import io.netty.channel.ChannelFutureListener;
19 import io.netty.channel.EventLoop;
20 import io.netty.channel.EventLoopGroup;
21 import io.netty.channel.epoll.Epoll;
22 import io.netty.channel.epoll.EpollEventLoopGroup;
23 import java.net.InetSocketAddress;
24 import java.util.Timer;
25 import java.util.TimerTask;
26 import java.util.concurrent.TimeUnit;
27 import javax.annotation.concurrent.GuardedBy;
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.protocol.concepts.KeyMapping;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 public class BmpDispatcherImpl implements BmpDispatcher {
37
38     private static final Logger LOG = LoggerFactory.getLogger(BmpDispatcherImpl.class);
39
40     private static final int CONNECT_TIMEOUT = 5000;
41     private static final int INITIAL_BACKOFF = 30_000;
42     private static final int MAXIMUM_BACKOFF = 720_000;
43     private static final long TIMEOUT = 10;
44
45     private final BmpHandlerFactory hf;
46     private final EventLoopGroup bossGroup;
47     private final EventLoopGroup workerGroup;
48     private final BmpSessionFactory sessionFactory;
49     @GuardedBy("this")
50     private boolean close;
51
52     public BmpDispatcherImpl(final EventLoopGroup bossGroup, final EventLoopGroup workerGroup,
53             final BmpMessageRegistry registry, final BmpSessionFactory sessionFactory) {
54         if (Epoll.isAvailable()) {
55             this.bossGroup = new EpollEventLoopGroup();
56             this.workerGroup = new EpollEventLoopGroup();
57         } else {
58             this.bossGroup = requireNonNull(bossGroup);
59             this.workerGroup = requireNonNull(workerGroup);
60         }
61         this.hf = new BmpHandlerFactory(requireNonNull(registry));
62         this.sessionFactory = requireNonNull(sessionFactory);
63     }
64
65     @Override
66     public ChannelFuture createClient(final InetSocketAddress remoteAddress, final BmpSessionListenerFactory slf,
67             final KeyMapping keys) {
68         final Bootstrap bootstrap = createClientBootstrap(this.sessionFactory, this.hf,
69                 BmpDispatcherUtil::createChannelWithDecoder, slf, remoteAddress, this.workerGroup,
70                 CONNECT_TIMEOUT, keys);
71         final ChannelFuture channelPromise = bootstrap.connect();
72         channelPromise.addListener(new BootstrapListener(bootstrap, remoteAddress, slf, keys));
73         LOG.debug("Initiated BMP Client {} at {}.", channelPromise, remoteAddress);
74         return channelPromise;
75     }
76
77     @Override
78     public ChannelFuture createServer(final InetSocketAddress address, final BmpSessionListenerFactory slf,
79             final KeyMapping keys) {
80         final ServerBootstrap serverBootstrap = createServerBootstrap(this.sessionFactory, this.hf, slf,
81                 BmpDispatcherUtil::createChannelWithDecoder, this.bossGroup, this.workerGroup, keys);
82         final ChannelFuture channelFuture = serverBootstrap.bind(address);
83         LOG.debug("Initiated BMP server {} at {}.", channelFuture, address);
84         return channelFuture;
85     }
86
87     @Override
88     public synchronized void close() {
89         this.close = true;
90         if (Epoll.isAvailable()) {
91             this.workerGroup.shutdownGracefully(0, TIMEOUT, TimeUnit.SECONDS);
92             this.bossGroup.shutdownGracefully(0, TIMEOUT, TimeUnit.SECONDS);
93         }
94     }
95
96     private class BootstrapListener implements ChannelFutureListener {
97         private final Bootstrap bootstrap;
98         private final InetSocketAddress remoteAddress;
99         private final BmpSessionListenerFactory slf;
100         private final KeyMapping keys;
101         private long delay;
102         private final Timer timer = new Timer();
103
104         BootstrapListener(final Bootstrap bootstrap,
105                 final InetSocketAddress remoteAddress, final BmpSessionListenerFactory slf, final KeyMapping keys) {
106             this.bootstrap = bootstrap;
107             this.remoteAddress = remoteAddress;
108             this.delay = INITIAL_BACKOFF;
109             this.slf = slf;
110             this.keys = keys;
111         }
112
113         @Override
114         public void operationComplete(final ChannelFuture future) throws Exception {
115             if (future.isCancelled()) {
116                 LOG.debug("Connection {} cancelled!", future);
117             } else if (future.isSuccess()) {
118                 LOG.debug("Connection {} succeeded!", future);
119                 future.channel().closeFuture().addListener((ChannelFutureListener) channelFuture -> scheduleConnect());
120             } else {
121                 if (this.delay > MAXIMUM_BACKOFF) {
122                     LOG.warn("The time of maximum backoff has been exceeded. No further connection attempts with BMP "
123                             + "router {}.", this.remoteAddress);
124                     future.cancel(false);
125                     return;
126                 }
127                 final EventLoop loop = future.channel().eventLoop();
128                 loop.schedule(() -> this.bootstrap.connect().addListener(this), this.delay, TimeUnit.MILLISECONDS);
129                 LOG.info("The connection try to BMP router {} failed. Next reconnection attempt in {} milliseconds.",
130                         this.remoteAddress, this.delay);
131                 this.delay *= 2;
132             }
133         }
134
135         private void scheduleConnect() {
136             if (!BmpDispatcherImpl.this.close) {
137                 timer.schedule(new TimerTask() {
138                     @Override
139                     public void run() {
140                         createClient(BootstrapListener.this.remoteAddress, BootstrapListener.this.slf,
141                                 BootstrapListener.this.keys);
142                     }
143                 }, (long) 5);
144             }
145         }
146     }
147 }