org.opendaylight.usc.manager.UscRouteBrokerService.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.usc.manager.UscRouteBrokerService.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.manager;

import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import io.netty.buffer.Unpooled;
import io.netty.channel.local.LocalChannel;

import org.opendaylight.usc.manager.api.UscConfigurationService;
import org.opendaylight.usc.manager.api.UscMonitor;
import org.opendaylight.usc.manager.cluster.UscCommunicatorActor;
import org.opendaylight.usc.manager.cluster.UscDeviceMountTable;
import org.opendaylight.usc.manager.cluster.UscRemoteChannelIdentifier;
import org.opendaylight.usc.manager.cluster.UscRouteIdentifier;
import org.opendaylight.usc.manager.cluster.UscRouteIdentifierData;
import org.opendaylight.usc.manager.cluster.UscRoutedLocalSessionManager;
import org.opendaylight.usc.manager.cluster.UscRoutedRemoteSessionManager;
import org.opendaylight.usc.manager.cluster.message.UscRemoteDataMessage;
import org.opendaylight.usc.manager.cluster.message.UscRemoteExceptionMessage;
import org.opendaylight.usc.manager.cluster.message.UscRemoteMessage;
import org.opendaylight.usc.manager.monitor.UscMonitorImpl;
import org.opendaylight.usc.manager.monitor.evt.UscChannelCloseEvent;
import org.opendaylight.usc.manager.monitor.evt.UscSessionCloseEvent;
import org.opendaylight.usc.manager.monitor.evt.UscSessionCreateEvent;
import org.opendaylight.usc.manager.monitor.evt.UscSessionErrorEvent;
import org.opendaylight.usc.manager.monitor.evt.UscSessionTransactionEvent;
import org.opendaylight.usc.manager.monitor.evt.base.UscErrorLevel;
import org.opendaylight.usc.plugin.UscConnectionManager;
import org.opendaylight.usc.plugin.exception.UscChannelException;
import org.opendaylight.usc.plugin.exception.UscException;
import org.opendaylight.usc.plugin.exception.UscSessionException;
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.protocol.UscData;
import org.opendaylight.usc.util.UscServiceUtils;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import scala.collection.JavaConversions;

import com.google.common.base.Preconditions;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.ActorSystem;
import akka.actor.Address;
import akka.cluster.Cluster;
import akka.cluster.Member;
import akka.osgi.BundleDelegatingClassLoader;

/**
 * the route broker service, create the actor system and manage all of route
 * information for USC, and process all request for remote channel
 *
 */
public class UscRouteBrokerService {
    private static final String ACTOR_SYSTEM_NAME = "odl-cluster-usc";
    private static final String COMMUNICATOR_ACTOR_NAME = "UscCommunicator";
    private static final Logger LOG = LoggerFactory.getLogger(UscRouteBrokerService.class);
    private UscDeviceMountTable deviceTable;
    private UscRoutedRemoteSessionManager remoteSessionManager = new UscRoutedRemoteSessionManager();
    private UscRoutedLocalSessionManager localSessionManager = new UscRoutedLocalSessionManager();
    private ConcurrentHashMap<String, UscConnectionManager> connectionManagerMap = new ConcurrentHashMap<String, UscConnectionManager>();
    private ConcurrentHashMap<UscRemoteChannelIdentifier, Integer> sessionIdMap = new ConcurrentHashMap<UscRemoteChannelIdentifier, Integer>();
    private ActorRef communicator;
    private static UscRouteBrokerService service = new UscRouteBrokerService();
    private final int MAX_FIXED_SESSION_ID = Character.MAX_VALUE - 1000;
    private ActorSystem actorSystem = null;
    private Set<ActorSelection> remoteActors = new CopyOnWriteArraySet<ActorSelection>();
    private Config actorSystemConfig = null;
    private Cluster cluster = null;
    private UscMonitor monitor = new UscMonitorImpl();

    private UscRouteBrokerService() {

    }

    /**
     * create actor system and communicator
     */
    public void init() {
        this.deviceTable = UscDeviceMountTable.getInstance();
        UscConfigurationService configService = UscServiceUtils.getService(UscConfigurationService.class);
        if (configService == null) {
            LOG.error(
                    "Failed to get configuration service,can't create ActorSystem and local remote communicator!");
            return;
        }
        BundleDelegatingClassLoader classLoader = new BundleDelegatingClassLoader(
                FrameworkUtil.getBundle(UscRouteBrokerService.class),
                Thread.currentThread().getContextClassLoader());
        File defaultConfigFile = new File(
                configService.getConfigStringValue(UscConfigurationService.AKKA_CLUSTER_FILE));
        Preconditions.checkState(defaultConfigFile.exists(), "akka.conf is missing");
        if (defaultConfigFile.exists()) {
            actorSystemConfig = ConfigFactory.load(ConfigFactory.parseFile(defaultConfigFile))
                    .getConfig(ACTOR_SYSTEM_NAME);
            if (actorSystemConfig == null) {
                LOG.error("Failed to create ActorSystem and local remote communicator!");
                return;
            }
            actorSystem = ActorSystem.create(ACTOR_SYSTEM_NAME, actorSystemConfig, classLoader);
            cluster = Cluster.get(actorSystem);
            communicator = actorSystem.actorOf(UscCommunicatorActor.props(), COMMUNICATOR_ACTOR_NAME);
        } else {
            LOG.error("Failed to create ActorSystem and local remote communicator!");
        }
    }

    private void updateActorListFromCluster() {
        List<String> actorList = new ArrayList<String>();
        ActorSelection remoteActorSelection = null;
        scala.collection.immutable.List<Address> seedNodeList = cluster.settings().SeedNodes().toList();
        for (Address address : JavaConversions.seqAsJavaList(seedNodeList)) {
            if (isLocalAddress(address)) {
                continue;
            }
            actorList.add(address.toString());
        }
        for (String path : actorList) {
            if (!isLocalActor(path)) {
                path = path + communicator.path().toStringWithoutAddress();
                remoteActorSelection = actorSystem.actorSelection(path);
                if (remoteActorSelection != null) {
                    remoteActors.add(remoteActorSelection);
                } else {
                    LOG.error("Failed to get actor selection for " + path);
                }

            }
        }
    }

    /**
     * process member up event for cluster
     *
     * @param member
     *            the member of cluster
     * @return always true,currently not false
     */
    public boolean clusterMemberUp(Member member) {
        if (isLocalAddress(member.address())) {
            return true;
        }
        String path = member.address() + communicator.path().toStringWithoutAddress();
        ActorSelection remoteActorSelection = actorSystem.actorSelection(path);
        if (remoteActorSelection != null) {
            remoteActors.add(remoteActorSelection);
            LOG.info("Added remote actor selection for " + path + ", remote actor number becomes "
                    + remoteActors.size());
        } else {
            LOG.error("Failed to get actor selection for " + path);
        }
        return true;
    }

    private boolean isLocalAddress(Address address) {
        if (cluster.selfAddress().equals(address)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * process member down event for cluster
     *
     * @param member
     *            the member of cluster
     * @return always true,currently not false
     */
    public boolean clusterMemberDown(Member member) {
        if (isLocalAddress(member.address())) {
            return true;
        }
        for (ActorSelection actorSelection : remoteActors) {
            LOG.info("clusterMemberDown: actorSelection is " + actorSelection.pathString() + ",member is "
                    + member.address());
            if (actorSelection.pathString().contains(member.address().toString())) {
                remoteActors.remove(actorSelection);
                String actorPath = member.address() + communicator.path().toStringWithoutAddress();
                // remove all remote channels related with the specified cluster
                // member
                deviceTable.removeAll(actorPath);
                LOG.info("Succed to remove the remote actor when Member(" + member.address()
                        + ") is down or unreachable.");
                return true;
            }
        }
        LOG.info("Failed to remove the remote actor when Member(" + member.address() + ") is down or unreachable.");
        return false;
    }

    private boolean isLocalActor(String server) {
        try {
            if (InetAddress.getLocalHost().getHostName().equalsIgnoreCase(server)) {
                return true;
            }
        } catch (UnknownHostException e) {
            if (LOG.isDebugEnabled()) {
                e.printStackTrace();
            }
            LOG.warn("Failed to get local hostname!error message is " + e.getMessage());
        }
        return false;
    }

    /**
     * get singleton instance of broker service
     *
     * @return singleton instance of broker service
     */
    public static UscRouteBrokerService getInstance() {
        return service;
    }

    /**
     * check if the route identifier is local session using remote channel for
     * intercepting the request from local channel
     *
     * @param routeId
     *            route identifier
     * @return true for local route identifier, false for others
     */
    public boolean isLocalRemoteSession(UscRouteIdentifier routeId) {
        return localSessionManager.isRemoteMessage(routeId);
    }

    /**
     * check if the route identifier is the remote session using local channel
     * for intercepting the response from agent channel
     *
     * @param routeId
     *            route identifier
     * @return true for remote route identifier, false for others
     */
    public boolean isRemoteSession(UscRouteIdentifier routeId) {
        if (routeId == null)
            return false;
        return remoteSessionManager.isRemoteSession(routeId);
    }

    /**
     * get actor which has the remote channel of local route identifier
     *
     * @param localRouteId
     *            local route identifier
     * @return actorRef
     */
    public ActorRef getRemoteActorForRequest(UscRouteIdentifier localRouteId) {
        // since route identifier has different hash code , even it is the child
        // of remote channel
        UscRemoteChannelIdentifier remoteChannel = new UscRemoteChannelIdentifier(localRouteId.getInetAddress(),
                localRouteId.getChannelType());
        return deviceTable.getActorRef(remoteChannel);
    }

    /**
     * get actor which sending request to local channel, and will call back to
     * it
     *
     * @param localRouteId
     *            local route identifier which geting from agent channel
     * @return call back actorRef
     */
    public ActorRef getRemoteActorForResponse(UscRouteIdentifier localRouteId) {
        return remoteSessionManager.getActorRef(localRouteId);
    }

    /**
     * check if exist the remote channel in remote device table
     *
     * @param remoteChannel
     *            remote channel
     * @return true for exist,false for others
     */
    public boolean existRemoteChannel(UscRemoteChannelIdentifier remoteChannel) {
        return deviceTable.existRemoteChannel(remoteChannel);
    }

    private UscRouteIdentifier getRemoteRouteIdentifier(UscRouteIdentifier localRouteId) {
        return remoteSessionManager.getRemoteRouteIdentifier(localRouteId);
    }

    /**
     * add remote channel and communicator which belongs to the controller which
     * connected with the remote channel
     *
     * @param remoteChannel
     *            remote channel
     * @param communicator
     *            communicator
     */
    public void addMountedDevice(UscRemoteChannelIdentifier remoteChannel, ActorRef communicator) {
        // since the hash code is different,it can not use UscRouteIdentifier as
        // a UscRemoteChannelIdentifier
        UscRemoteChannelIdentifier filteredRemoteChannel = new UscRemoteChannelIdentifier(
                remoteChannel.getInetAddress(), remoteChannel.getChannelType());
        deviceTable.addEntry(filteredRemoteChannel, communicator);
        if (communicator.compareTo(this.communicator) != 0) {
            ActorSelection remoteActorSelection = actorSystem.actorSelection(communicator.path());
            if (remoteActorSelection != null) {
                remoteActors.add(remoteActorSelection);
            } else {
                LOG.error("Failed to get actor selection for " + communicator.path());
            }
        }
    }

    /**
     * remove remote channel and communicator which belongs to the controller
     * which connected with the remote channel
     *
     * @param remoteChannel
     *            remote channel
     * @param communicator
     *            communicator
     */
    public void removeMountedDevice(UscRemoteChannelIdentifier remoteChannel, ActorRef communicator) {
        deviceTable.removeEntry(remoteChannel, communicator.toString());
        // send channel connection exception to all related local session
        localSessionManager.removeAll(remoteChannel);
        monitor.onEvent(new UscChannelCloseEvent(remoteChannel.getIp(), remoteChannel.getRemoteChannelType()));
        LOG.info("Remove remote channel {}", remoteChannel);
    }

    /**
     * add local session for processing the response which getting from remote
     * controller
     *
     * @param localRouteId
     *            local route identifier
     * @param serverChannel
     *            server local channel
     */
    public void addLocalSession(UscRouteIdentifier localRouteId, LocalChannel serverChannel) {
        localSessionManager.addEntry(localRouteId, serverChannel);
        monitor.onEvent(new UscSessionCreateEvent(localRouteId.getIp(), localRouteId.getRemoteChannelType(),
                localRouteId.getSessionId() + "", localRouteId.getApplicationPort()));
    }

    /**
     * remove local session for processing the response which getting from
     * remote controller
     *
     * @param localRouteId
     *            local route identifier
     */
    public void removeLocalSession(UscRouteIdentifier localRouteId) {
        localSessionManager.removeEntry(localRouteId);
        monitor.onEvent(new UscSessionCloseEvent(localRouteId.getIp(), localRouteId.getRemoteChannelType(),
                localRouteId.getSessionId() + ""));
        LOG.info("Remove local session {}", localRouteId);
    }

    /**
     * create a new lcoal session id for remote caller
     *
     * @param remoteRouteId
     *            remote route identifier
     * @return the new session id, the id is descending from max session id
     */
    public int createNewLocalSessionId(UscRouteIdentifier remoteRouteId) {
        Integer maxSessionId = sessionIdMap.get(remoteRouteId);
        if (maxSessionId == null) {
            sessionIdMap.put(remoteRouteId, 1);
            return MAX_FIXED_SESSION_ID;
        } else {
            int sessionId = MAX_FIXED_SESSION_ID - maxSessionId;
            sessionIdMap.put(remoteRouteId, maxSessionId + 1);
            return sessionId;
        }
    }

    /**
     * get the server channel for sending request to remote using the particular
     * route identifier
     *
     * @param localRouteId
     *            local route identifier
     * @return server local channel
     */
    public LocalChannel getRequestSource(UscRouteIdentifier localRouteId) {
        return localSessionManager.getServerChannel(localRouteId);
    }

    /**
     * send local remote request or notice to remote actor
     *
     * @param message
     *            remote message
     */
    public void sendRequest(UscRemoteMessage message) {
        UscRouteIdentifier routeId = message.getRouteIdentifier();
        ActorRef remoteActorRef = getRemoteActorForRequest(routeId);
        if (remoteActorRef != null) {
            remoteActorRef.tell(message, communicator);
            if (message instanceof UscRemoteDataMessage) {
                monitor.onEvent(new UscSessionTransactionEvent(routeId.getIp(), routeId.getRemoteChannelType(),
                        routeId.getSessionId() + "", 0, ((UscRemoteDataMessage) message).getPayload().length));
            }
        } else {
            LOG.error("Failed to send request,since not found any remote actoRef for remote channel:" + routeId);
        }
    }

    /**
     * broad cast message to all managed remote actors,like add channel event
     * message
     *
     * @param message
     *            remote message
     */
    public void broadcastMessage(UscRemoteMessage message) {
        if (remoteActors.size() == 0) {
            // normally will not enter here, since cluster member up event will
            // happen at before
            updateActorListFromCluster();
        }
        if (remoteActors.size() == 0) {
            LOG.warn(
                    "Failed to send broadcast message to all remote actor, since currently there is no remote actor!Remote actor list is empty!");
            // TODO broadcast message
            for (ActorRef actorRef : deviceTable.getActorRefList()) {
                if (actorRef.compareTo(communicator) != 0) {
                    actorRef.tell(message, communicator);
                }
            }
        } else {
            LOG.trace("Start to send broadcast message to " + remoteActors.size() + " remote acotrs.");
            for (ActorSelection actorSelection : remoteActors) {
                actorSelection.tell(message, communicator);
            }
        }
    }

    /**
     * send local USC channel response to remote session
     *
     * @param localRouteId
     *            local route identifier
     * @param payload
     *            response pay load, no usc header
     */
    public void sendResponse(UscRouteIdentifier localRouteId, byte[] payload) {
        ActorRef remoteActor = getRemoteActorForResponse(localRouteId);
        if (remoteActor != null) {
            UscRouteIdentifier remoteRouteId = getRemoteRouteIdentifier(localRouteId);
            // frame sessionId is local sessionId,can not be used in remote
            UscRemoteDataMessage message = new UscRemoteDataMessage(remoteRouteId, payload, false);
            remoteActor.tell(message, communicator);
        } else {
            LOG.error("Not found the remote actor for routeIdentifier (" + localRouteId + ")");
        }
    }

    /**
     * send local USC channel exception response to remote session
     *
     * @param localRouteId
     *            local route identifier
     * @param exception
     *            exception of agent channel
     */
    public void sendException(UscRouteIdentifier localRouteId, UscException exception) {
        ActorRef remoteActor = getRemoteActorForResponse(localRouteId);
        if (remoteActor != null) {
            UscRouteIdentifier remoteRouteId = getRemoteRouteIdentifier(localRouteId);
            // frame sessionId is local sessionId,can not be used in remote
            UscRemoteExceptionMessage message = new UscRemoteExceptionMessage(remoteRouteId, exception);
            remoteActor.tell(message, communicator);
        } else {
            LOG.error("Not found the remote actor for routeIdentifier (" + localRouteId + ")");
        }
    }

    /**
     * process remote request for remote caller(sender)
     *
     * @param message
     *            request content
     * @param sender
     *            request caller
     */
    public void processRequest(UscRemoteDataMessage message, ActorRef sender) {
        UscRouteIdentifier remoteRouteId = message.getRouteIdentifier();
        // find response remote session
        UscRouteIdentifier localRouteId = remoteSessionManager.getLocalRouteIdentifier(remoteRouteId);
        io.netty.channel.Channel agentChannel = null;
        if (localRouteId == null) {

            // first time for this route id
            UscChannelImpl localUscChannel = getLocalUscChannel(remoteRouteId);
            if (localUscChannel == null) {
                // since local USC Channel is not exist,send error response
                // directly
                UscRemoteExceptionMessage responseMessage = new UscRemoteExceptionMessage(remoteRouteId,
                        new UscChannelException("Remote channel is not existed in here!"));
                sender.tell(responseMessage, communicator);
                return;
            }

            // add new remote session for first time
            UscRouteIdentifierData routeData = new UscRouteIdentifierData(sender, remoteRouteId,
                    createNewLocalSessionId(remoteRouteId), localUscChannel.getChannel());
            remoteSessionManager.addEntry(routeData);
            LOG.info("Added remote session for " + routeData);
            localRouteId = routeData.getLocalRouteIdentifier();
            agentChannel = localUscChannel.getChannel();
        } else {
            LOG.trace("Find used channel, send request to agent directly.");
            // for next time request from same remote route id
            agentChannel = remoteSessionManager.getAgentChannel(localRouteId);
        }

        // change remote session id to local session id

        UscData data = new UscData(message.getRouteIdentifier().getApplicationPort(), localRouteId.getSessionId(),
                Unpooled.copiedBuffer(message.getPayload()));
        agentChannel.writeAndFlush(data);
        return;

    }

    /**
     * process response which getting from remote controller
     *
     * @param message
     *            response content
     */
    public void processResponse(UscRemoteMessage message) {
        if (message instanceof UscRemoteDataMessage) {
            LOG.info("get response from remote channel, for " + message.getRouteIdentifier());
            UscRemoteDataMessage temp = (UscRemoteDataMessage) message;
            UscRouteIdentifier localRouteId = message.getRouteIdentifier();
            LocalChannel serverChannel = getRequestSource(localRouteId);
            if (serverChannel != null) {
                LOG.trace("Write response to serverChannel(" + serverChannel.hashCode() + "), content "
                        + new String(temp.getPayload()));
                serverChannel.writeAndFlush(Unpooled.copiedBuffer(temp.getPayload()));
                monitor.onEvent(
                        new UscSessionTransactionEvent(localRouteId.getIp(), localRouteId.getRemoteChannelType(),
                                localRouteId.getSessionId() + "", temp.getPayload().length, 0));
            } else {
                LOG.error("Failed to find the server channel for routeIdentifier({}), can't process response({})!",
                        temp.getRouteIdentifier(), message);
            }
        } else {
            LOG.warn("The message type is different, it can't be processed.message type is {}", message.getClass());
        }
    }

    /**
     * process remote exception message
     *
     * @param message
     */
    public void processException(UscRemoteExceptionMessage message) {
        UscException ex = message.getException();
        LocalChannel serverChannel = getRequestSource(message.getRouteIdentifier());
        serverChannel.writeAndFlush(ex);
        UscRouteIdentifier routeId = message.getRouteIdentifier();
        if (ex instanceof UscSessionException) {
            UscSessionException tmp = (UscSessionException) ex;
            monitor.onEvent(new UscSessionErrorEvent(routeId.getIp(), routeId.getRemoteChannelType(),
                    routeId.getSessionId() + "", tmp.getErrorCode().getCode(), UscErrorLevel.ERROR,
                    tmp.getMessage()));
        } else {
            LOG.warn("Unmonitored error event: error is {}, remote identifier is {}.", ex,
                    message.getRouteIdentifier());
        }
    }

    /**
     * get local usc channel through the remote channel identifier
     *
     * @param remoteChannel
     *            remote channel identifier
     * @return local corresponding usc channel
     */
    private UscChannelImpl getLocalUscChannel(UscRemoteChannelIdentifier remoteChannel) {
        UscConnectionManager connectionManager = connectionManagerMap.get(remoteChannel.getChannelType().name());
        if (connectionManager == null) {
            LOG.info("Current connection manager list is " + connectionManagerMap + ",size is "
                    + connectionManagerMap.size());
            LOG.error("Failed to get the connection manager for channel type(" + remoteChannel.getChannelType()
                    + "),so UscRemoteChannel(" + remoteChannel + ") is not found in local!");
            return null;
        }
        try {
            return connectionManager.getConnection(new UscDevice(remoteChannel.getInetAddress()),
                    remoteChannel.getChannelType());
        } catch (Exception e) {
            LOG.error("UscRemoteChannel(" + remoteChannel + ") is not found in local!error = " + e.getMessage());
            return null;
        }
    }

    /**
     * set connection manager for each channel type
     *
     * @param type
     *            channel type
     * @param connectionManager
     *            connection manager
     */
    public void setConnetionManager(ChannelType type, UscConnectionManager connectionManager) {
        connectionManagerMap.put(type.name(), connectionManager);
    }

    /**
     * destroy broker service
     */
    public void destroy() {
        if (actorSystem != null) {
            actorSystem.shutdown();
        }
    }

}