[IMP] openerp.service: _logger with fully qualified module name.
[odoo/odoo.git] / openerp / service / netrpc_server.py
1 # -*- coding: utf-8 -*-
2
3 #
4 # Copyright P. Christeas <p_christ@hol.gr> 2008,2009
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 #.apidoc title: NET-RPC Server
23
24 """ This file contains instance of the net-rpc server
25
26     
27 """
28 import logging
29 import select
30 import socket
31 import sys
32 import threading
33 import traceback
34 import openerp
35 import openerp.netsvc as netsvc
36 import openerp.tiny_socket as tiny_socket
37 import openerp.tools as tools
38
39 _logger = logging.getLogger(__name__)
40
41 class TinySocketClientThread(threading.Thread):
42     def __init__(self, sock, threads):
43         spn = sock and sock.getpeername()
44         spn = 'netrpc-client-%s:%s' % spn[0:2]
45         threading.Thread.__init__(self, name=spn)
46         self.sock = sock
47         # Only at the server side, use a big timeout: close the
48         # clients connection when they're idle for 20min.
49         self.sock.settimeout(1200)
50         self.threads = threads
51
52     def run(self):
53         self.running = True
54         try:
55             ts = tiny_socket.mysocket(self.sock)
56         except Exception:
57             self.threads.remove(self)
58             self.running = False
59             return False
60
61         while self.running:
62             try:
63                 msg = ts.myreceive()
64                 result = netsvc.dispatch_rpc(msg[0], msg[1], msg[2:])
65                 ts.mysend(result)
66             except socket.timeout:
67                 #terminate this channel because other endpoint is gone
68                 break
69             except Exception, e:
70                 try:
71                     valid_exception = Exception(netrpc_handle_exception_legacy(e)) 
72                     valid_traceback = getattr(e, 'traceback', sys.exc_info())
73                     formatted_traceback = "".join(traceback.format_exception(*valid_traceback))
74                     _logger.debug("netrpc: communication-level exception", exc_info=True)
75                     ts.mysend(valid_exception, exception=True, traceback=formatted_traceback)
76                     break
77                 except Exception, ex:
78                     #terminate this channel if we can't properly send back the error
79                     _logger.exception("netrpc: cannot deliver exception message to client")
80                     break
81
82         netsvc.close_socket(self.sock)
83         self.sock = None
84         self.threads.remove(self)
85         self.running = False
86         return True
87
88     def stop(self):
89         self.running = False
90         
91 def netrpc_handle_exception_legacy(e):
92     if isinstance(e, openerp.osv.osv.except_osv):
93         return 'warning -- ' + e.name + '\n\n' + e.value
94     if isinstance(e, openerp.exceptions.Warning):
95         return 'warning -- Warning\n\n' + str(e)
96     if isinstance(e, openerp.exceptions.AccessError):
97         return 'warning -- AccessError\n\n' + str(e)
98     if isinstance(e, openerp.exceptions.AccessDenied):
99         return 'AccessDenied ' + str(e)
100     return openerp.tools.exception_to_unicode(e)
101
102 class TinySocketServerThread(threading.Thread,netsvc.Server):
103     def __init__(self, interface, port, secure=False):
104         threading.Thread.__init__(self, name="NetRPCDaemon-%d"%port)
105         netsvc.Server.__init__(self)
106         self.__port = port
107         self.__interface = interface
108         self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
109         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
110         self.socket.bind((self.__interface, self.__port))
111         self.socket.listen(5)
112         self.threads = []
113         netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, 
114                          "starting NET-RPC service on %s:%s" % (interface or '0.0.0.0', port,))
115
116     def run(self):
117         try:
118             self.running = True
119             while self.running:
120                 fd_sets = select.select([self.socket], [], [], self._busywait_timeout)
121                 if not fd_sets[0]:
122                     continue
123                 (clientsocket, address) = self.socket.accept()
124                 ct = TinySocketClientThread(clientsocket, self.threads)
125                 clientsocket = None
126                 self.threads.append(ct)
127                 ct.start()
128                 lt = len(self.threads)
129                 if (lt > 10) and (lt % 10 == 0):
130                      # Not many threads should be serving at the same time, so log
131                      # their abuse.
132                      _logger.debug("Netrpc: %d threads", len(self.threads))
133             self.socket.close()
134         except Exception, e:
135             _logger.warning("Netrpc: closing because of exception %s" % str(e))
136             self.socket.close()
137             return False
138
139     def stop(self):
140         self.running = False
141         for t in self.threads:
142             t.stop()
143         self._close_socket()
144
145     def stats(self):
146         res = "Net-RPC: " + ( (self.running and "running") or  "stopped")
147         i = 0
148         for t in self.threads:
149             i += 1
150             res += "\nNet-RPC #%d: %s " % (i, t.name)
151             if t.isAlive():
152                 res += "running"
153             else:
154                 res += "finished"
155             if t.sock:
156                 res += ", socket"
157         return res
158
159 netrpcd = None
160
161 def init_servers():
162     global netrpcd
163     if tools.config.get('netrpc', False):
164         netrpcd = TinySocketServerThread(
165             tools.config.get('netrpc_interface', ''), 
166             int(tools.config.get('netrpc_port', 8070)))
167
168 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: