server/server.py
changeset 10235 684215aca046
parent 10232 cda1bdc3652e
child 10236 ef3059a692cb
equal deleted inserted replaced
10232:cda1bdc3652e 10235:684215aca046
     1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """Pyro RQL server"""
       
    19 
       
    20 __docformat__ = "restructuredtext en"
       
    21 
       
    22 import select
       
    23 from time import localtime, mktime
       
    24 
       
    25 from cubicweb.server.utils import TasksManager
       
    26 from cubicweb.server.repository import Repository
       
    27 
       
    28 class Finished(Exception):
       
    29     """raise to remove an event from the event loop"""
       
    30 
       
    31 class TimeEvent:
       
    32     """base event"""
       
    33     # timefunc = staticmethod(localtime)
       
    34     timefunc = localtime
       
    35 
       
    36     def __init__(self, absolute=None, period=None):
       
    37         # local time tuple
       
    38         if absolute is None:
       
    39             absolute = self.timefunc()
       
    40         self.absolute = absolute
       
    41         # optional period in seconds
       
    42         self.period = period
       
    43 
       
    44     def is_ready(self):
       
    45         """return  true if the event is ready to be fired"""
       
    46         now = self.timefunc()
       
    47         if self.absolute <= now:
       
    48             return True
       
    49         return False
       
    50 
       
    51     def fire(self, server):
       
    52         """fire the event
       
    53         must be overridden by concrete events
       
    54         """
       
    55         raise NotImplementedError()
       
    56 
       
    57     def update(self):
       
    58         """update the absolute date for the event or raise a finished exception
       
    59         """
       
    60         if self.period is None:
       
    61             raise Finished
       
    62         self.absolute = localtime(mktime(self.absolute) + self.period)
       
    63 
       
    64 
       
    65 class QuitEvent(TimeEvent):
       
    66     """stop the server"""
       
    67     def fire(self, server):
       
    68         server.repo.shutdown()
       
    69         server.quiting = True
       
    70 
       
    71 
       
    72 class RepositoryServer(object):
       
    73 
       
    74     def __init__(self, config):
       
    75         """make the repository available as a PyRO object"""
       
    76         self.config = config
       
    77         self.repo = Repository(config, TasksManager())
       
    78         self.ns = None
       
    79         self.quiting = None
       
    80         # event queue
       
    81         self.events = []
       
    82 
       
    83     def add_event(self, event):
       
    84         """add an event to the loop"""
       
    85         self.info('adding event %s', event)
       
    86         self.events.append(event)
       
    87 
       
    88     def trigger_events(self):
       
    89         """trigger ready events"""
       
    90         for event in self.events[:]:
       
    91             if event.is_ready():
       
    92                 self.info('starting event %s', event)
       
    93                 event.fire(self)
       
    94                 try:
       
    95                     event.update()
       
    96                 except Finished:
       
    97                     self.events.remove(event)
       
    98 
       
    99     def run(self, req_timeout=5.0):
       
   100         """enter the service loop"""
       
   101         # start repository looping tasks
       
   102         self.repo.start_looping_tasks()
       
   103         while self.quiting is None:
       
   104             try:
       
   105                 self.daemon.handleRequests(req_timeout)
       
   106             except select.error:
       
   107                 continue
       
   108             finally:
       
   109                 self.trigger_events()
       
   110 
       
   111     def quit(self):
       
   112         """stop the server"""
       
   113         self.add_event(QuitEvent())
       
   114 
       
   115     def connect(self, host='', port=0):
       
   116         """the connect method on the repository only register to pyro if
       
   117         necessary
       
   118         """
       
   119         self.daemon = self.repo.pyro_register(host)
       
   120 
       
   121     # server utilitities ######################################################
       
   122 
       
   123     def install_sig_handlers(self):
       
   124         """install signal handlers"""
       
   125         import signal
       
   126         self.info('installing signal handlers')
       
   127         signal.signal(signal.SIGINT, lambda x, y, s=self: s.quit())
       
   128         signal.signal(signal.SIGTERM, lambda x, y, s=self: s.quit())
       
   129 
       
   130 
       
   131     # these are overridden by set_log_methods below
       
   132     # only defining here to prevent pylint from complaining
       
   133     @classmethod
       
   134     def info(cls, msg, *a, **kw):
       
   135         pass
       
   136 
       
   137 from logging import getLogger
       
   138 from cubicweb import set_log_methods
       
   139 LOGGER = getLogger('cubicweb.reposerver')
       
   140 set_log_methods(RepositoryServer, LOGGER)