Add custom EXI buffer management
[netconf.git] / netconf / netconf-netty-util / src / main / java / org / opendaylight / netconf / nettyutil / ReconnectPromise.java
1 /*
2  * Copyright (c) 2013 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.netconf.nettyutil;
9
10 import com.google.common.base.Preconditions;
11 import io.netty.bootstrap.Bootstrap;
12 import io.netty.channel.ChannelHandlerContext;
13 import io.netty.channel.ChannelInboundHandlerAdapter;
14 import io.netty.util.concurrent.DefaultPromise;
15 import io.netty.util.concurrent.EventExecutor;
16 import io.netty.util.concurrent.Future;
17 import java.net.InetSocketAddress;
18 import org.opendaylight.netconf.api.NetconfSession;
19 import org.opendaylight.netconf.api.NetconfSessionListener;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 @Deprecated
24 final class ReconnectPromise<S extends NetconfSession, L extends NetconfSessionListener<? super S>>
25         extends DefaultPromise<Void> {
26     private static final Logger LOG = LoggerFactory.getLogger(ReconnectPromise.class);
27
28     private final AbstractNetconfDispatcher<S, L> dispatcher;
29     private final InetSocketAddress address;
30     private final ReconnectStrategyFactory strategyFactory;
31     private final Bootstrap bootstrap;
32     private final AbstractNetconfDispatcher.PipelineInitializer<S> initializer;
33     private Future<?> pending;
34
35     ReconnectPromise(final EventExecutor executor, final AbstractNetconfDispatcher<S, L> dispatcher,
36             final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory,
37             final Bootstrap bootstrap, final AbstractNetconfDispatcher.PipelineInitializer<S> initializer) {
38         super(executor);
39         this.bootstrap = bootstrap;
40         this.initializer = Preconditions.checkNotNull(initializer);
41         this.dispatcher = Preconditions.checkNotNull(dispatcher);
42         this.address = Preconditions.checkNotNull(address);
43         this.strategyFactory = Preconditions.checkNotNull(connectStrategyFactory);
44     }
45
46     synchronized void connect() {
47         final ReconnectStrategy cs = this.strategyFactory.createReconnectStrategy();
48
49         // Set up a client with pre-configured bootstrap, but add a closed channel handler into the pipeline to support
50         // reconnect attempts
51         pending = this.dispatcher.createClient(this.address, cs, bootstrap, (channel, promise) -> {
52             initializer.initializeChannel(channel, promise);
53             // add closed channel handler
54             // This handler has to be added as last channel handler and the channel inactive event has to be caught by
55             // it
56             // Handlers in front of it can react to channelInactive event, but have to forward the event or the
57             // reconnect will not work
58             // This handler is last so all handlers in front of it can handle channel inactive (to e.g. resource
59             // cleanup) before a new connection is started
60             channel.pipeline().addLast(new ClosedChannelHandler(ReconnectPromise.this));
61         });
62
63         pending.addListener(future -> {
64             if (!future.isSuccess() && !ReconnectPromise.this.isDone()) {
65                 ReconnectPromise.this.setFailure(future.cause());
66             }
67         });
68     }
69
70     /**
71      * Indicate if the initial connection succeeded.
72      *
73      * @return true if initial connection was established successfully, false if initial connection failed due to e.g.
74      *         Connection refused, Negotiation failed
75      */
76     private synchronized boolean isInitialConnectFinished() {
77         Preconditions.checkNotNull(pending);
78         return pending.isDone() && pending.isSuccess();
79     }
80
81     @Override
82     public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
83         if (super.cancel(mayInterruptIfRunning)) {
84             Preconditions.checkNotNull(pending);
85             this.pending.cancel(mayInterruptIfRunning);
86             return true;
87         }
88
89         return false;
90     }
91
92     /**
93      * Channel handler that responds to channelInactive event and reconnects the session.
94      * Only if the promise was not canceled.
95      */
96     private static final class ClosedChannelHandler extends ChannelInboundHandlerAdapter {
97         private final ReconnectPromise<?, ?> promise;
98
99         ClosedChannelHandler(final ReconnectPromise<?, ?> promise) {
100             this.promise = promise;
101         }
102
103         @Override
104         public void channelInactive(final ChannelHandlerContext ctx) {
105             // This is the ultimate channel inactive handler, not forwarding
106             if (promise.isCancelled()) {
107                 return;
108             }
109
110             if (promise.isInitialConnectFinished() == false) {
111                 LOG.debug("Connection to {} was dropped during negotiation, reattempting", promise.address);
112             }
113
114             LOG.debug("Reconnecting after connection to {} was dropped", promise.address);
115             promise.connect();
116         }
117     }
118 }