Merge "Bug 2806 - Immediate and infinite reconnect attempts during negotiation" into...
[netconf.git] / opendaylight / netconf / netconf-netty-util / src / main / java / org / opendaylight / netconf / nettyutil / handler / ssh / client / AsyncSshHandlerReader.java
1 /*
2  * Copyright (c) 2014 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.netconf.nettyutil.handler.ssh.client;
10
11 import io.netty.buffer.ByteBuf;
12 import io.netty.buffer.Unpooled;
13 import org.apache.sshd.common.future.SshFutureListener;
14 import org.apache.sshd.common.io.IoInputStream;
15 import org.apache.sshd.common.io.IoReadFuture;
16 import org.apache.sshd.common.util.Buffer;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 /**
21  * Listener on async input stream from SSH session.
22  * This listeners schedules reads in a loop until the session is closed or read fails.
23  */
24 public final class AsyncSshHandlerReader implements SshFutureListener<IoReadFuture>, AutoCloseable {
25
26     private static final Logger LOG = LoggerFactory.getLogger(AsyncSshHandlerReader.class);
27
28     private static final int BUFFER_SIZE = 8192;
29
30     private final AutoCloseable connectionClosedCallback;
31     private final ReadMsgHandler readHandler;
32
33     private final String channelId;
34     private IoInputStream asyncOut;
35     private Buffer buf;
36     private IoReadFuture currentReadFuture;
37
38     public AsyncSshHandlerReader(final AutoCloseable connectionClosedCallback, final ReadMsgHandler readHandler, final String channelId, final IoInputStream asyncOut) {
39         this.connectionClosedCallback = connectionClosedCallback;
40         this.readHandler = readHandler;
41         this.channelId = channelId;
42         this.asyncOut = asyncOut;
43         buf = new Buffer(BUFFER_SIZE);
44         asyncOut.read(buf).addListener(this);
45     }
46
47     @Override
48     public synchronized void operationComplete(final IoReadFuture future) {
49         if(future.getException() != null) {
50
51             //if asyncout is already set to null by close method, do nothing
52             if(asyncOut == null) {
53                 return;
54             }
55
56             if(asyncOut.isClosed() || asyncOut.isClosing()) {
57                 // Ssh dropped
58                 LOG.debug("Ssh session dropped on channel: {}", channelId, future.getException());
59             } else {
60                 LOG.warn("Exception while reading from SSH remote on channel {}", channelId, future.getException());
61             }
62             invokeDisconnect();
63             return;
64         }
65
66         if (future.getRead() > 0) {
67             final ByteBuf msg = Unpooled.wrappedBuffer(buf.array(), 0, future.getRead());
68             if(LOG.isTraceEnabled()) {
69                 LOG.trace("Reading message on channel: {}, message: {}", channelId, AsyncSshHandlerWriter.byteBufToString(msg));
70             }
71             readHandler.onMessageRead(msg);
72
73             // Schedule next read
74             buf = new Buffer(BUFFER_SIZE);
75             currentReadFuture = asyncOut.read(buf);
76             currentReadFuture.addListener(this);
77         }
78     }
79
80     private void invokeDisconnect() {
81         try {
82             connectionClosedCallback.close();
83         } catch (final Exception e) {
84             // This should not happen
85             throw new IllegalStateException(e);
86         }
87     }
88
89     @Override
90     public synchronized void close() {
91         // Remove self as listener on close to prevent reading from closed input
92         if(currentReadFuture != null) {
93             currentReadFuture.removeListener(this);
94             currentReadFuture = null;
95         }
96
97         asyncOut = null;
98     }
99
100     public interface ReadMsgHandler {
101
102         void onMessageRead(ByteBuf msg);
103     }
104 }