2 * Copyright (c) 2016 Brocade Communication Systems 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.callhome.protocol;
11 import com.google.common.base.Preconditions;
12 import io.netty.buffer.ByteBuf;
13 import io.netty.channel.AbstractServerChannel;
14 import io.netty.channel.ChannelConfig;
15 import io.netty.channel.ChannelHandlerContext;
16 import io.netty.channel.ChannelMetadata;
17 import io.netty.channel.ChannelOutboundBuffer;
18 import io.netty.channel.ChannelOutboundHandlerAdapter;
19 import io.netty.channel.ChannelPromise;
20 import io.netty.channel.DefaultChannelConfig;
21 import io.netty.channel.EventLoop;
22 import java.net.SocketAddress;
23 import org.apache.sshd.client.channel.ClientChannel;
24 import org.apache.sshd.client.session.ClientSession;
25 import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerReader;
26 import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerReader.ReadMsgHandler;
27 import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerWriter;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
31 class MinaSshNettyChannel extends AbstractServerChannel {
32 private static final Logger LOG = LoggerFactory.getLogger(MinaSshNettyChannel.class);
33 private static final ChannelMetadata METADATA = new ChannelMetadata(false);
35 private final ChannelConfig config = new DefaultChannelConfig(this);
36 private final CallHomeSessionContext context;
37 private final ClientSession session;
38 private final ClientChannel sshChannel;
39 private final AsyncSshHandlerReader sshReadHandler;
40 private final AsyncSshHandlerWriter sshWriteAsyncHandler;
42 private volatile boolean nettyClosed = false;
44 MinaSshNettyChannel(final CallHomeSessionContext context, final ClientSession session,
45 final ClientChannel sshChannel) {
46 this.context = Preconditions.checkNotNull(context);
47 this.session = Preconditions.checkNotNull(session);
48 this.sshChannel = Preconditions.checkNotNull(sshChannel);
49 this.sshReadHandler = new AsyncSshHandlerReader(
50 new ConnectionClosedDuringRead(), new FireReadMessage(), "netconf", sshChannel.getAsyncOut());
51 this.sshWriteAsyncHandler = new AsyncSshHandlerWriter(sshChannel.getAsyncIn());
52 pipeline().addFirst(createChannelAdapter());
55 private ChannelOutboundHandlerAdapter createChannelAdapter() {
56 return new ChannelOutboundHandlerAdapter() {
58 public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) {
59 sshWriteAsyncHandler.write(ctx, msg, promise);
65 public ChannelConfig config() {
69 private static boolean notClosing(final org.apache.sshd.common.Closeable sshCloseable) {
70 return !sshCloseable.isClosing() && !sshCloseable.isClosed();
74 public boolean isOpen() {
75 return notClosing(session);
79 public boolean isActive() {
80 return notClosing(session);
84 public ChannelMetadata metadata() {
89 protected AbstractUnsafe newUnsafe() {
90 return new SshUnsafe();
94 protected boolean isCompatible(final EventLoop loop) {
99 protected SocketAddress localAddress0() {
100 return session.getIoSession().getLocalAddress();
104 protected SocketAddress remoteAddress0() {
105 return context.getRemoteAddress();
109 protected void doBind(final SocketAddress localAddress) {
110 throw new UnsupportedOperationException("Bind not supported.");
113 void doMinaDisconnect(final boolean blocking) {
114 if (notClosing(session)) {
115 sshChannel.close(blocking);
116 session.close(blocking);
120 void doNettyDisconnect() {
123 pipeline().fireChannelInactive();
124 sshReadHandler.close();
125 sshWriteAsyncHandler.close();
130 protected void doDisconnect() {
131 LOG.info("Disconnect invoked");
133 doMinaDisconnect(false);
137 protected void doClose() {
138 context.removeSelf();
139 if (notClosing(session)) {
141 sshChannel.close(true);
146 protected void doBeginRead() {
147 // Intentional NOOP - read is started by AsyncSshHandlerReader
151 protected void doWrite(final ChannelOutboundBuffer in) {
152 throw new IllegalStateException("Outbound writes to SSH should be done by SSH Write handler");
155 private final class FireReadMessage implements ReadMsgHandler {
157 public void onMessageRead(final ByteBuf msg) {
158 pipeline().fireChannelRead(msg);
162 private final class ConnectionClosedDuringRead implements AutoCloseable {
165 * Invoked when SSH session dropped during read using {@link AsyncSshHandlerReader}.
168 public void close() {
173 private class SshUnsafe extends AbstractUnsafe {
175 public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress,
176 final ChannelPromise promise) {
177 throw new UnsupportedOperationException("Unsafe is not supported.");