merge upstream
[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 netrpc_server
33 import web_services
34 import web_services
35 import wsgi_server
36
37 import openerp.modules
38 import openerp.netsvc
39 import openerp.osv
40 from openerp.release import nt_service_name
41 import openerp.tools
42
43 #.apidoc title: RPC Services
44
45 """ Classes of this module implement the network protocols that the
46     OpenERP server uses to communicate with remote clients.
47
48     Some classes are mostly utilities, whose API need not be visible to
49     the average user/developer. Study them only if you are about to
50     implement an extension to the network protocols, or need to debug some
51     low-level behavior of the wire.
52 """
53
54 _logger = logging.getLogger(__name__)
55
56 def load_server_wide_modules():
57     for m in openerp.conf.server_wide_modules:
58         try:
59             openerp.modules.module.load_openerp_module(m)
60         except Exception:
61             msg = ''
62             if m == 'web':
63                 msg = """
64 The `web` module is provided by the addons found in the `openerp-web` project.
65 Maybe you forgot to add those addons in your addons_path configuration."""
66             _logger.exception('Failed to load server-wide module `%s`.%s', m, msg)
67
68 start_internal_done = False
69 main_thread_id = threading.currentThread().ident
70
71 def start_internal():
72     global start_internal_done
73     if start_internal_done:
74         return
75     openerp.netsvc.init_logger()
76     openerp.modules.loading.open_openerp_namespace()
77
78     # Instantiate local services (this is a legacy design).
79     openerp.osv.osv.start_object_proxy()
80     # Export (for RPC) services.
81     web_services.start_service()
82
83     load_server_wide_modules()
84     start_internal_done = True
85
86 def start_services():
87     """ Start all services including http, netrpc and cron """
88     start_internal()
89     # Initialize the NETRPC server.
90     netrpc_server.start_service()
91     # Start the WSGI server.
92     wsgi_server.start_service()
93     # Start the main cron thread.
94     cron.start_service()
95
96 def stop_services():
97     """ Stop all services. """
98     # stop services
99     cron.stop_service()
100     netrpc_server.stop_service()
101     wsgi_server.stop_service()
102
103     _logger.info("Initiating shutdown")
104     _logger.info("Hit CTRL-C again or send a second signal to force the shutdown.")
105
106     # Manually join() all threads before calling sys.exit() to allow a second signal
107     # to trigger _force_quit() in case some non-daemon threads won't exit cleanly.
108     # threading.Thread.join() should not mask signals (at least in python 2.5).
109     me = threading.currentThread()
110     _logger.debug('current thread: %r', me)
111     for thread in threading.enumerate():
112         _logger.debug('process %r (%r)', thread, thread.isDaemon())
113         if thread != me and not thread.isDaemon() and thread.ident != main_thread_id:
114             while thread.isAlive():
115                 _logger.debug('join and sleep')
116                 # Need a busyloop here as thread.join() masks signals
117                 # and would prevent the forced shutdown.
118                 thread.join(0.05)
119                 time.sleep(0.05)
120
121     _logger.debug('--')
122     openerp.modules.registry.RegistryManager.delete_all()
123     logging.shutdown()
124
125 def start_services_workers():
126     import openerp.service.workers
127     openerp.multi_process = True
128     openerp.service.workers.Multicorn(openerp.service.wsgi_server.application).run()
129
130 def _reexec():
131     """reexecute openerp-server process with (nearly) the same arguments"""
132     if openerp.tools.osutil.is_running_as_nt_service():
133         subprocess.call('sc stop {0} && sc start {0}'.format(nt_service_name), shell=True)
134     exe = os.path.basename(sys.executable)
135     strip_args = ['-d', '-u']
136     a = sys.argv[:]
137     args = [x for i, x in enumerate(a) if x not in strip_args and a[max(i - 1, 0)] not in strip_args]
138     if not args or args[0] != exe:
139         args.insert(0, exe)
140     os.execv(sys.executable, args)
141
142 def restart_server():
143     if openerp.multi_process:
144         raise NotImplementedError("Multicorn is not supported (but gunicorn was)")
145         pid = openerp.wsgi.core.arbiter_pid
146         os.kill(pid, signal.SIGHUP)
147     else:
148         if os.name == 'nt':
149             def reborn():
150                 stop_services()
151                 _reexec()
152
153             # run in a thread to let the current thread return response to the caller.
154             threading.Thread(target=reborn).start()
155         else:
156             openerp.phoenix = True
157             os.kill(os.getpid(), signal.SIGINT)
158
159 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: