[FIX] service: put autocommit true on EDI import service to store imported data
[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 """ 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 openerp.netsvc as netsvc
34 import openerp.tiny_socket as tiny_socket
35 import openerp.tools as 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                     break
91                 except Exception, ex:
92                     #terminate this channel if we can't properly send back the error
93                     logging.getLogger('web-services').exception("netrpc: cannot deliver exception message to client")
94                     break
95
96         self.sock.shutdown(socket.SHUT_RDWR)
97         self.sock.close()
98         self.sock = None
99         self.threads.remove(self)
100         self.running = False
101         return True
102
103     def stop(self):
104         self.running = False
105
106
107 class TinySocketServerThread(threading.Thread,netsvc.Server):
108     def __init__(self, interface, port, secure=False):
109         threading.Thread.__init__(self, name="NetRPCDaemon-%d"%port)
110         netsvc.Server.__init__(self)
111         self.__port = port
112         self.__interface = interface
113         self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
114         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
115         self.socket.bind((self.__interface, self.__port))
116         self.socket.listen(5)
117         self.threads = []
118         netsvc.Logger().notifyChannel("web-services", netsvc.LOG_INFO, 
119                          "starting NET-RPC service at %s port %d" % (interface or '0.0.0.0', port,))
120
121     def run(self):
122         try:
123             self.running = True
124             while self.running:
125                 fd_sets = select.select([self.socket], [], [], self._busywait_timeout)
126                 if not fd_sets[0]:
127                     continue
128                 (clientsocket, address) = self.socket.accept()
129                 ct = TinySocketClientThread(clientsocket, self.threads)
130                 clientsocket = None
131                 self.threads.append(ct)
132                 ct.start()
133                 lt = len(self.threads)
134                 if (lt > 10) and (lt % 10 == 0):
135                      # Not many threads should be serving at the same time, so log
136                      # their abuse.
137                      netsvc.Logger().notifyChannel("web-services", netsvc.LOG_DEBUG,
138                         "Netrpc: %d threads" % len(self.threads))
139             self.socket.close()
140         except Exception, e:
141             logging.getLogger('web-services').warning("Netrpc: closing because of exception %s" % str(e))
142             self.socket.close()
143             return False
144
145     def stop(self):
146         self.running = False
147         for t in self.threads:
148             t.stop()
149         self._close_socket()
150
151     def stats(self):
152         res = "Net-RPC: " + ( (self.running and "running") or  "stopped")
153         i = 0
154         for t in self.threads:
155             i += 1
156             res += "\nNet-RPC #%d: %s " % (i, t.name)
157             if t.isAlive():
158                 res += "running"
159             else:
160                 res += "finished"
161             if t.sock:
162                 res += ", socket"
163         return res
164
165 netrpcd = None
166
167 def init_servers():
168     global netrpcd
169     if tools.config.get('netrpc', False):
170         netrpcd = TinySocketServerThread(
171             tools.config.get('netrpc_interface', ''), 
172             int(tools.config.get('netrpc_port', 8070)))