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