Gnumed.pycommon.gmScriptingListener

Package Gnumed :: Package pycommon :: Module gmScriptingListener |
[frames] | no frames]

# Source Code for Module Gnumed.pycommon.gmScriptingListener

      1  """GNUmed scripting listener.
      2
      3  This module implements threaded listening for scripting.
      4  """
      5  #=====================================================================
      6  __version__ = "$Revision: 1.7 $"
      7  __author__ = "K.Hilbert <karsten.hilbert@gmx.net>"
      8
      9  import sys, time, threading, select, logging
     10  import xmlrpc.server
     11
     12
     13  _log = logging.getLogger('gm.scripting')
     14
     15  #=====================================================================
     16 -class cScriptingListener:
     17
     18          # FIXME: this should use /var/run/gnumed/xml-rpc-port.pid
     19          # FIXME: and store the current port there
     20
     21          """This class handles how GNUmed listens for external requests.
     22
     23          It starts an XML-RPC server and forks a thread which
     24          listens for incoming requests. Those requests are then
     25          handed over to a macro executor and the results handed
     26          back to the caller.
     27          """
     28 -        def __init__(self, port = None, macro_executor = None, poll_interval = 3):
     29                  # listener thread will regularly try to acquire
     30                  # this lock, when it succeeds it will quit
     31                  self._quit_lock = threading.Lock()
     32                  if not self._quit_lock.acquire(0):
     33                          _log.error('cannot acquire thread quit lock !?! aborting')
     34                          import thread
     35                          raise thread.error("cannot acquire thread quit-lock")
     36
     37                  # check for data every 'poll_interval' seconds
     38                  self._poll_interval = poll_interval
     39                  # localhost only for somewhat better security
     40                  self._listener_address = '127.0.0.1'
     41                  self._port = int(port)
     42                  self._macro_executor = macro_executor
     43
     44                  self._server = xmlrpc.server.SimpleXMLRPCServer(addr=(self._listener_address, self._port), logRequests=False)
     45                  self._server.register_instance(self._macro_executor)
     46                  self._server.allow_reuse_address = True
     47
     48                  self._thread = threading.Thread (
     49                          target = self._process_RPCs,
     50                          name = self.__class__.__name__
     51                  )
     52                  self._thread.setDaemon(True)
     53                  self._thread.start()
     54
     55                  _log.info('scripting listener started on [%s:%s]' % (self._listener_address, self._port))
     56                  _log.info('macro executor: %s' % self._macro_executor)
     57                  _log.info('poll interval: %s seconds', self._poll_interval)
     58          #-------------------------------
     59          # public API
     60          #-------------------------------
     61 -        def shutdown(self):
     62                  """Cleanly shut down. Complement to __init__()."""
     63
     64                  if self._thread is None:
     65                          return
     66
     67                  _log.info('stopping frontend scripting listener thread')
     68                  self._quit_lock.release()
     69                  try:
     70                          # give the worker thread time to terminate
     71                          self._thread.join(self._poll_interval+5)
     72                          try:
     73                                  if self._thread.isAlive():
     74                                          _log.error('listener thread still alive after join()')
     75                                          _log.debug('active threads: %s' % threading.enumerate())
     76                          except Exception:
     77                                  pass
     78                  except Exception:
     79                          print sys.exc_info()
     80
     81                  self._thread = None
     82
     83                  try:
     84                          self._server.socket.shutdown(2)
     85                  except Exception:
     86                          _log.exception('cannot cleanly shutdown(5) scripting listener socket')
     87
     88                  try:
     89                          self._server.socket.close()
     90                  except Exception:
     91                          _log.exception('cannot cleanly close() scripting listener socket')
     92          #-------------------------------
     93          # internal helpers
     94          #-------------------------------
     95 -        def _process_RPCs(self):
     96                  """The actual thread code."""
     97                  while 1:
     98                          if self._quit_lock.acquire(0):
     99                                  break
    100                          time.sleep(0.35)                                        # give others time to acquire lock
    101                          if self._quit_lock.acquire(0):
    102                                  break
    103                          # wait at most self.__poll_interval for new data
    104                          ready_input_sockets = select.select([self._server.socket], [], [], self._poll_interval)[0]
    105                          # any input available ?
    106                          if len(ready_input_sockets) != 0:
    107                                  # we may be in __del__ so we might fail here
    108                                  try:
    109                                          self._server.handle_request()
    110                                  except Exception:
    111                                          print("cannot serve RPC")
    112                                          break
    113                                  if self._quit_lock.acquire(0):
    114                                          break
    115                                  time.sleep(0.25)
    116                                  if self._quit_lock.acquire(0):
    117                                          break
    118                          else:
    119                                  time.sleep(0.35)
    120                                  if self._quit_lock.acquire(0):
    121                                          break
    122
    123                  # exit thread activity
    124                  return
    125  #=====================================================================
    126  # main
    127  #=====================================================================
    128  if __name__ == "__main__":
    129
    130          import xmlrpc.client
    131
    132          #-------------------------------
    133 -        class runner:
    134 -                def tell_time(self):
    135                          return time.asctime()
    136          #-------------------------------
    137          if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
    138                  import xmlrpclib
    139
    140                  try:
    141                          listener = cScriptingListener(macro_executor=runner(), port=9999)
    142                  except Exception:
    143                          _log.exception('cannot instantiate scripting listener')
    144                          sys.exit(1)
    145
    146                  s = xmlrpclib.client.ServerProxy('http://localhost:9999')
    147                  try:
    148                          t = s.tell_time()
    149                          print t
    150                  except Exception:
    151                          _log.exception('cannot interact with server')
    152
    153                  listener.shutdown()
    154