Update MRI projects for Aluminium
[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.MoreExecutors;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Optional;
18 import java.util.Set;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.function.Function;
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
38     private static final Logger LOG = LoggerFactory.getLogger(StalePassiveConnectionService.class);
39
40     private final Map<OvsdbClient, Set<OvsdbClient>> pendingClients = new ConcurrentHashMap<>();
41     private final Function<OvsdbClient, Void> clientNotificationCallback;
42
43     public StalePassiveConnectionService(Function<OvsdbClient, Void> clientNotificationCallback) {
44         this.clientNotificationCallback = clientNotificationCallback;
45     }
46
47     public Map<OvsdbClient, Set<OvsdbClient>> getPendingClients() {
48         return new HashMap<>(pendingClients);
49     }
50
51     /**
52      * This method makes sure that all stale connections from the same node are properly cleaned up before processing
53      * new connection request.
54      *
55      * @param newOvsdbClient the connecting OvsdbClient
56      * @param clientsFromSameNode list of existing OvsdbClients from the same node as the new OvsdbClient
57      */
58     public void handleNewPassiveConnection(final OvsdbClient newOvsdbClient,
59                                            final List<OvsdbClient> clientsFromSameNode) {
60         LOG.info("Adding client to pending list {}", newOvsdbClient.getConnectionInfo());
61         pendingClients.put(newOvsdbClient, new HashSet<>());
62         /*
63             if old client echo succeeds
64                do not notify new client as it has to wait
65             else
66                 if all old clients got disconnected/echo failed notify the new client
67          */
68         for (final OvsdbClient oldClient : clientsFromSameNode) {
69             pendingClients.get(newOvsdbClient).add(oldClient);
70             LOG.info("Echo testing client {}", oldClient.getConnectionInfo());
71             Futures.addCallback(oldClient.echo(),
72                 new FutureCallback<List<String>>() {
73                     @Override
74                     public void onSuccess(List<String> result) {
75                         //old client still active
76                         LOG.info("Echo testing of old client {} succeeded", oldClient.getConnectionInfo());
77                     }
78
79                     @Override
80                     public void onFailure(Throwable throwable) {
81                         LOG.info("Echo testing of old client {} failed, disconnect and notify clients",
82                                 oldClient.getConnectionInfo());
83                         //disconnect the old client to cleanup, so that new connection can proceed
84                         oldClient.disconnect();
85                         onInactiveClient(oldClient);
86                     }
87                 }, MoreExecutors.directExecutor());
88         }
89     }
90
91     /**
92      * Notify the service that the given client has disconnected.
93      * @param disconnectedClient the client just disconnected
94      */
95     public synchronized void clientDisconnected(OvsdbClient disconnectedClient) {
96         LOG.info("Client disconnected {}", disconnectedClient.getConnectionInfo());
97         onInactiveClient(disconnectedClient);
98     }
99
100     public synchronized void onInactiveClient(OvsdbClient disconnectedClient) {
101         pendingClients.remove(disconnectedClient);
102         pendingClients.entrySet().stream().forEach(entry -> entry.getValue().remove(disconnectedClient));
103         Optional<OvsdbClient> clientOptional = pendingClients.entrySet().stream()
104                 .filter(entry -> entry.getValue().isEmpty())
105                 .map(entry -> entry.getKey())
106                 .findFirst();
107         if (clientOptional.isPresent()) {
108             LOG.info("Sending notification for client {}", clientOptional.get().getConnectionInfo());
109             pendingClients.remove(clientOptional.get());
110             clientNotificationCallback.apply(clientOptional.get());
111         }
112     }
113
114     @Override
115     public void close() {
116     }
117 }