1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
57 if 0:
58 SimpleXMLRPCRequestHandler.log_message(self, format, *args)
59
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
70
71
72 self.allow_reuse_address = True
73 SimpleXMLRPCServer.__init__(self, addr, SilenceableXMLRPCRequestHandler, log_requests)
74
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
85 """
86 Adds ThreadingMixin to SimpleXMLRPCServer to support multiple concurrent
87 requests via forking. Also makes logging toggleable.
88 """
90 SimpleXMLRPCServer.__init__(self, addr, request_handler, log_requests)
91
92
94 """
95 Base handler API for handlers used with XmlRpcNode. Public methods will be
96 exported as XML RPC methods.
97 """
98
100 """
101 callback into handler to inform it of XML-RPC URI
102 """
103 pass
104
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
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
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
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
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
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
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
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]
195 if not self.port:
196 self.port = self.server.socket.getsockname()[1]
197 if not self.port:
198 raise roslib.exceptions.ROSLibException("Unable to retrieve local address binding")
199
200
201
202
203
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
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
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
242
243
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