Merge "Bug 6088 - Threads problem on gathering statistics"
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / connection / ConnectionContextImpl.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
9 package org.opendaylight.openflowplugin.impl.connection;
10
11 import com.google.common.base.Preconditions;
12 import java.math.BigInteger;
13 import java.net.InetSocketAddress;
14 import java.util.concurrent.Callable;
15 import java.util.concurrent.ExecutionException;
16 import java.util.concurrent.Executors;
17 import java.util.concurrent.Future;
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.TimeoutException;
20 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
21 import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
22 import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueueHandlerRegistration;
23 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
24 import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
25 import org.opendaylight.openflowplugin.api.openflow.connection.OutboundQueueProvider;
26 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
27 import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceDisconnectedHandler;
28 import org.opendaylight.openflowplugin.impl.statistics.ofpspecific.SessionStatistics;
29 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
34 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  *
40  */
41 public class ConnectionContextImpl implements ConnectionContext {
42
43     private final ConnectionAdapter connectionAdapter;
44     private volatile CONNECTION_STATE connectionState;
45     private FeaturesReply featuresReply;
46     private NodeId nodeId;
47     private DeviceDisconnectedHandler deviceDisconnectedHandler;
48     private static final Logger LOG = LoggerFactory.getLogger(ConnectionContextImpl.class);
49     private OutboundQueueProvider outboundQueueProvider;
50     private OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration;
51     private HandshakeContext handshakeContext;
52     private DeviceInfo deviceInfo;
53
54     /**
55      * @param connectionAdapter
56      */
57     public ConnectionContextImpl(final ConnectionAdapter connectionAdapter) {
58         this.connectionAdapter = connectionAdapter;
59     }
60
61     @Override
62     public ConnectionAdapter getConnectionAdapter() {
63         return connectionAdapter;
64     }
65
66     @Override
67     public OutboundQueue getOutboundQueueProvider() {
68         return this.outboundQueueProvider;
69     }
70
71     @Override
72     public void setOutboundQueueProvider(final OutboundQueueProvider outboundQueueProvider) {
73         this.outboundQueueProvider = outboundQueueProvider;
74     }
75
76     @Override
77     public CONNECTION_STATE getConnectionState() {
78         return connectionState;
79     }
80
81     @Override
82     public NodeId getNodeId() {
83         return nodeId;
84     }
85
86     @Override
87     public void setNodeId(final NodeId nodeId) {
88         this.nodeId = nodeId;
89     }
90
91     @Override
92     public FeaturesReply getFeatures() {
93         return featuresReply;
94     }
95
96     @Override
97     public void setDeviceDisconnectedHandler(final DeviceDisconnectedHandler deviceDisconnectedHandler) {
98         this.deviceDisconnectedHandler = deviceDisconnectedHandler;
99     }
100
101     @Override
102     public void setFeatures(final FeaturesReply featuresReply) {
103         this.featuresReply = featuresReply;
104     }
105
106     @Override
107     public void closeConnection(final boolean propagate) {
108         if (null == nodeId){
109             SessionStatistics.countEvent(connectionAdapter.getRemoteAddress().toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_OFP);
110         } else {
111             SessionStatistics.countEvent(nodeId.toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_OFP);
112         }
113         final BigInteger datapathId = featuresReply != null ? featuresReply.getDatapathId() : BigInteger.ZERO;
114         LOG.debug("Actively closing connection: {}, datapathId:{}.",
115                 connectionAdapter.getRemoteAddress(), datapathId);
116         connectionState = ConnectionContext.CONNECTION_STATE.RIP;
117
118         Future<Void> future = Executors.newSingleThreadExecutor().submit(new Callable<Void>() {
119             @Override
120             public Void call() throws Exception {
121                 unregisterOutboundQueue();
122                 return null;
123             }
124         });
125         try {
126             LOG.debug("Waiting 1s for unregistering outbound queue.");
127             future.get(1, TimeUnit.SECONDS);
128             LOG.info("Unregistering outbound queue successful.");
129         } catch (InterruptedException e) {
130             LOG.warn("Unregistering outbound queue was interrupted for node {}", nodeId);
131         } catch (ExecutionException e) {
132             LOG.warn("Unregistering outbound queue throws exception for node {}", nodeId, e);
133         } catch (TimeoutException e) {
134             LOG.warn("Unregistering outbound queue took longer than 1 seconds for node {}", nodeId);
135         }
136
137         closeHandshakeContext();
138
139         if (getConnectionAdapter().isAlive()) {
140             getConnectionAdapter().disconnect();
141         }
142
143         if (propagate) {
144             LOG.debug("Propagating device disconnect for node {}", nodeId);
145             propagateDeviceDisconnectedEvent();
146         } else {
147             LOG.debug("Close connection without propagating for node {}", nodeId);
148         }
149     }
150
151     private void closeHandshakeContext() {
152         LOG.debug("Trying closing handshake context for node {}", nodeId);
153         if (handshakeContext != null) {
154             try {
155                 handshakeContext.close();
156             } catch (Exception e) {
157                 LOG.error("handshake context closing failed:{} ", e);
158             } finally {
159                 handshakeContext = null;
160             }
161         }
162     }
163
164     @Override
165     public void onConnectionClosed() {
166         if (null == nodeId){
167             SessionStatistics.countEvent(connectionAdapter.getRemoteAddress().toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
168         } else {
169             SessionStatistics.countEvent(nodeId.toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
170         }
171         connectionState = ConnectionContext.CONNECTION_STATE.RIP;
172
173         final InetSocketAddress remoteAddress = connectionAdapter.getRemoteAddress();
174         final Short auxiliaryId;
175         if (null != getFeatures() && null != getFeatures().getAuxiliaryId()) {
176             auxiliaryId = getFeatures().getAuxiliaryId();
177         } else {
178             auxiliaryId = 0;
179         }
180
181         LOG.debug("disconnecting: node={}|auxId={}|connection state = {}",
182                 remoteAddress,
183                 auxiliaryId,
184                 getConnectionState());
185
186         unregisterOutboundQueue();
187         closeHandshakeContext();
188         propagateDeviceDisconnectedEvent();
189     }
190
191     private void propagateDeviceDisconnectedEvent() {
192         if (null != deviceDisconnectedHandler) {
193             final BigInteger datapathId = featuresReply != null ? featuresReply.getDatapathId() : BigInteger.ZERO;
194             LOG.debug("Propagating connection closed event: {}, datapathId:{}.",
195                     connectionAdapter.getRemoteAddress(), datapathId);
196             deviceDisconnectedHandler.onDeviceDisconnected(this);
197         }
198     }
199
200     @Override
201     public void setOutboundQueueHandleRegistration(OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration) {
202         this.outboundQueueHandlerRegistration = outboundQueueHandlerRegistration;
203     }
204
205     private void unregisterOutboundQueue() {
206         LOG.debug("Trying unregister outbound queue handler registration for node {}", nodeId);
207         if (outboundQueueHandlerRegistration != null) {
208             outboundQueueHandlerRegistration.close();
209             outboundQueueHandlerRegistration = null;
210         }
211     }
212
213     @Override
214     public synchronized void changeStateToHandshaking() {
215         connectionState = CONNECTION_STATE.HANDSHAKING;
216     }
217
218     @Override
219     public synchronized void changeStateToTimeouting() {
220         connectionState = CONNECTION_STATE.TIMEOUTING;
221     }
222
223     @Override
224     public synchronized void changeStateToWorking() {
225         connectionState = CONNECTION_STATE.WORKING;
226     }
227
228     @Override
229     public DeviceInfo getDeviceInfo() {
230         return this.deviceInfo;
231     }
232
233     @Override
234     public void handshakeSuccessful() {
235         Preconditions.checkNotNull(nodeId, "Cannot create DeviceInfo if 'NodeId' is not set!");
236         Preconditions.checkNotNull(featuresReply, "Cannot create DeviceInfo if 'features' is not set!");
237         this.deviceInfo = new DeviceInfoImpl(
238                 nodeId,
239                 DeviceStateUtil.createNodeInstanceIdentifier(nodeId),
240                 featuresReply.getVersion(),
241                 featuresReply.getDatapathId());
242     }
243
244     @Override
245     public void setHandshakeContext(HandshakeContext handshakeContext) {
246         this.handshakeContext = handshakeContext;
247     }
248
249
250     private class DeviceInfoImpl implements DeviceInfo {
251
252         final private NodeId nodeId;
253         final private KeyedInstanceIdentifier<Node, NodeKey> nodeII;
254         final private Short version;
255         final private BigInteger datapathId;
256
257         DeviceInfoImpl(
258                 final NodeId nodeId,
259                 final KeyedInstanceIdentifier<Node, NodeKey> nodeII,
260                 final Short version,
261                 final BigInteger datapathId) {
262             this.nodeId = nodeId;
263             this.nodeII = nodeII;
264             this.version = version;
265             this.datapathId = datapathId;
266         }
267
268         @Override
269         public NodeId getNodeId() {
270             return nodeId;
271         }
272
273         @Override
274         public KeyedInstanceIdentifier<Node, NodeKey> getNodeInstanceIdentifier() {
275             return nodeII;
276         }
277
278         @Override
279         public Short getVersion() {
280             return version;
281         }
282
283         @Override
284         public BigInteger getDatapathId() {
285             return datapathId;
286         }
287
288         @Override
289         public boolean equals(Object o) {
290             if (this == o) return true;
291             if (o == null || getClass() != o.getClass()) return false;
292
293             DeviceInfoImpl that = (DeviceInfoImpl) o;
294
295             if (!nodeId.equals(that.nodeId)) return false;
296             if (!nodeII.equals(that.nodeII)) return false;
297             if (!version.equals(that.version)) return false;
298             return datapathId.equals(that.datapathId);
299
300         }
301
302         @Override
303         public int hashCode() {
304             int result = nodeId.hashCode();
305             result = 31 * result + nodeII.hashCode();
306             result = 31 * result + version.hashCode();
307             result = 31 * result + datapathId.hashCode();
308             return result;
309         }
310     }
311 }