|
1 """Pyro RQL server |
|
2 |
|
3 :organization: Logilab |
|
4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
6 """ |
|
7 __docformat__ = "restructuredtext en" |
|
8 |
|
9 import os |
|
10 import sys |
|
11 import select |
|
12 import warnings |
|
13 from time import localtime, mktime |
|
14 |
|
15 from cubicweb.cwconfig import CubicWebConfiguration |
|
16 from cubicweb.server.repository import Repository |
|
17 |
|
18 class Finished(Exception): |
|
19 """raise to remove an event from the event loop""" |
|
20 |
|
21 class TimeEvent: |
|
22 """base event""" |
|
23 # timefunc = staticmethod(localtime) |
|
24 timefunc = localtime |
|
25 |
|
26 def __init__(self, absolute=None, period=None): |
|
27 # local time tuple |
|
28 if absolute is None: |
|
29 absolute = self.timefunc() |
|
30 self.absolute = absolute |
|
31 # optional period in seconds |
|
32 self.period = period |
|
33 |
|
34 def is_ready(self): |
|
35 """return true if the event is ready to be fired""" |
|
36 now = self.timefunc() |
|
37 if self.absolute < now: |
|
38 return True |
|
39 return False |
|
40 |
|
41 def fire(self, server): |
|
42 """fire the event |
|
43 must be overridden by concrete events |
|
44 """ |
|
45 raise NotImplementedError() |
|
46 |
|
47 def update(self): |
|
48 """update the absolute date for the event or raise a finished exception |
|
49 """ |
|
50 if self.period is None: |
|
51 raise Finished |
|
52 self.absolute = localtime(mktime(self.absolute) + self.period) |
|
53 |
|
54 |
|
55 class QuitEvent(TimeEvent): |
|
56 """stop the server""" |
|
57 def fire(self, server): |
|
58 server.repo.shutdown() |
|
59 server.quiting = True |
|
60 |
|
61 |
|
62 class RepositoryServer(object): |
|
63 |
|
64 def __init__(self, config, debug=False): |
|
65 """make the repository available as a PyRO object""" |
|
66 self.config = config |
|
67 self.repo = Repository(config, debug=debug) |
|
68 self.ns = None |
|
69 self.quiting = None |
|
70 # event queue |
|
71 self.events = [] |
|
72 # start repository looping tasks |
|
73 |
|
74 def add_event(self, event): |
|
75 """add an event to the loop""" |
|
76 self.info('adding event %s', event) |
|
77 self.events.append(event) |
|
78 |
|
79 def trigger_events(self): |
|
80 """trigger ready events""" |
|
81 for event in self.events[:]: |
|
82 if event.is_ready(): |
|
83 self.info('starting event %s', event) |
|
84 event.fire(self) |
|
85 try: |
|
86 event.update() |
|
87 except Finished: |
|
88 self.events.remove(event) |
|
89 |
|
90 def run(self, req_timeout=5.0): |
|
91 """enter the service loop""" |
|
92 while self.quiting is None: |
|
93 try: |
|
94 self.daemon.handleRequests(req_timeout) |
|
95 except select.error: |
|
96 continue |
|
97 self.trigger_events() |
|
98 |
|
99 def quit(self): |
|
100 """stop the server""" |
|
101 self.add_event(QuitEvent()) |
|
102 |
|
103 def connect(self, host='', port=0): |
|
104 """the connect method on the repository only register to pyro if |
|
105 necessary |
|
106 """ |
|
107 self.daemon = self.repo.pyro_register(host) |
|
108 |
|
109 # server utilitities ###################################################### |
|
110 |
|
111 def install_sig_handlers(self): |
|
112 """install signal handlers""" |
|
113 import signal |
|
114 self.info('installing signal handlers') |
|
115 signal.signal(signal.SIGINT, lambda x, y, s=self: s.quit()) |
|
116 signal.signal(signal.SIGTERM, lambda x, y, s=self: s.quit()) |
|
117 |
|
118 def daemonize(self, pid_file=None): |
|
119 """daemonize the process""" |
|
120 # fork so the parent can exist |
|
121 if (os.fork()): |
|
122 return -1 |
|
123 # deconnect from tty and create a new session |
|
124 os.setsid() |
|
125 # fork again so the parent, (the session group leader), can exit. |
|
126 # as a non-session group leader, we can never regain a controlling |
|
127 # terminal. |
|
128 if (os.fork()): |
|
129 return -1 |
|
130 # move to the root to avoit mount pb |
|
131 os.chdir('/') |
|
132 # set paranoid umask |
|
133 os.umask(077) |
|
134 if pid_file is not None: |
|
135 # write pid in a file |
|
136 f = open(pid_file, 'w') |
|
137 f.write(str(os.getpid())) |
|
138 f.close() |
|
139 # filter warnings |
|
140 warnings.filterwarnings('ignore') |
|
141 # close standard descriptors |
|
142 sys.stdin.close() |
|
143 sys.stdout.close() |
|
144 sys.stderr.close() |
|
145 |
|
146 from logging import getLogger |
|
147 from cubicweb import set_log_methods |
|
148 LOGGER = getLogger('cubicweb.reposerver') |
|
149 set_log_methods(CubicWebConfiguration, LOGGER) |