Package roslib :: Module xmlrpc
[frames] | no frames]

Source Code for Module roslib.xmlrpc

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2008, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32  # 
 33  # Revision $Id: xmlrpc.py 13721 2011-04-29 16:30:36Z kwc $ 
 34   
 35  """ 
 36  Common XML-RPC for higher-level libraries running XML-RPC libraries in 
 37  ROS. In particular, this library provides common handling for URI 
 38  calculation based on ROS environment variables. 
 39   
 40  The common entry point for most libraries is the L{XmlRpcNode} class. 
 41  """ 
 42   
 43  import logging 
 44  import socket 
 45  import string 
 46  import thread 
 47  import traceback 
 48  from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler 
 49  import SocketServer 
 50  import select 
 51   
 52  import roslib.network 
 53  import roslib.exceptions 
 54   
55 -class SilenceableXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
56 - def log_message(self, format, *args):
57 if 0: 58 SimpleXMLRPCRequestHandler.log_message(self, format, *args)
59
60 -class ThreadingXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
61 """ 62 Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent 63 requests via threading. Also makes logging toggleable. 64 """
65 - def __init__(self, addr, log_requests=1):
66 """ 67 Overrides SimpleXMLRPCServer to set option to allow_reuse_address. 68 """ 69 # allow_reuse_address defaults to False in Python 2.4. We set it 70 # to True to allow quick restart on the same port. This is equivalent 71 # to calling setsockopt(SOL_SOCKET,SO_REUSEADDR,1) 72 self.allow_reuse_address = True 73 SimpleXMLRPCServer.__init__(self, addr, SilenceableXMLRPCRequestHandler, log_requests)
74
75 - def handle_error(self, request, client_address):
76 """ 77 override ThreadingMixin, which sends errors to stderr 78 """ 79 if logging and traceback: 80 logger = logging.getLogger('xmlrpc') 81 if logger: 82 logger.error(traceback.format_exc())
83
84 -class ForkingXMLRPCServer(SocketServer.ForkingMixIn, SimpleXMLRPCServer):
85 """ 86 Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent 87 requests via forking. Also makes logging toggleable. 88 """
89 - def __init__(self, addr, request_handler=SilenceableXMLRPCRequestHandler, log_requests=1):
90 SimpleXMLRPCServer.__init__(self, addr, request_handler, log_requests)
91 92
93 -class XmlRpcHandler(object):
94 """ 95 Base handler API for handlers used with XmlRpcNode. Public methods will be 96 exported as XML RPC methods. 97 """ 98
99 - def _ready(self, uri):
100 """ 101 callback into handler to inform it of XML-RPC URI 102 """ 103 pass
104
105 -class XmlRpcNode(object):
106 """ 107 Generic XML-RPC node. Handles the additional complexity of binding 108 an XML-RPC server to an arbitrary port. 109 XmlRpcNode is initialized when the uri field has a value. 110 """ 111
112 - def __init__(self, port=0, rpc_handler=None, on_run_error=None):
113 """ 114 XML RPC Node constructor 115 @param port: port to use for starting XML-RPC API. Set to 0 or omit to bind to any available port. 116 @type port: int 117 @param rpc_handler: XML-RPC API handler for node. 118 @type rpc_handler: XmlRpcHandler 119 @param on_run_error: function to invoke if server.run() throws 120 Exception. Server always terminates if run() throws, but this 121 enables cleanup routines to be invoked if server goes down, as 122 well as include additional debugging. 123 @type on_run_error: fn(Exception) 124 """ 125 super(XmlRpcNode, self).__init__() 126 127 self.handler = rpc_handler 128 self.uri = None # initialize the property now so it can be tested against, will be filled in later 129 self.server = None 130 if port and isinstance(port, basestring): 131 port = string.atoi(port) 132 self.port = port 133 self.is_shutdown = False 134 self.on_run_error = on_run_error
135
136 - def shutdown(self, reason):
137 """ 138 Terminate i/o connections for this server. 139 @param reason: human-readable debug string 140 @type reason: str 141 """ 142 self.is_shutdown = True 143 if self.server: 144 server = self.server 145 handler = self.handler 146 self.handler = self.server = self.port = self.uri = None 147 if handler: 148 handler._shutdown(reason) 149 if server: 150 server.socket.close() 151 server.server_close()
152
153 - def start(self):
154 """ 155 Initiate a thread to run the XML RPC server. Uses thread.start_new_thread. 156 """ 157 thread.start_new_thread(self.run, ())
158
159 - def set_uri(self, uri):
160 """ 161 Sets the XML-RPC URI. Defined as a separate method as a hood 162 for subclasses to bootstrap initialization. Should not be called externally. 163 @param uri: XMLRPC URI. 164 @type uri: str 165 """ 166 self.uri = uri
167
168 - def run(self):
169 try: 170 self._run() 171 except Exception, e: 172 if self.is_shutdown: 173 pass 174 elif self.on_run_error is not None: 175 self.on_run_error(e) 176 else: 177 raise
178
179 - def _run(self):
180 """ 181 Main processing thread body. 182 @raise socket.error: If server cannot bind 183 @raise roslib.exceptions.ROSLibException: If unknown error occurs 184 """ 185 logger = logging.getLogger('xmlrpc') 186 try: 187 log_requests = 0 188 port = self.port or 0 #0 = any 189 190 bind_address = roslib.network.get_bind_address() 191 logger.info("XML-RPC server binding to %s"%bind_address) 192 193 self.server = ThreadingXMLRPCServer((bind_address, port), log_requests) 194 self.port = self.server.server_address[1] #set the port to whatever server bound to 195 if not self.port: 196 self.port = self.server.socket.getsockname()[1] #Python 2.4 197 if not self.port: 198 raise roslib.exceptions.ROSLibException("Unable to retrieve local address binding") 199 200 # #528: semi-complicated logic for determining XML-RPC URI 201 # - if ROS_IP/ROS_HOSTNAME is set, use that address 202 # - if the hostname returns a non-localhost value, use that 203 # - use whatever roslib.network.get_local_address() returns 204 uri = None 205 override = roslib.network.get_address_override() 206 if override: 207 uri = 'http://%s:%s/'%(override, self.port) 208 else: 209 try: 210 hostname = socket.gethostname() 211 if hostname and not hostname == 'localhost' and not hostname.startswith('127.'): 212 uri = 'http://%s:%s/'%(hostname, self.port) 213 except: 214 pass 215 if not uri: 216 uri = 'http://%s:%s/'%(roslib.network.get_local_address(), self.port) 217 self.set_uri(uri) 218 219 #print "... started XML-RPC Server", self.uri 220 logger.info("Started XML-RPC server [%s]", self.uri) 221 222 self.server.register_multicall_functions() 223 self.server.register_instance(self.handler) 224 225 except socket.error, (n, errstr): 226 if n == 98: 227 msg = "ERROR: Unable to start XML-RPC server, port %s is already in use"%self.port 228 else: 229 msg = "ERROR: Unable to start XML-RPC server: %s"%errstr 230 logger.error(msg) 231 print msg 232 raise #let higher level catch this 233 234 if self.handler is not None: 235 self.handler._ready(self.uri) 236 logger.info("xml rpc node: starting XML-RPC server") 237 while not self.is_shutdown: 238 try: 239 self.server.serve_forever() 240 except (IOError,select.error) as (errno, errstr): 241 # check for interrupted call, which can occur if we're 242 # embedded in a program using signals. All other 243 # exceptions break _run. 244 if self.is_shutdown: 245 pass 246 elif errno != 4: 247 self.is_shutdown = True 248 logger.error("serve forever IOError: %s, %s"%(errno, errstr))
249