[FIX] restart of server on windows
[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 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import logging
23 import os
24 import signal
25 import sys
26 import threading
27 import time
28
29 import netrpc_server
30 import web_services
31
32 import openerp.cron
33 import openerp.modules
34 import openerp.netsvc
35 import openerp.osv
36 import openerp.tools
37 import openerp.service.wsgi_server
38
39 #.apidoc title: RPC Services
40
41 """ Classes of this module implement the network protocols that the
42     OpenERP server uses to communicate with remote clients.
43
44     Some classes are mostly utilities, whose API need not be visible to
45     the average user/developer. Study them only if you are about to
46     implement an extension to the network protocols, or need to debug some
47     low-level behavior of the wire.
48 """
49
50 _logger = logging.getLogger(__name__)
51
52 def load_server_wide_modules():
53     for m in openerp.conf.server_wide_modules:
54         try:
55             openerp.modules.module.load_openerp_module(m)
56         except Exception:
57             msg = ''
58             if m == 'web':
59                 msg = """
60 The `web` module is provided by the addons found in the `openerp-web` project.
61 Maybe you forgot to add those addons in your addons_path configuration."""
62             _logger.exception('Failed to load server-wide module `%s`.%s', m, msg)
63
64 start_internal_done = False
65
66 def start_internal():
67     global start_internal_done
68     if start_internal_done:
69         return
70     openerp.netsvc.init_logger()
71     openerp.modules.loading.open_openerp_namespace()
72     # Instantiate local services (this is a legacy design).
73     openerp.osv.osv.start_object_proxy()
74     # Export (for RPC) services.
75     web_services.start_web_services()
76     load_server_wide_modules()
77     start_internal_done = True
78
79 def start_services():
80     """ Start all services including http, netrpc and cron """
81     start_internal()
82
83     # Initialize the HTTP stack.
84     netrpc_server.init_servers()
85
86     # Start the main cron thread.
87     if openerp.conf.max_cron_threads:
88         openerp.cron.start_master_thread()
89
90     # Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC).
91     openerp.netsvc.Server.startAll()
92
93     # Start the WSGI server.
94     openerp.service.wsgi_server.start_server()
95
96 def stop_services():
97     """ Stop all services. """
98     # stop scheduling new jobs; we will have to wait for the jobs to complete below
99     openerp.cron.cancel_all()
100
101     openerp.netsvc.Server.quitAll()
102     openerp.service.wsgi_server.stop_server()
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():
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     # TODO check if parent is a NT service
133     strip_args = ['-d', '-u']
134     a = sys.argv[:]
135     args = [x for i, x in enumerate(a) if x not in strip_args and a[max(i - 1, 0)] not in strip_args]
136     os.execv(sys.executable, [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: