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)
60 sshWriteAsyncHandler.write(ctx, msg, promise);
66 public ChannelConfig config() {
70 private static boolean notClosing(final org.apache.sshd.common.Closeable sshCloseable) {
71 return !sshCloseable.isClosing() && !sshCloseable.isClosed();
75 public boolean isOpen() {
76 return notClosing(session);
80 public boolean isActive() {
81 return notClosing(session);
85 public ChannelMetadata metadata() {
90 protected AbstractUnsafe newUnsafe() {
91 return new SshUnsafe();
95 protected boolean isCompatible(final EventLoop loop) {
100 protected SocketAddress localAddress0() {
101 return session.getIoSession().getLocalAddress();
105 protected SocketAddress remoteAddress0() {
106 return context.getRemoteAddress();
110 protected void doBind(final SocketAddress localAddress) throws Exception {
111 throw new UnsupportedOperationException("Bind not supported.");
114 void doMinaDisconnect(final boolean blocking) {
115 if (notClosing(session)) {
116 sshChannel.close(blocking);
117 session.close(blocking);
121 void doNettyDisconnect() {
124 pipeline().fireChannelInactive();
125 sshReadHandler.close();
126 sshWriteAsyncHandler.close();
131 protected void doDisconnect() throws Exception {
132 LOG.info("Disconnect invoked");
134 doMinaDisconnect(false);
138 protected void doClose() throws Exception {
139 context.removeSelf();
140 if (notClosing(session)) {
142 sshChannel.close(true);
147 protected void doBeginRead() throws Exception {
148 // Intentional NOOP - read is started by AsyncSshHandlerReader
152 protected void doWrite(final ChannelOutboundBuffer in) throws Exception {
153 throw new IllegalStateException("Outbound writes to SSH should be done by SSH Write handler");
156 private final class FireReadMessage implements ReadMsgHandler {
158 public void onMessageRead(final ByteBuf msg) {
159 pipeline().fireChannelRead(msg);
163 private final class ConnectionClosedDuringRead implements AutoCloseable {
166 * Invoked when SSH session dropped during read using {@link AsyncSshHandlerReader}.
169 public void close() throws Exception {
174 private class SshUnsafe extends AbstractUnsafe {
176 public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress,
177 final ChannelPromise promise) {
178 throw new UnsupportedOperationException("Unsafe is not supported.");