2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.netconf.nettyutil.handler.ssh.client;
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.Buffer;
17 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
22 * Listener on async input stream from SSH session.
23 * This listeners schedules reads in a loop until the session is closed or read fails.
25 public final class AsyncSshHandlerReader implements SshFutureListener<IoReadFuture>, AutoCloseable {
27 private static final Logger LOG = LoggerFactory.getLogger(AsyncSshHandlerReader.class);
29 private static final int BUFFER_SIZE = 2048;
31 private final AutoCloseable connectionClosedCallback;
32 private final ReadMsgHandler readHandler;
34 private final String channelId;
35 private IoInputStream asyncOut;
37 private IoReadFuture currentReadFuture;
39 public AsyncSshHandlerReader(final AutoCloseable connectionClosedCallback, final ReadMsgHandler readHandler,
40 final String channelId, final IoInputStream asyncOut) {
41 this.connectionClosedCallback = connectionClosedCallback;
42 this.readHandler = readHandler;
43 this.channelId = channelId;
44 this.asyncOut = asyncOut;
45 buf = new ByteArrayBuffer(BUFFER_SIZE);
46 asyncOut.read(buf).addListener(this);
50 public void operationComplete(final IoReadFuture future) {
51 if (checkDisconnect(future)) {
56 private synchronized boolean checkDisconnect(final IoReadFuture future) {
57 if (future.getException() != null) {
58 //if asyncout is already set to null by close method, do nothing
59 if (asyncOut == null) {
63 if (asyncOut.isClosed() || asyncOut.isClosing()) {
65 LOG.debug("Ssh session dropped on channel: {}", channelId, future.getException());
67 LOG.warn("Exception while reading from SSH remote on channel {}", channelId, future.getException());
70 } else if (future.getRead() > 0) {
71 final ByteBuf msg = Unpooled.wrappedBuffer(buf.array(), 0, future.getRead());
72 if (LOG.isTraceEnabled()) {
73 LOG.trace("Reading message on channel: {}, message: {}",
74 channelId, AsyncSshHandlerWriter.byteBufToString(msg));
76 readHandler.onMessageRead(msg);
79 buf = new ByteArrayBuffer(BUFFER_SIZE);
80 currentReadFuture = asyncOut.read(buf);
81 currentReadFuture.addListener(this);
87 * Closing of the {@link AsyncSshHandlerReader}. This method should never be called with any locks held since
88 * call to {@link AutoCloseable#close()} can be a source of ABBA deadlock.
90 @SuppressWarnings("checkstyle:IllegalCatch")
91 private void invokeDisconnect() {
93 connectionClosedCallback.close();
94 } catch (final Exception e) {
95 // This should not happen
96 throw new IllegalStateException(e);
101 public synchronized void close() {
102 // Remove self as listener on close to prevent reading from closed input
103 if (currentReadFuture != null) {
104 currentReadFuture.removeListener(this);
105 currentReadFuture = null;
111 public interface ReadMsgHandler {
113 void onMessageRead(ByteBuf msg);