org.opendaylight.usc.plugin.UscPlugin.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.usc.plugin.UscPlugin.java

Source

/*
 * Copyright (c) 2015 Huawei, Inc 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.usc.plugin;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
import io.netty.channel.local.LocalServerChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.AttributeKey;

import java.net.InetSocketAddress;
import java.net.PortUnreachableException;
import java.net.SocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.opendaylight.usc.manager.UscRouteBrokerService;
import org.opendaylight.usc.manager.api.UscEvent;
import org.opendaylight.usc.manager.api.UscMonitor;
import org.opendaylight.usc.manager.cluster.UscRemoteChannelIdentifier;
import org.opendaylight.usc.manager.cluster.UscRouteIdentifier;
import org.opendaylight.usc.manager.monitor.UscMonitorImpl;
import org.opendaylight.usc.manager.monitor.evt.UscChannelCreateEvent;
import org.opendaylight.usc.plugin.model.UscChannel.ChannelType;
import org.opendaylight.usc.plugin.model.UscChannelImpl;
import org.opendaylight.usc.plugin.model.UscDevice;
import org.opendaylight.usc.plugin.model.UscSessionImpl;
import org.opendaylight.usc.protocol.UscControl;
import org.opendaylight.usc.util.UscServiceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.util.concurrent.SettableFuture;

/**
 * This is the base class for all UscPlugin classes. This handles common
 * connection setup and channel/session management capabilities.
 */
public abstract class UscPlugin implements AutoCloseable {

    /**
     * Constant used for setting the UscChannel attribute on a netty channel.
     */
    public static final AttributeKey<UscChannelImpl> CHANNEL = AttributeKey.valueOf("channel");

    /**
     * Constant used for setting the UscSession attribute on a netty channel.
     */

    public static final AttributeKey<SettableFuture<UscSessionImpl>> SESSION = AttributeKey.valueOf("session");

    /**
     * Constant used for setting the client channel attribute on a server
     * channel
     */
    public static final AttributeKey<Channel> CLIENT_CHANNEL = AttributeKey.valueOf("client_channel");
    /**
     * Constant used for setting the UscDevice attribute on a server channel
     */
    public static final AttributeKey<UscRouteIdentifier> ROUTE_IDENTIFIER = AttributeKey
            .valueOf("route_identifier");

    /**
     * Constant used for setting the next direct channel between the plugin and
     * device
     */
    public static final AttributeKey<Channel> DIRECT_CHANNEL = AttributeKey.valueOf("direct_channel");
    public static final AttributeKey<LocalChannel> LOCAL_SERVER_CHANNEL = AttributeKey
            .valueOf("local_server_channel");

    private static final Logger LOG = LoggerFactory.getLogger(UscPlugin.class);
    private LocalAddress localServerAddr;
    private final UscExceptionHandler uscExceptionHandler = new UscExceptionHandler(this);

    /**
     * Map from client SocketAddress to serverChildChannel
     */
    private final ConcurrentMap<SocketAddress, SettableFuture<LocalChannel>> serverChannels = new ConcurrentHashMap<>();
    private final ConcurrentMap<Channel, SettableFuture<Boolean>> closeFuture = new ConcurrentHashMap<>();
    private final UscConnectionManager connectionManager = new UscConnectionManager(this);
    private final EventLoopGroup localGroup = new LocalEventLoopGroup();
    private final UscDemultiplexer demuxer = new UscDemultiplexer(this);
    private final Demultiplexer dmpx = new Demultiplexer(this);
    private final UscMultiplexer muxer = new UscMultiplexer(this);
    private final UscRemoteDeviceHandler remoteDeviceHandler = new UscRemoteDeviceHandler();
    private final UscRemoteServerHandler remoteServerHandler = new UscRemoteServerHandler();
    private final UscMonitor monitor = new UscMonitorImpl();

    protected UscPlugin(LocalAddress localAddr) {
        LOG.debug("UscPlugin " + this + "started");
        localServerAddr = localAddr;

        final ServerBootstrap localServerBootstrap = new ServerBootstrap();
        localServerBootstrap.group(localGroup);
        localServerBootstrap.channel(LocalServerChannel.class);
        localServerBootstrap.childHandler(new ChannelInitializer<LocalChannel>() {
            @Override
            public void initChannel(final LocalChannel serverChannel) throws Exception {
                ChannelPipeline p = serverChannel.pipeline();
                p.addLast(new LoggingHandler("localServerBootstrp Handler 4", LogLevel.TRACE));

                // call this first so that the attribute will be visible
                // to the outside once the localAddress is set
                serverChannel.attr(SESSION).setIfAbsent(SettableFuture.<UscSessionImpl>create());

                // register the child channel by address for lookup
                // outside
                LocalAddress localAddress = serverChannel.remoteAddress();
                serverChannels.putIfAbsent(localAddress, SettableFuture.<LocalChannel>create());
                serverChannels.get(localAddress).set(serverChannel);

                p.addLast(new LoggingHandler("localServerBootstrp Handler 3", LogLevel.TRACE));

                // add remote device handler for route remote request
                p.addLast(remoteDeviceHandler);
                p.addLast(new LoggingHandler("localServerBootstrp Handler 2", LogLevel.TRACE));
                p.addLast(getMultiplexer());
                p.addLast(new LoggingHandler("localServerBootstrp Handler 1", LogLevel.TRACE));
            }
        });

        // Start the server.
        final ChannelFuture serverChannelFuture = localServerBootstrap.bind(localServerAddr);
        LOG.debug("serverChannel: " + serverChannelFuture);
    }

    protected void initAgentPipeline(ChannelPipeline p, ChannelHandler securityHandler) {

        p.addLast(new LoggingHandler("UscPlugin Handler 6", LogLevel.TRACE));

        // security handler
        p.addLast("securityHandler", securityHandler);
        p.addLast(new LoggingHandler("UscPlugin Handler 5", LogLevel.TRACE));

        // Encoders
        // UscFrameEncoder is Sharable
        p.addLast("frameEncoder", getFrameEncoder());
        p.addLast(new LoggingHandler("UscPlugin Handler 4", LogLevel.TRACE));

        // Decoders
        // UscFrameDecoderUdp is Sharable
        p.addLast("frameDecoder", getFrameDecoder());
        p.addLast(new LoggingHandler("UscPlugin Handler 3", LogLevel.TRACE));

        // add handler for handling response for remote session like a dummy
        // server
        p.addLast(remoteServerHandler);

        p.addLast(new LoggingHandler("UscPlugin Handler 2", LogLevel.TRACE));
        // UscDemultiplexer
        p.addLast("UscDemultiplexer", getDemultiplexer());

        p.addLast(new LoggingHandler("UscPlugin Handler 1", LogLevel.TRACE));
    }

    protected void initDirectPipeline(ChannelPipeline p, ChannelHandler securityHandler) {

        p.addLast(new LoggingHandler("UscPlugin direct handler 4", LogLevel.TRACE));

        // security handler
        p.addLast("securityHandler", securityHandler);
        p.addLast(new LoggingHandler("UscPlugin direct handler 3", LogLevel.TRACE));

        // add handler for handling response for remote session like a dummy
        // server
        p.addLast(remoteServerHandler);

        p.addLast(new LoggingHandler("UscPlugin direct handler 2", LogLevel.TRACE));
        // demultiplexer
        p.addLast("Demultiplexer", getDmpx());

        p.addLast(new LoggingHandler("UscPlugin direct handler 1", LogLevel.TRACE));
    }

    protected ChannelInboundHandler getMultiplexer() {
        return muxer;
    }

    protected UscDemultiplexer getDemultiplexer() {
        return demuxer;
    }

    protected Demultiplexer getDmpx() {
        return dmpx;
    }

    protected UscConnectionManager getConnectionManager() {
        return connectionManager;
    }

    protected abstract ChannelOutboundHandler getFrameEncoder();

    protected abstract ChannelInboundHandler getFrameDecoder();

    /**
     * Initiates a client session to a device service as specified by the
     * address parameter.
     * 
     * @param clientBootstrap
     *            the Netty bootstrap to use to create the session
     * @param address
     *            the IP address and port of the device service
     * @return the Netty ChannelFuture that can be used to communicate with the
     *         device service
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public ChannelFuture connect(Bootstrap clientBootstrap, final InetSocketAddress address)
            throws InterruptedException, ExecutionException, Exception {

        return connect(clientBootstrap, address, false);
    }

    public ChannelFuture connect(Bootstrap clientBootstrap, final InetSocketAddress address, boolean remote)
            throws InterruptedException, ExecutionException, Exception {

        LOG.trace("Attempt to connect to " + address + ",remote is " + remote);
        boolean remoteDevice = false;

        // Connect to USC Agent to the device if one's not already created
        UscChannelImpl connection = null;
        Channel directChannel = null;
        UscDevice device = new UscDevice(address.getAddress(), address.getPort());
        UscRouteBrokerService routeBroker = UscServiceUtils.getService(UscRouteBrokerService.class);
        Exception connectException = null;

        if (remote) {
            if (routeBroker != null) {
                if (routeBroker.existRemoteChannel(
                        new UscRemoteChannelIdentifier(device.getInetAddress(), getChannelType()))) {
                    remoteDevice = true;
                    LOG.trace("Find remote channel for device " + device);
                } else {
                    remote = false;
                    LOG.warn("remote channel is not found for device " + device + ", try to connect from local.");
                }
            } else {
                LOG.error("Broker service is null, try to connect from local.");
                remote = false;
            }
        }
        if (!remote) {
            if (getChannelType() == ChannelType.DTLS || getChannelType() == ChannelType.UDP) {
                try {
                    connection = connectionManager.getConnection(
                            new UscDevice(address.getAddress(), address.getPort()), getChannelType());
                } catch (Exception e) {
                    LOG.error("Failed to get udp agent connection, try to directly connect.error is "
                            + e.getMessage());
                    connectException = e;
                }

                LOG.trace("Returned connection is " + connection);
                Channel channel = connection.getChannel();
                UscDemultiplexer handler = (UscDemultiplexer) channel.pipeline().get("UscDemultiplexer");
                SocketAddress remoteAddress = channel.remoteAddress();
                if (!handler.promiseMap.containsKey(remoteAddress)) {
                    handler.promiseMap.putIfAbsent(remoteAddress, SettableFuture.<Throwable>create());

                    UscControl echoControl = new UscControl(address.getPort(), 1,
                            UscControl.ControlCode.ECHO.getCode());
                    channel.writeAndFlush(echoControl);
                    LOG.trace("Send a ECHO message to see if the usc agent port is reachable.");
                }

                Throwable e = null;
                e = handler.promiseMap.get(remoteAddress).get(5000, TimeUnit.MILLISECONDS);

                if (e != null) {
                    LOG.trace("connect: handler.promise is " + e);
                    if (e != null && e instanceof PortUnreachableException) {
                        LOG.trace("connect: caught exception PortUnreachableException");
                        channel.close();
                        connectionManager.removeConnection(connection);
                        connection = null;
                        LOG.trace("connect: start connecting to " + address.getAddress() + (":") + address.getPort()
                                + " directly.");
                        try {
                            directChannel = connectToDeviceDirectly(
                                    new UscDevice(address.getAddress(), address.getPort()));
                        } catch (Exception ex) {
                            LOG.error("Failed to get direct connection, try to remote connect.error is "
                                    + e.getMessage());
                            connectException = ex;
                        }
                    }
                }
            } else {
                try {
                    connection = connectionManager.getConnection(
                            new UscDevice(address.getAddress(), address.getPort()), getChannelType());
                } catch (Exception e) {
                    LOG.error("Failed to get agent connection, try to directly connect.error is " + e.getMessage());
                    try {
                        directChannel = connectToDeviceDirectly(
                                new UscDevice(address.getAddress(), address.getPort()));
                    } catch (Exception ex) {
                        LOG.error("Failed to get direct connection, try to remote connect.error is "
                                + e.getMessage());
                        connectException = ex;
                    }
                }
            }
        }
        if (connectException != null) {
            if (routeBroker != null) {
                if (routeBroker.existRemoteChannel(
                        new UscRemoteChannelIdentifier(device.getInetAddress(), getChannelType()))) {
                    remoteDevice = true;
                    LOG.trace("Found remote channel for device " + device);
                } else {
                    LOG.warn("Failed to find remote channel in device table!");
                    throw connectException;
                }
            } else {
                LOG.warn("Broker service is null, can't find exist remote channel, throw exception dirctly.");
                throw connectException;
            }
        }

        final ChannelFuture clientChannelFuture = clientBootstrap.connect(localServerAddr);
        clientChannelFuture.channel().pipeline().addLast(uscExceptionHandler);

        // sync to ensure that localAddress is not null
        final Channel clientChannel = clientChannelFuture.sync().channel();
        SocketAddress localAddress = clientChannel.localAddress();
        serverChannels.putIfAbsent(localAddress, SettableFuture.<LocalChannel>create());

        // wait for the peer to populate
        LocalChannel serverChannel = serverChannels.get(localAddress).get();

        LOG.trace("connect: serverChannel = " + serverChannel);

        assert serverChannel != null;
        // remove the entry from the map as its purpose is complete
        serverChannels.remove(localAddress);

        if (connection != null) {
            UscSessionImpl session = connection.addSession(address.getPort(), serverChannel);

            LOG.trace("clientChannel set session " + session);
            // these attributes are used by unit test cases
            clientChannel.attr(SESSION).setIfAbsent(SettableFuture.<UscSessionImpl>create());
            clientChannel.attr(SESSION).get().set(session);

            // these attributes are used by UscMultiplexer
            serverChannel.attr(SESSION).get().set(session);

            // this attribute is used by UscDemultiplexer
            serverChannel.attr(CLIENT_CHANNEL).set(clientChannel);
            LOG.info("Connected with channel for " + session);
        } else if (directChannel != null) {
            clientChannel.attr(LOCAL_SERVER_CHANNEL).set(serverChannel);
            serverChannel.attr(DIRECT_CHANNEL).set(directChannel);
            serverChannel.attr(CLIENT_CHANNEL).set(clientChannel);
            directChannel.attr(LOCAL_SERVER_CHANNEL).set(serverChannel);
            LOG.info("Connected channel using direct way for " + device);
        }

        if (remoteDevice) {
            UscRemoteChannelIdentifier remoteChannel = new UscRemoteChannelIdentifier(device.getInetAddress(),
                    getChannelType());
            UscRouteIdentifier routeId = new UscRouteIdentifier(remoteChannel, serverChannel.hashCode(),
                    address.getPort());
            clientChannel.attr(ROUTE_IDENTIFIER).setIfAbsent(routeId);
            serverChannel.attr(ROUTE_IDENTIFIER).setIfAbsent(routeId);
            sendEvent(new UscChannelCreateEvent(remoteChannel.getIp(), true, remoteChannel.getRemoteChannelType()));
            // register local session for routing to remote device
            routeBroker.addLocalSession(routeId, serverChannel);
            if (directChannel != null) {
                // direct connection only has one session
                directChannel.attr(ROUTE_IDENTIFIER).set(routeId);
            }
            LOG.info("Initialized local remote channel for " + routeId);
        }
        return clientChannelFuture;
    }

    protected abstract ChannelType getChannelType();

    protected abstract Channel connectToAgent(UscDevice device) throws InterruptedException, Exception;

    protected abstract Channel connectToDeviceDirectly(UscDevice device) throws InterruptedException, Exception;

    @Override
    public void close() {
        localGroup.shutdownGracefully();

        LOG.debug("UscPlugin " + this + "closed");
    }

    protected void addCallHomeConnection(InetSocketAddress address, Channel channel) {
        final UscDevice device = new UscDevice(address.getAddress());
        connectionManager.addConnection(device, channel, true, getChannelType());
    }

    protected ConcurrentMap<Channel, SettableFuture<Boolean>> getCloseFuture() {
        return closeFuture;
    }

    /**
     * send event to monitor service using monitor
     * 
     * @param event
     *            event object
     */
    public void sendEvent(UscEvent event) {
        monitor.onEvent(event);
    }

    /**
     * 
     * @param clientChannel
     * @return close status
     */
    public SettableFuture<Boolean> closeAgentInternalConnection(Channel clientChannel) {
        closeFuture.remove(clientChannel);
        closeFuture.putIfAbsent(clientChannel, SettableFuture.<Boolean>create());

        try {
            UscSessionImpl session = clientChannel.attr(SESSION).get().get();
            Channel outboundChannel = session.getChannel().getChannel();

            UscControl data = new UscControl(session.getPort(), session.getSessionId(),
                    UscControl.ControlCode.TERMINATION_REQUEST.getCode());
            outboundChannel.writeAndFlush(data);

            LOG.trace("UscPlugin closeAgentInternalConnection port#: " + session.getPort() + " ,session#: "
                    + session.getSessionId());

        } catch (Exception e) {
            e.printStackTrace();
        }

        return closeFuture.get(clientChannel);
    }

    public boolean isChannelAvailable(InetSocketAddress address) {
        try {
            final UscChannelImpl connection = connectionManager.getConnection(new UscDevice(address.getAddress()),
                    getChannelType());
            return connection != null;
        } catch (Exception e) {
            LOG.warn("Unable to create USC channel to " + address.getAddress());
            return false;
        }
    }

    public UscChannelImpl retrieveChannelImpl(InetSocketAddress address) {
        try {
            final UscChannelImpl connection = connectionManager.getConnection(new UscDevice(address.getAddress()),
                    getChannelType());
            return connection;
        } catch (Exception e) {
            LOG.warn("Unable to retrieve USC channel to " + address.getAddress());
            return null;
        }
    }
}