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) |
|