/* * 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.internal.node.xmlrpc; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.commons.logging.Log; import org.ros.internal.node.response.Response; import org.ros.internal.node.response.StatusCode; import org.ros.internal.node.server.ServerException; import org.ros.internal.node.server.SlaveServer; import org.ros.internal.node.topic.DefaultPublisher; import org.ros.internal.node.topic.DefaultSubscriber; import org.ros.internal.transport.ProtocolDescription; import org.ros.log.RosLogFactory; import org.ros.namespace.GraphName; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import java.util.concurrent.TimeUnit; /** * An XML RPC server endpoint for a slave server. * * @author damonkohler@google.com (Damon Kohler) */ public class SlaveXmlRpcEndpointImpl implements SlaveXmlRpcEndpoint { /** * The log to use for printing messages. */ private static final Log LOG = RosLogFactory.getLog(SlaveXmlRpcEndpointImpl.class); /** * The slave server. */ private final SlaveServer slaveServer; /** * Create a new SlaveXmlRpcEndpointImpl for the given SlaveServer. * * @param slave * the slave server */ public SlaveXmlRpcEndpointImpl(SlaveServer slave) { this.slaveServer = slave; } @Override public List<Object> getBusStats(String callerId) { return slaveServer.getBusStats(callerId); } @Override public List<Object> getBusInfo(String callerId) { List<Object> busInfo = slaveServer.getBusInfo(callerId); return Response.newSuccess("bus info", busInfo).toList(); } @Override public List<Object> getMasterUri(String callerId) { URI uri = slaveServer.getMasterUri(); return new Response<String>(StatusCode.SUCCESS, "", uri.toString()).toList(); } @Override public List<Object> shutdown(String callerId, String message) { LOG.info("Shutdown requested by " + callerId + " with message \"" + message + "\""); // Have to spawn a thread to shutdown the slave, otherwise you get a java.lang.IllegalStateException // for attempting to shut down the slave from a I/O thread: https://b2.corp.google.com/issues/23760053. // // Potential race condition here if server shutdown happens before the response is sent out. So will do the shutdown // in the future. // // TODO(keith): Be able to return responses but shut server down after response sent. slaveServer.getExecutorService().schedule(new Runnable() { @Override public void run() { doSlaveServerShutdown(); } }, 500, TimeUnit.MILLISECONDS); return Response.newSuccess("Shutdown successful.", null).toList(); } /** * Perform the slave server shutdown. */ private void doSlaveServerShutdown() { try { slaveServer.shutdown(); } catch (Throwable e) { LOG.error("Error shutting down node slave server", e); } } @Override public List<Object> getPid(String callerId) { try { int pid = slaveServer.getPid(); return Response.newSuccess("PID: " + pid, pid).toList(); } catch (UnsupportedOperationException e) { return Response.newFailure("Cannot retrieve PID on this platform.", -1).toList(); } } @Override public List<Object> getSubscriptions(String callerId) { Collection<DefaultSubscriber<?>> subscribers = slaveServer.getSubscriptions(); List<List<String>> subscriptions = Lists.newArrayList(); for (DefaultSubscriber<?> subscriber : subscribers) { subscriptions.add(subscriber.getTopicDeclarationAsList()); } return Response.newSuccess("Success", subscriptions).toList(); } @Override public List<Object> getPublications(String callerId) { Collection<DefaultPublisher<?>> publishers = slaveServer.getPublications(); List<List<String>> publications = Lists.newArrayList(); for (DefaultPublisher<?> publisher : publishers) { publications.add(publisher.getTopicDeclarationAsList()); } return Response.newSuccess("Success", publications).toList(); } /** * Update the slave server's entry for the given parameter and value. * * @param parameterName * the parameter name to update * @param parameterValue * the new value for the parameter * @return the Response for the update, which indicates success or failure. */ private List<Object> parameterUpdate(String parameterName, Object parameterValue) { if (slaveServer.paramUpdate(GraphName.of(parameterName), parameterValue) > 0) { return Response.newSuccess("Success", null).toList(); } return Response.newError("No subscribers for parameter key \"" + parameterName + "\".", null).toList(); } @Override public List<Object> paramUpdate(String callerId, String key, boolean value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, char value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, byte value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, short value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, int value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, double value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, String value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, List<?> value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, Vector<?> value) { return parameterUpdate(key, value); } @Override public List<Object> paramUpdate(String callerId, String key, Map<?, ?> value) { return parameterUpdate(key, value); } @Override public List<Object> publisherUpdate(String callerId, String topicName, Object[] publishers) { try { ArrayList<URI> publisherUris = new ArrayList<URI>(publishers.length); for (Object publisher : publishers) { URI uri = new URI(publisher.toString()); if (!uri.getScheme().equals("http") && !uri.getScheme().equals("https")) { return Response.newError("Unknown URI scheme sent in update.", 0).toList(); } publisherUris.add(uri); } slaveServer.publisherUpdate(callerId, topicName, publisherUris); return Response.newSuccess("Publisher update received.", 0).toList(); } catch (URISyntaxException e) { return Response.newError("Invalid URI sent in update.", 0).toList(); } } @Override public List<Object> requestTopic(String callerId, String topic, Object[] protocols) { Set<String> requestedProtocols = Sets.newHashSet(); for (int i = 0; i < protocols.length; i++) { requestedProtocols.add((String) ((Object[]) protocols[i])[0]); } ProtocolDescription protocol; try { protocol = slaveServer.requestTopic(topic, requestedProtocols); } catch (ServerException e) { return Response.newError(e.getMessage(), null).toList(); } List<Object> response = Response.newSuccess(protocol.toString(), protocol.toList()).toList(); if (LOG.isDebugEnabled()) { LOG.debug("requestTopic(" + topic + ", " + requestedProtocols + ") response: " + response.toString()); } return response; } }