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