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