All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.netconf.callhome.protocol.MinaSshNettyChannel Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016 Brocade Communication Systems and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.netconf.callhome.protocol;

import static java.util.Objects.requireNonNull;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.buffer.ByteBuf;
import io.netty.channel.AbstractServerChannel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.EventLoop;
import java.net.SocketAddress;
import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerReader;
import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerReader.ReadMsgHandler;
import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerWriter;
import org.opendaylight.netconf.shaded.sshd.client.channel.ClientChannel;
import org.opendaylight.netconf.shaded.sshd.client.session.ClientSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// Non-final for testing
class MinaSshNettyChannel extends AbstractServerChannel {
    private static final Logger LOG = LoggerFactory.getLogger(MinaSshNettyChannel.class);
    private static final ChannelMetadata METADATA = new ChannelMetadata(false);

    private final ChannelConfig config = new DefaultChannelConfig(this);
    private final CallHomeSessionContext context;
    private final ClientSession session;
    private final ClientChannel sshChannel;
    private final AsyncSshHandlerReader sshReadHandler;
    private final AsyncSshHandlerWriter sshWriteAsyncHandler;

    private volatile boolean nettyClosed = false;

    @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "Access to our pipeline()")
    MinaSshNettyChannel(final CallHomeSessionContext context, final ClientSession session,
        final ClientChannel sshChannel) {
        this.context = requireNonNull(context);
        this.session = requireNonNull(session);
        this.sshChannel = requireNonNull(sshChannel);
        sshReadHandler = new AsyncSshHandlerReader(
            new ConnectionClosedDuringRead(), new FireReadMessage(), "netconf", sshChannel.getAsyncOut());
        sshWriteAsyncHandler = new AsyncSshHandlerWriter(sshChannel.getAsyncIn());
        pipeline().addFirst(createChannelAdapter());
    }

    private ChannelOutboundHandlerAdapter createChannelAdapter() {
        return new ChannelOutboundHandlerAdapter() {
            @Override
            public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) {
                sshWriteAsyncHandler.write(ctx, msg, promise);
            }
        };
    }

    @Override
    public ChannelConfig config() {
        return config;
    }

    private static boolean notClosing(final org.opendaylight.netconf.shaded.sshd.common.Closeable sshCloseable) {
        return !sshCloseable.isClosing() && !sshCloseable.isClosed();
    }

    @Override
    public boolean isOpen() {
        return notClosing(session);
    }

    @Override
    public boolean isActive() {
        return notClosing(session);
    }

    @Override
    public ChannelMetadata metadata() {
        return METADATA;
    }

    @Override
    protected AbstractUnsafe newUnsafe() {
        return new SshUnsafe();
    }

    @Override
    protected boolean isCompatible(final EventLoop loop) {
        return true;
    }

    @Override
    protected SocketAddress localAddress0() {
        return session.getIoSession().getLocalAddress();
    }

    @Override
    protected SocketAddress remoteAddress0() {
        return context.getRemoteAddress();
    }

    @Override
    protected void doBind(final SocketAddress localAddress) {
        throw new UnsupportedOperationException("Bind not supported.");
    }

    void doMinaDisconnect(final boolean blocking) {
        if (notClosing(session)) {
            sshChannel.close(blocking);
            session.close(blocking);
        }
    }

    void doNettyDisconnect() {
        if (!nettyClosed) {
            nettyClosed = true;
            pipeline().fireChannelInactive();
            sshReadHandler.close();
            sshWriteAsyncHandler.close();
        }
    }

    @Override
    protected void doDisconnect() {
        LOG.info("Disconnect invoked");
        doNettyDisconnect();
        doMinaDisconnect(false);
    }

    @Override
    protected void doClose() {
        context.removeSelf();
        if (notClosing(session)) {
            session.close(true);
            sshChannel.close(true);
        }
    }

    @Override
    protected void doBeginRead() {
        // Intentional NOOP - read is started by AsyncSshHandlerReader
    }

    @Override
    protected void doWrite(final ChannelOutboundBuffer in) {
        throw new IllegalStateException("Outbound writes to SSH should be done by SSH Write handler");
    }

    private final class FireReadMessage implements ReadMsgHandler {
        @Override
        public void onMessageRead(final ByteBuf msg) {
            pipeline().fireChannelRead(msg);
        }
    }

    private final class ConnectionClosedDuringRead implements AutoCloseable {

        /**
         * Invoked when SSH session dropped during read using {@link AsyncSshHandlerReader}.
         */
        @Override
        public void close() {
            doNettyDisconnect();
        }
    }

    private final class SshUnsafe extends AbstractUnsafe {
        @Override
        public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress,
                final ChannelPromise promise) {
            throw new UnsupportedOperationException("Unsafe is not supported.");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy