org.ros.internal.node.server.master.MasterRegistrationManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ros.internal.node.server.master.MasterRegistrationManagerImpl.java

Source

/*
 * Copyright (C) 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.ros.internal.node.server.master;

import com.google.common.collect.Maps;

import org.apache.commons.logging.Log;
import org.ros.internal.node.service.ServiceIdentifier;
import org.ros.log.RosLogFactory;
import org.ros.master.client.TopicSystemState;
import org.ros.namespace.GraphName;
import org.ros.node.service.ServiceServer;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;

/**
 * Manages all registration logic for the {@link MasterServer}.
 *
 * <p>
 * This class is not thread-safe.
 *
 * @author khughes@google.com (Keith M. Hughes)
 */
public class MasterRegistrationManagerImpl {

    private static final Log log = RosLogFactory.getLog(MasterRegistrationManagerImpl.class);

    /**
     * A map from node names to the information about the mode.
     */
    private final Map<GraphName, NodeRegistrationInfo> nodes;

    /**
     * A {@link Map} from the name of the {@link ServiceServer} to the
     * {@link ServiceIdentifier}.
     */
    private final Map<GraphName, ServiceRegistrationInfo> services;

    /**
     * A {@link Map} from {@link TopicSystemState} name to the
     * {@link TopicRegistrationInfo} about the topic.
     */
    private final Map<GraphName, TopicRegistrationInfo> topics;

    /**
     * A listener for master registration events.
     */
    private final MasterRegistrationListener listener;

    public MasterRegistrationManagerImpl(MasterRegistrationListener listener) {
        this.listener = listener;
        nodes = Maps.newHashMap();
        services = Maps.newConcurrentMap();
        topics = Maps.newHashMap();
    }

    /**
     * Register a publisher.
     *
     * @param nodeName
     *          name of the node with the publisher
     * @param nodeSlaveUri
     *          URI of the slave server on the node
     * @param topicName
     *          then name of the topic
     * @param topicMessageType
     *          message type of the topic
     *
     * @return The registration information for the topic.
     */
    public TopicRegistrationInfo registerPublisher(GraphName nodeName, URI nodeSlaveUri, GraphName topicName,
            String topicMessageType) {
        if (log.isDebugEnabled()) {
            log.debug(String.format(
                    "Registering publisher topic %s with message type %s on node %s with slave URI %s", topicName,
                    topicMessageType, nodeName, nodeSlaveUri));
        }

        TopicRegistrationInfo topic = obtainTopicRegistrationInfo(topicName, true);
        NodeRegistrationInfo node = obtainNodeRegistrationInfo(nodeName, nodeSlaveUri);
        topic.addPublisher(node, topicMessageType);
        node.addPublisher(topic);

        return topic;
    }

    /**
     * Unregister a publisher.
     *
     * @param nodeName
     *          name of the node which has the publisher
     * @param topicName
     *          name of the publisher's topic
     *
     * @return {@code true} if the publisher was actually registered before the
     *         call.
     */
    public boolean unregisterPublisher(GraphName nodeName, GraphName topicName) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("Unregistering publisher of topic %s from node %s", topicName, nodeName));
        }

        TopicRegistrationInfo topic = obtainTopicRegistrationInfo(topicName, false);
        if (topic != null) {
            NodeRegistrationInfo node = nodes.get(nodeName);
            if (node != null) {
                node.removePublisher(topic);
                topic.removePublisher(node);

                potentiallyDeleteNode(node);

                return true;
            } else {
                // never was a node with that name
                if (log.isWarnEnabled()) {
                    log.warn(String.format("Received unregister publisher for topic %s on unknown node %s",
                            topicName, nodeName));
                }

                return false;
            }
        } else {
            // If no topic, there will be no node registration.
            if (log.isWarnEnabled()) {
                log.warn(String.format("Received unregister publisher for unknown topic %s on node %s", topicName,
                        nodeName));
            }

            return false;
        }
    }

    /**
     * Register a subscriber.
     *
     * @param nodeName
     *          name of the node with the subscriber
     * @param nodeSlaveUri
     *          URI of the slave server on the node
     * @param topicName
     *          then name of the topic
     * @param topicMessageType
     *          message type of the topic
     *
     * @return The registration information for the topic.
     */
    public TopicRegistrationInfo registerSubscriber(GraphName nodeName, URI nodeSlaveUri, GraphName topicName,
            String topicMessageType) {
        if (log.isDebugEnabled()) {
            log.debug(String.format(
                    "Registering subscriber topic %s with message type %s on node %s with slave URI %s", topicName,
                    topicMessageType, nodeName, nodeSlaveUri));
        }

        TopicRegistrationInfo topic = obtainTopicRegistrationInfo(topicName, true);
        NodeRegistrationInfo node = obtainNodeRegistrationInfo(nodeName, nodeSlaveUri);
        topic.addSubscriber(node, topicMessageType);
        node.addSubscriber(topic);

        return topic;
    }

    /**
     * Unregister a subscriber.
     *
     * @param nodeName
     *          name of the node which has the subscriber
     * @param topicName
     *          name of the subscriber's topic
     *
     * @return {@code true} if the subscriber was actually registered before the
     *         call.
     */
    public boolean unregisterSubscriber(GraphName nodeName, GraphName topicName) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("Unregistering subscriber of topic %s from node %s", topicName, nodeName));
        }

        TopicRegistrationInfo topic = obtainTopicRegistrationInfo(topicName, false);
        if (topic != null) {
            NodeRegistrationInfo node = nodes.get(nodeName);
            if (node != null) {
                node.removeSubscriber(topic);
                topic.removeSubscriber(node);
                potentiallyDeleteNode(node);
                return true;
            } else {
                // never was a node with that name
                if (log.isWarnEnabled()) {
                    log.warn(String.format("Received unregister subscriber for topic %s on unknown node %s",
                            topicName, nodeName));
                }
                return false;
            }
        } else {
            // If no topic, there will be no node registration.
            if (log.isWarnEnabled()) {
                log.warn(String.format("Received unregister subscriber for unknown topic %s on node %s", topicName,
                        nodeName));
            }
            return false;
        }
    }

    /**
     * Register a service.
     *
     * @param nodeName
     *          name of the node with the service
     * @param nodeSlaveUri
     *          URI of the slave server on the node
     * @param serviceName
     *          the name of the service
     * @param serviceUri
     *          URI of the service server on the node
     *
     * @return The registration information for the service.
     */
    public ServiceRegistrationInfo registerService(GraphName nodeName, URI nodeSlaveUri, GraphName serviceName,
            URI serviceUri) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("Registering service %s with server URI %s on node %s with slave URI %s",
                    serviceName, serviceUri, nodeName, nodeSlaveUri));
        }

        NodeRegistrationInfo node = obtainNodeRegistrationInfo(nodeName, nodeSlaveUri);

        ServiceRegistrationInfo service = services.get(serviceName);
        if (service != null) {
            NodeRegistrationInfo previousServiceNode = service.getNode();
            if (previousServiceNode == node) {
                // If node is the same, no need to do anything
                if (log.isWarnEnabled()) {
                    log.warn(String.format(
                            "Registering already known service %s with server URI %s on node %s with slave URI %s",
                            serviceName, serviceUri, nodeName, nodeSlaveUri));
                }
                return service;
            } else {
                // The service's node is changing.
                previousServiceNode.removeService(service);
                potentiallyDeleteNode(previousServiceNode);
            }
        }

        // Service didn't exist or the node is changing.
        service = new ServiceRegistrationInfo(serviceName, serviceUri, node);
        node.addService(service);

        services.put(serviceName, service);

        return service;
    }

    /**
     * Unregister a service.
     *
     * @param nodeName
     *          name of the node with the service
     * @param serviceName
     *          the name of the service
     * @param serviceUri
     *          URI of the service server on the node
     *
     * @return {@code true} if the service was actually registered before the
     *         call.
     */
    public boolean unregisterService(GraphName nodeName, GraphName serviceName, URI serviceUri) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("Unregistering service %s from node %s", serviceName, nodeName));
        }

        ServiceRegistrationInfo service = services.get(serviceName);
        if (service != null) {
            NodeRegistrationInfo node = nodes.get(nodeName);
            if (node != null) {
                // No need to keep service around.
                services.remove(serviceName);

                node.removeService(service);
                potentiallyDeleteNode(node);

                return true;
            } else {
                // never was a node with that name
                if (log.isWarnEnabled()) {
                    log.warn(String.format("Received unregister for service %s on unknown node %s", serviceName,
                            nodeName));
                }

                // TODO(keith): Should the node be removed anyway, or should only its
                // real node be able to unregister it?

                return false;
            }
        } else {
            // If no service, there will be no node registration.
            if (log.isWarnEnabled()) {
                log.warn(String.format("Received unregister for unknown service %s on node %s", serviceName,
                        nodeName));
            }

            return false;
        }
    }

    /**
     * Get all topics registered.
     *
     * @return An immutable collection of topics.
     */
    public Collection<TopicRegistrationInfo> getAllTopics() {
        return Collections.unmodifiableCollection(topics.values());
    }

    /**
     * Get the information known about a topic.
     *
     * @param topicName
     *          the name of the topic
     *
     * @return The information about the topic. Can be {@code null} if the topic
     *         was never registered.
     */
    public TopicRegistrationInfo getTopicRegistrationInfo(GraphName topicName) {
        return topics.get(topicName);
    }

    /**
     * Get the information known about a node.
     *
     * @param nodeName
     *          the name of the node
     *
     * @return The information about the node. Can be {@code null} if no topic was
     *         ever registered for the node.
     */
    public NodeRegistrationInfo getNodeRegistrationInfo(GraphName nodeName) {
        return nodes.get(nodeName);
    }

    /**
     * Get all services registered.
     *
     * @return An immutable collection of services.
     */
    public Collection<ServiceRegistrationInfo> getAllServices() {
        return Collections.unmodifiableCollection(services.values());
    }

    /**
     * Get the information known about a service.
     *
     * @param serviceName
     *          the name of the service
     *
     * @return The information about the service. Can be {@code null} if there is
     *         no service registered with the given name.
     */
    public ServiceRegistrationInfo getServiceRegistrationInfo(GraphName serviceName) {
        return services.get(serviceName);
    }

    /**
     * Get the {@link TopicRegistrationInfo} for the given topic name.
     *
     * @param topicName
     *          the name of the topic
     * @param shouldCreate
     *          {@code true} if a new one should be created if it isn't found
     *
     * @return The registration info for the topic. A new one will be created if
     *         none exists and on.
     */
    private TopicRegistrationInfo obtainTopicRegistrationInfo(GraphName topicName, boolean shouldCreate) {
        TopicRegistrationInfo info = topics.get(topicName);
        if (info == null && shouldCreate) {
            info = new TopicRegistrationInfo(topicName);
            topics.put(topicName, info);
        }

        return info;
    }

    /**
     * Get the {@link NodeRegistrationInfo} for the given node slave identifier.
     *
     * @param nodeName
     *          the name of the node
     * @param nodeSlaveUri
     *          the URI for the node's slave server
     *
     * @return The registration info for the node. A new one will be created if
     *         none exists.
     */
    private NodeRegistrationInfo obtainNodeRegistrationInfo(GraphName nodeName, URI nodeSlaveUri) {
        NodeRegistrationInfo node = nodes.get(nodeName);
        if (node != null) {
            // The node exists. Any need to shut it down?
            if (node.getNodeSlaveUri().equals(nodeSlaveUri)) {
                // OK, same URI so can just return it.
                return node;
            }

            // The node is switching slave URIs, so we need a new one.
            potentiallyDeleteNode(node);
            cleanupNode(node);
            try {
                listener.onNodeReplacement(node);
            } catch (Exception e) {
                // No matter what, we want to keep going
                log.error("Error during onNodeReplacement call", e);
            }
        }

        // Either no existing node, or the old node needs to go away
        node = new NodeRegistrationInfo(nodeName, nodeSlaveUri);
        nodes.put(nodeName, node);

        return node;
    }

    /**
     * A node is being replaced. Clean it up. This includes unregistering from
     * topic objects.
     *
     * @param node
     *          the node being replaced
     */
    private void cleanupNode(NodeRegistrationInfo node) {
        for (TopicRegistrationInfo topic : node.getPublishers()) {
            topic.removePublisher(node);
        }

        for (TopicRegistrationInfo topic : node.getSubscribers()) {
            topic.removeSubscriber(node);
        }

        for (ServiceRegistrationInfo service : node.getServices()) {
            services.remove(service.getServiceName());
        }
    }

    /**
     * Remove a node from registration if it no longer has any registrations.
     *
     * @param node
     *          the node to possibly remove
     */
    private void potentiallyDeleteNode(NodeRegistrationInfo node) {
        if (!node.hasRegistrations()) {
            nodes.remove(node.getNodeName());
        }
    }
}