[MERGE] merged long-polling branch:
[odoo/odoo.git] / openerp / service / __init__.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #    Copyright (C) 2010-2012 OpenERP SA (<http://www.openerp.com>)
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU Affero General Public License as
10 #    published by the Free Software Foundation, either version 3 of the
11 #    License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU Affero General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import logging
24 import os
25 import signal
26 import subprocess
27 import sys
28 import threading
29 import time
30
31 import cron
32 import wsgi_server
33
34 import openerp
35 import openerp.modules
36 import openerp.netsvc
37 import openerp.osv
38 from openerp.release import nt_service_name
39 import openerp.tools
40
41 import common
42 import db
43 import model
44 import report
45
46 #.apidoc title: RPC Services
47
48 """ Classes of this module implement the network protocols that the
49     OpenERP server uses to communicate with remote clients.
50
51     Some classes are mostly utilities, whose API need not be visible to
52     the average user/developer. Study them only if you are about to
53     implement an extension to the network protocols, or need to debug some
54     low-level behavior of the wire.
55 """
56
57 _logger = logging.getLogger(__name__)
58
59 def load_server_wide_modules():
60     for m in openerp.conf.server_wide_modules:
61         try:
62             openerp.modules.module.load_openerp_module(m)
63         except Exception:
64             msg = ''
65             if m == 'web':
66                 msg = """
67 The `web` module is provided by the addons found in the `openerp-web` project.
68 Maybe you forgot to add those addons in your addons_path configuration."""
69             _logger.exception('Failed to load server-wide module `%s`.%s', m, msg)
70
71 start_internal_done = False
72 main_thread_id = threading.currentThread().ident
73
74 def start_internal():
75     global start_internal_done
76     if start_internal_done:
77         return
78     openerp.netsvc.init_logger()
79
80     load_server_wide_modules()
81     start_internal_done = True
82
83 def start_services():
84     """ Start all services including http, and cron """
85     start_internal()
86     # Start the WSGI server.
87     wsgi_server.start_service()
88     # Start the main cron thread.
89     if not openerp.evented:
90         cron.start_service()
91
92 def stop_services():
93     """ Stop all services. """
94     # stop services
95     if not openerp.evented:
96         cron.stop_service()
97     wsgi_server.stop_service()
98
99     _logger.info("Initiating shutdown")
100     _logger.info("Hit CTRL-C again or send a second signal to force the shutdown.")
101
102     # Manually join() all threads before calling sys.exit() to allow a second signal
103     # to trigger _force_quit() in case some non-daemon threads won't exit cleanly.
104     # threading.Thread.join() should not mask signals (at least in python 2.5).
105     me = threading.currentThread()
106     _logger.debug('current thread: %r', me)
107     for thread in threading.enumerate():
108         _logger.debug('process %r (%r)', thread, thread.isDaemon())
109         if thread != me and not thread.isDaemon() and thread.ident != main_thread_id:
110             while thread.isAlive():
111                 _logger.debug('join and sleep')
112                 # Need a busyloop here as thread.join() masks signals
113                 # and would prevent the forced shutdown.
114                 thread.join(0.05)
115                 time.sleep(0.05)
116
117     _logger.debug('--')
118     openerp.modules.registry.RegistryManager.delete_all()
119     logging.shutdown()
120
121 def start_services_workers():
122     import openerp.service.workers
123     openerp.multi_process = True
124     openerp.service.workers.Multicorn(openerp.service.wsgi_server.application).run()
125
126 def _reexec():
127     """reexecute openerp-server process with (nearly) the same arguments"""
128     if openerp.tools.osutil.is_running_as_nt_service():
129         subprocess.call('net stop {0} && net start {0}'.format(nt_service_name), shell=True)
130     exe = os.path.basename(sys.executable)
131     strip_args = ['-d', '-u']
132     a = sys.argv[:]
133     args = [x for i, x in enumerate(a) if x not in strip_args and a[max(i - 1, 0)] not in strip_args]
134     if not args or args[0] != exe:
135         args.insert(0, exe)
136     os.execv(sys.executable, args)
137
138 def restart_server():
139     if openerp.multi_process:
140         raise NotImplementedError("Multicorn is not supported (but gunicorn was)")
141         pid = openerp.wsgi.core.arbiter_pid
142         os.kill(pid, signal.SIGHUP)
143     else:
144         if os.name == 'nt':
145             def reborn():
146                 stop_services()
147                 _reexec()
148
149             # run in a thread to let the current thread return response to the caller.
150             threading.Thread(target=reborn).start()
151         else:
152             openerp.phoenix = True
153             os.kill(os.getpid(), signal.SIGINT)
154
155 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: