Fix findbugs violations in library
[ovsdb.git] / library / impl / src / main / java / org / opendaylight / ovsdb / lib / impl / StalePassiveConnectionService.java
1 /*
2  * Copyright © 2016, 2017 NEC Corporation 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.ovsdb.lib.impl;
9
10 import com.google.common.util.concurrent.FutureCallback;
11 import com.google.common.util.concurrent.Futures;
12 import com.google.common.util.concurrent.SettableFuture;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Map.Entry;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.Future;
18 import java.util.concurrent.ScheduledExecutorService;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21 import org.opendaylight.ovsdb.lib.OvsdbClient;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * StalePassiveConnectionService provides functionalities to clean up stale passive connections
27  * from the same node before new connection request arrives, especially for connection flapping scenarios.
28  *
29  * <p>When new connection arrives all connections from the same node are pinged. The pings cause
30  * the stale netty connections to close due to IOException. Those have not been closed after a timeout
31  * will be closed programmatically. New connection request handling is then proceeded after all
32  * stale connections are cleaned up in the OvsdbConnectionService
33  *
34  * @author Vinh Nguyen (vinh.nguyen@hcl.com) on 6/10/16.
35  */
36 public class StalePassiveConnectionService implements AutoCloseable {
37     private static final Logger LOG = LoggerFactory.getLogger(StalePassiveConnectionService.class);
38
39     private static Map<OvsdbClient, Map<OvsdbClient, SettableFuture<List<String>>>> pendingConnectionClients =
40             new ConcurrentHashMap<>();
41
42     private final ScheduledExecutorService executorService;
43     private static final int ECHO_TIMEOUT = 10;
44
45     public StalePassiveConnectionService(final ScheduledExecutorService executorService) {
46         this.executorService = executorService;
47     }
48
49     /**
50      * This method makes sure that all stale connections from the same node are properly cleaned up before processing
51      * new connection request.
52      *
53      * @param newOvsdbClient the connecting OvsdbClient
54      * @param clientsFromSameNode list of existing OvsdbClients from the same node as the new OvsdbClient
55      */
56     public void handleNewPassiveConnection(final OvsdbClient newOvsdbClient,
57                                            final List<OvsdbClient> clientsFromSameNode) {
58         final Map<OvsdbClient, SettableFuture<List<String>>> clientFutureMap = new ConcurrentHashMap<>();
59         pendingConnectionClients.put(newOvsdbClient, clientFutureMap);
60
61         // scheduled task for ping response timeout. Connections that don't response to the
62         // ping or haven't disconnected after the timeout will be closed
63         final ScheduledFuture<?> echoTimeoutFuture =
64                 executorService.schedule(() -> {
65                     for (Entry<OvsdbClient, SettableFuture<List<String>>> entry : clientFutureMap.entrySet()) {
66                         OvsdbClient client = entry.getKey();
67                         Future<?> clientFuture = entry.getValue();
68                         if (!clientFuture.isDone() && !clientFuture.isCancelled()) {
69                             clientFuture.cancel(true);
70                         }
71                         if (client.isActive()) {
72                             client.disconnect();
73                         }
74                     }
75                 }, ECHO_TIMEOUT, TimeUnit.SECONDS);
76
77         // for every connection create a SettableFuture, save it to 'clientFutureMap', and send a ping (echo).
78         // The ping results in either:
79         // 1. ping response returns - the connection is active
80         // 2. the netty connection is closed due to IO exception -
81         // The future is removed from the 'clientFutureMap' when the onSuccess event for each future arrives
82         // If the map is empty we proceed with new connection process
83         for (final OvsdbClient client : clientsFromSameNode) {
84             SettableFuture<List<String>> clientFuture = SettableFuture.create();
85             clientFutureMap.put(client, clientFuture);
86             Futures.addCallback(clientFuture,
87                     createStaleConnectionFutureCallback(client, newOvsdbClient, clientFutureMap, echoTimeoutFuture));
88             Futures.addCallback(client.echo(),
89                     createStaleConnectionFutureCallback(client, newOvsdbClient, clientFutureMap, echoTimeoutFuture));
90         }
91     }
92
93     /**
94      * Notify the service that the given client has disconnected.
95      * @param disconnectedClient the client just disconnected
96      */
97     public void clientDisconnected(OvsdbClient disconnectedClient) {
98         for (Entry<OvsdbClient, Map<OvsdbClient, SettableFuture<List<String>>>> entry :
99                 pendingConnectionClients.entrySet()) {
100             OvsdbClient pendingClient = entry.getKey();
101
102             // set the future result for pending connections that wait for this client to be disconnected
103             if (pendingClient.getConnectionInfo().getRemoteAddress()
104                     .equals(disconnectedClient.getConnectionInfo().getRemoteAddress())) {
105                 Map<OvsdbClient, SettableFuture<List<String>>> clientFutureMap = entry.getValue();
106                 SettableFuture<List<String>> future = clientFutureMap.get(disconnectedClient);
107                 if (future != null) {
108                     future.set(null);
109                 }
110             }
111         }
112     }
113
114     @Override
115     public void close() {
116     }
117
118     private FutureCallback<List<String>> createStaleConnectionFutureCallback(
119             final OvsdbClient cbForClient, final OvsdbClient newClient,
120             final Map<OvsdbClient, SettableFuture<List<String>>> clientFutureMap,
121             final ScheduledFuture<?> echoTimeoutFuture) {
122         return new FutureCallback<List<String>>() {
123             @Override
124             public void onSuccess(List<String> result) {
125                 // The future is removed from the 'clientFutureMap' when the onSuccess event for each future arrives
126                 // If the map is empty we proceed with new connection process
127                 clientFutureMap.remove(cbForClient);
128                 if (clientFutureMap.isEmpty()) {
129                     if (!echoTimeoutFuture.isDone() && !echoTimeoutFuture.isCancelled()) {
130                         echoTimeoutFuture.cancel(true);
131                     }
132                     OvsdbConnectionService.notifyListenerForPassiveConnection(newClient);
133                     pendingConnectionClients.remove(newClient);
134                 }
135             }
136
137             @Override
138             public void onFailure(Throwable throwable) {
139                 LOG.error("Error in checking stale connections)", throwable);
140             }
141         };
142     }
143 }