Migrate pcep to MD-SAL APIs
[bgpcep.git] / pcep / impl / src / main / java / org / opendaylight / protocol / pcep / impl / PCEPProtocolSessionPromise.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.pcep.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Preconditions;
13 import io.netty.bootstrap.Bootstrap;
14 import io.netty.channel.ChannelFuture;
15 import io.netty.channel.ChannelFutureListener;
16 import io.netty.channel.ChannelOption;
17 import io.netty.channel.EventLoop;
18 import io.netty.util.concurrent.DefaultPromise;
19 import io.netty.util.concurrent.EventExecutor;
20 import io.netty.util.concurrent.Future;
21 import io.netty.util.concurrent.Promise;
22 import java.net.InetSocketAddress;
23 import java.util.concurrent.TimeUnit;
24 import javax.annotation.concurrent.GuardedBy;
25 import javax.annotation.concurrent.ThreadSafe;
26 import org.opendaylight.protocol.pcep.PCEPSession;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 @ThreadSafe
31 public final class PCEPProtocolSessionPromise<S extends PCEPSession> extends DefaultPromise<S> {
32     private static final Logger LOG = LoggerFactory.getLogger(PCEPProtocolSessionPromise.class);
33     private InetSocketAddress address;
34     private final int retryTimer;
35     private final int connectTimeout;
36     private final Bootstrap b;
37     @GuardedBy("this")
38     private Future<?> pending;
39
40     PCEPProtocolSessionPromise(final EventExecutor executor, final InetSocketAddress address,
41             final int retryTimer, final int connectTimeout, final Bootstrap b) {
42         super(executor);
43         this.address = requireNonNull(address);
44         this.retryTimer = retryTimer;
45         this.connectTimeout = connectTimeout;
46         this.b = requireNonNull(b);
47     }
48
49     synchronized void connect() {
50         final PCEPProtocolSessionPromise<?> lock = this;
51
52         try {
53             LOG.debug("Promise {} attempting connect for {}ms", lock, this.connectTimeout);
54             if (this.address.isUnresolved()) {
55                 this.address = new InetSocketAddress(this.address.getHostName(), this.address.getPort());
56             }
57
58             this.b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, this.connectTimeout);
59             this.b.remoteAddress(this.address);
60             final ChannelFuture connectFuture = this.b.connect();
61             connectFuture.addListener(new BootstrapConnectListener());
62             this.pending = connectFuture;
63         } catch (Exception e) {
64             LOG.info("Failed to connect to {}", this.address, e);
65             this.setFailure(e);
66         }
67     }
68
69     @Override
70     public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
71         if (super.cancel(mayInterruptIfRunning)) {
72             this.pending.cancel(mayInterruptIfRunning);
73             return true;
74         }
75
76         return false;
77     }
78
79     @Override
80     public synchronized Promise<S> setSuccess(final S result) {
81         LOG.debug("Promise {} completed", this);
82         return super.setSuccess(result);
83     }
84
85     private class BootstrapConnectListener implements ChannelFutureListener {
86         @Override
87         public void operationComplete(final ChannelFuture cf) {
88             synchronized (PCEPProtocolSessionPromise.this) {
89                 PCEPProtocolSessionPromise.LOG.debug("Promise {} connection resolved",
90                         PCEPProtocolSessionPromise.this);
91                 Preconditions.checkState(PCEPProtocolSessionPromise.this.pending.equals(cf));
92                 if (PCEPProtocolSessionPromise.this.isCancelled()) {
93                     if (cf.isSuccess()) {
94                         PCEPProtocolSessionPromise.LOG.debug("Closing channel for cancelled promise {}",
95                                 PCEPProtocolSessionPromise.this);
96                         cf.channel().close();
97                     }
98                 } else if (cf.isSuccess()) {
99                     PCEPProtocolSessionPromise.LOG.debug("Promise {} connection successful",
100                             PCEPProtocolSessionPromise.this);
101                 } else {
102                     PCEPProtocolSessionPromise.LOG.debug("Attempt to connect to {} failed", 
103                             PCEPProtocolSessionPromise.this.address, cf.cause());
104
105                     if (PCEPProtocolSessionPromise.this.retryTimer == 0) {
106                         PCEPProtocolSessionPromise.LOG
107                                 .debug("Retry timer value is 0. Reconnection will not be attempted");
108                         PCEPProtocolSessionPromise.this.setFailure(cf.cause());
109                         return;
110                     }
111
112                     final EventLoop loop = cf.channel().eventLoop();
113                     loop.schedule(() -> {
114                         synchronized (PCEPProtocolSessionPromise.this) {
115                             PCEPProtocolSessionPromise.LOG.debug("Attempting to connect to {}",
116                                     PCEPProtocolSessionPromise.this.address);
117                             final Future<Void> reconnectFuture = PCEPProtocolSessionPromise.this.b.connect();
118                             reconnectFuture.addListener(BootstrapConnectListener.this);
119                             PCEPProtocolSessionPromise.this.pending = reconnectFuture;
120                         }
121                     }, PCEPProtocolSessionPromise.this.retryTimer, TimeUnit.SECONDS);
122                     PCEPProtocolSessionPromise.LOG.debug("Next reconnection attempt in {}s",
123                             PCEPProtocolSessionPromise.this.retryTimer);
124                 }
125             }
126         }
127     }
128 }