Java tutorial
/* * Copyright (C) 2011 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.node; import com.google.common.base.Preconditions; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import org.apache.commons.logging.Log; import org.ros.concurrent.DefaultScheduledExecutorService; import org.ros.log.RosLogFactory; import org.ros.namespace.GraphName; import java.util.Collection; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; /** * Executes {@link NodeMain}s in separate threads. * * @author damonkohler@google.com (Damon Kohler) */ public class DefaultNodeMainExecutor implements NodeMainExecutor { private static final Log log = RosLogFactory.getLog(DefaultNodeMainExecutor.class); private final NodeFactory nodeFactory; private final ScheduledExecutorService scheduledExecutorService; private final Multimap<GraphName, ConnectedNode> connectedNodes; private final BiMap<Node, NodeMain> nodeMains; private class RegistrationListener implements NodeListener { @Override public void onStart(ConnectedNode connectedNode) { registerNode(connectedNode); } @Override public void onShutdown(Node node) { } @Override public void onShutdownComplete(Node node) { unregisterNode(node); } @Override public void onError(Node node, Throwable throwable) { log.error("Node error.", throwable); unregisterNode(node); } } /** * @return an instance of {@link DefaultNodeMainExecutor} that uses a * {@link ScheduledExecutorService} that is suitable for both * executing tasks immediately and scheduling tasks to execute in the * future */ public static NodeMainExecutor newDefault() { return newDefault(new DefaultScheduledExecutorService()); } /** * @return an instance of {@link DefaultNodeMainExecutor} that uses the * supplied {@link ExecutorService} */ public static NodeMainExecutor newDefault(ScheduledExecutorService executorService) { return new DefaultNodeMainExecutor(new DefaultNodeFactory(executorService), executorService); } /** * @param nodeFactory * {@link NodeFactory} to use for node creation. * @param scheduledExecutorService * {@link NodeMain}s will be executed using this */ private DefaultNodeMainExecutor(NodeFactory nodeFactory, ScheduledExecutorService scheduledExecutorService) { this.nodeFactory = nodeFactory; this.scheduledExecutorService = scheduledExecutorService; connectedNodes = Multimaps.synchronizedMultimap(HashMultimap.<GraphName, ConnectedNode>create()); nodeMains = Maps.synchronizedBiMap(HashBiMap.<Node, NodeMain>create()); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { DefaultNodeMainExecutor.this.shutdown(); } })); } @Override public ScheduledExecutorService getScheduledExecutorService() { return scheduledExecutorService; } @Override public void execute(final NodeMain nodeMain, final NodeConfiguration nodeConfiguration, final Collection<NodeListener> nodeListeners) { // NOTE(damonkohler): To avoid a race condition, we have to make our copy // of the NodeConfiguration in the current thread. final NodeConfiguration nodeConfigurationCopy = NodeConfiguration.copyOf(nodeConfiguration); nodeConfigurationCopy.setDefaultNodeName(nodeMain.getDefaultNodeName()); Preconditions.checkNotNull(nodeConfigurationCopy.getNodeName(), "Node name not specified."); if (log.isDebugEnabled()) { log.debug("Starting node: " + nodeConfigurationCopy.getNodeName()); } scheduledExecutorService.execute(new Runnable() { @Override public void run() { Collection<NodeListener> nodeListenersCopy = Lists.newArrayList(); nodeListenersCopy.add(new RegistrationListener()); nodeListenersCopy.add(nodeMain); if (nodeListeners != null) { nodeListenersCopy.addAll(nodeListeners); } // The new Node will call onStart(). Node node = nodeFactory.newNode(nodeConfigurationCopy, nodeListenersCopy); nodeMains.put(node, nodeMain); } }); } @Override public void execute(NodeMain nodeMain, NodeConfiguration nodeConfiguration) { execute(nodeMain, nodeConfiguration, null); } @Override public void shutdownNodeMain(NodeMain nodeMain) { Node node = nodeMains.inverse().get(nodeMain); if (node != null) { safelyShutdownNode(node); } } @Override public void shutdown() { synchronized (connectedNodes) { for (ConnectedNode connectedNode : connectedNodes.values()) { safelyShutdownNode(connectedNode); } } } /** * Trap and log any exceptions while shutting down the supplied {@link Node}. * * @param node * the {@link Node} to shut down */ private void safelyShutdownNode(Node node) { boolean success = true; try { node.shutdown(); } catch (Exception e) { // Ignore spurious errors during shutdown. log.error("Exception thrown while shutting down node.", e); // We don't expect any more callbacks from a node that throws an exception // while shutting down. So, we unregister it immediately. unregisterNode(node); success = false; } if (success) { log.info("Shutdown successful."); } } /** * Register a {@link ConnectedNode} with the {@link NodeMainExecutor}. * * @param connectedNode * the {@link ConnectedNode} to register */ private void registerNode(ConnectedNode connectedNode) { GraphName nodeName = connectedNode.getName(); synchronized (connectedNodes) { for (ConnectedNode illegalConnectedNode : connectedNodes.get(nodeName)) { System.err.println(String.format("Node name collision. Existing node %s (%s) will be shutdown.", nodeName, illegalConnectedNode.getUri())); illegalConnectedNode.shutdown(); } connectedNodes.put(nodeName, connectedNode); } } /** * Unregister a {@link Node} with the {@link NodeMainExecutor}. * * @param node * the {@link Node} to unregister */ private void unregisterNode(Node node) { connectedNodes.get(node.getName()).remove(node); nodeMains.remove(node); } }