Adjust to yangtools-2.0.0/odlparent-3.0.0 changes
[netconf.git] / netconf / callhome-protocol / src / main / java / org / opendaylight / netconf / callhome / protocol / MinaSshNettyChannel.java
1 /*
2  * Copyright (c) 2016 Brocade Communication Systems 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.callhome.protocol;
10
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;
30
31 class MinaSshNettyChannel extends AbstractServerChannel {
32     private static final Logger LOG = LoggerFactory.getLogger(MinaSshNettyChannel.class);
33     private static final ChannelMetadata METADATA = new ChannelMetadata(false);
34
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;
41
42     private volatile boolean nettyClosed = false;
43
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());
53     }
54
55     private ChannelOutboundHandlerAdapter createChannelAdapter() {
56         return new ChannelOutboundHandlerAdapter() {
57             @Override
58             public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise)
59                 throws Exception {
60                 sshWriteAsyncHandler.write(ctx, msg, promise);
61             }
62         };
63     }
64
65     @Override
66     public ChannelConfig config() {
67         return config;
68     }
69
70     private static boolean notClosing(final org.apache.sshd.common.Closeable sshCloseable) {
71         return !sshCloseable.isClosing() && !sshCloseable.isClosed();
72     }
73
74     @Override
75     public boolean isOpen() {
76         return notClosing(session);
77     }
78
79     @Override
80     public boolean isActive() {
81         return notClosing(session);
82     }
83
84     @Override
85     public ChannelMetadata metadata() {
86         return METADATA;
87     }
88
89     @Override
90     protected AbstractUnsafe newUnsafe() {
91         return new SshUnsafe();
92     }
93
94     @Override
95     protected boolean isCompatible(final EventLoop loop) {
96         return true;
97     }
98
99     @Override
100     protected SocketAddress localAddress0() {
101         return session.getIoSession().getLocalAddress();
102     }
103
104     @Override
105     protected SocketAddress remoteAddress0() {
106         return context.getRemoteAddress();
107     }
108
109     @Override
110     protected void doBind(final SocketAddress localAddress) throws Exception {
111         throw new UnsupportedOperationException("Bind not supported.");
112     }
113
114     void doMinaDisconnect(final boolean blocking) {
115         if (notClosing(session)) {
116             sshChannel.close(blocking);
117             session.close(blocking);
118         }
119     }
120
121     void doNettyDisconnect() {
122         if (!nettyClosed) {
123             nettyClosed = true;
124             pipeline().fireChannelInactive();
125             sshReadHandler.close();
126             sshWriteAsyncHandler.close();
127         }
128     }
129
130     @Override
131     protected void doDisconnect() throws Exception {
132         LOG.info("Disconnect invoked");
133         doNettyDisconnect();
134         doMinaDisconnect(false);
135     }
136
137     @Override
138     protected void doClose() throws Exception {
139         context.removeSelf();
140         if (notClosing(session)) {
141             session.close(true);
142             sshChannel.close(true);
143         }
144     }
145
146     @Override
147     protected void doBeginRead() throws Exception {
148         // Intentional NOOP - read is started by AsyncSshHandlerReader
149     }
150
151     @Override
152     protected void doWrite(final ChannelOutboundBuffer in) throws Exception {
153         throw new IllegalStateException("Outbound writes to SSH should be done by SSH Write handler");
154     }
155
156     private final class FireReadMessage implements ReadMsgHandler {
157         @Override
158         public void onMessageRead(final ByteBuf msg) {
159             pipeline().fireChannelRead(msg);
160         }
161     }
162
163     private final class ConnectionClosedDuringRead implements AutoCloseable {
164
165         /**
166          * Invoked when SSH session dropped during read using {@link AsyncSshHandlerReader}.
167          */
168         @Override
169         public void close() throws Exception {
170             doNettyDisconnect();
171         }
172     }
173
174     private class SshUnsafe extends AbstractUnsafe {
175         @Override
176         public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress,
177                 final ChannelPromise promise) {
178             throw new UnsupportedOperationException("Unsafe is not supported.");
179         }
180     }
181 }