1 |
|
# |
2 |
|
# This file is part of GNU Enterprise. |
3 |
|
# |
4 |
|
# GNU Enterprise is free software; you can redistribute it |
5 |
|
# and/or modify it under the terms of the GNU General Public |
6 |
|
# License as published by the Free Software Foundation; either |
7 |
|
# version 2, or (at your option) any later version. |
8 |
|
# |
9 |
|
# GNU Enterprise is distributed in the hope that it will be |
10 |
|
# useful, but WITHOUT ANY WARRANTY; without even the implied |
11 |
|
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
12 |
|
# PURPOSE. See the GNU General Public License for more details. |
13 |
|
# |
14 |
|
# You should have received a copy of the GNU General Public |
15 |
|
# License along with program; see the file COPYING. If not, |
16 |
|
# write to the Free Software Foundation, Inc., 59 Temple Place |
17 |
|
# - Suite 330, Boston, MA 02111-1307, USA. |
18 |
|
# |
19 |
|
# Copyright 2000-2003 Free Software Foundation |
20 |
|
# |
21 |
|
# FILE: |
22 |
|
# appserver/Connection.py |
23 |
|
# |
24 |
|
# DESCRIPTION: |
25 |
|
# Implementation of dbdriver for use with GNUe Application Server. |
26 |
|
# |
27 |
|
# NOTES: |
28 |
|
# |
29 |
|
# HISTORY: |
30 |
|
# |
31 |
|
|
32 |
|
VERSION="0.0.1" |
33 |
|
|
34 |
|
__all__ = ['Connection'] |
35 |
|
|
36 |
|
from gnue.common.datasources import GDataObjects, GConditions |
37 |
|
from gnue.common.datasources.drivers.Base import Connection as BaseConnection |
38 |
|
from gnue.common.apps import GDebug |
39 |
|
from gnue.common.rpc import GComm |
40 |
|
|
41 |
|
from DataObject import * |
42 |
|
|
43 |
|
import string |
44 |
|
import types |
45 |
|
import md5 |
46 |
|
import sys |
47 |
|
import mx.DateTime, mx.DateTime.ISO |
48 |
|
|
49 |
|
from DataObject import DataObject |
50 |
|
from gnue.common.datasources.drivers.appserver.Schema.Discovery.Introspection import Introspection |
51 |
|
|
52 |
|
|
53 |
|
# TODO: move all non standart Connection functions (request, ...) into a subobject native |
54 |
|
# to prevent namespace conflicts |
55 |
|
|
56 |
|
class Connection(BaseConnection): |
57 |
|
|
58 |
|
_DatabaseError = GComm.Error |
59 |
|
defaultBehavior = Introspection |
60 |
|
supportedDataObjects = { |
61 |
|
'object': DataObject |
62 |
|
} |
63 |
|
|
64 |
|
# We only need the basics -- username and password -- to log in |
65 |
|
def getLoginFields(self): |
66 |
|
return [['_username', 'User Name',0],['_password', 'Password',1]] |
67 |
|
|
68 |
|
|
69 |
|
def connect(self, connectData): |
70 |
|
user = connectData['_username'] |
71 |
|
passwd = connectData['_password'] |
72 |
|
|
73 |
|
params = { 'host': connectData['host'], |
74 |
|
'port': connectData['port'], |
75 |
|
'transport': connectData['transport']} |
76 |
|
|
77 |
|
self._server = GComm.attach(connectData['rpctype'],params) |
78 |
|
|
79 |
|
GDebug.printMesg(3,"Setup the link to the session manager") |
80 |
|
self._sm = self._server.request("Session") |
81 |
|
|
82 |
|
if connectData.has_key('encoding'): |
83 |
|
GDebug.printMesg(1,"Appserver's dbdriver doesn't 'encoding' parameter, as the transport"+\ |
84 |
|
" encoding has to be 'utf-8'.") |
85 |
|
|
86 |
|
#GDebug.printMesg(3,"Get the status of the session manager") |
87 |
|
#GDebug.printMesg(3,"Status: "+sessionManager.Status()) |
88 |
|
|
89 |
|
try: |
90 |
|
GDebug.printMesg(3,"Open Session ...") |
91 |
|
GDebug.printMesg(1,"Logging into appserver as user '%s'" % (user)) |
92 |
|
self._sess_id = self._sm.open({'user':user,'password':passwd}) |
93 |
|
|
94 |
|
except Exception, msg: |
95 |
|
tmsg = _("Error loging into appserver: %s") % msg |
96 |
|
raise GDataObjects.ConnectionError, tmsg |
97 |
|
|
98 |
|
if self._sess_id == 0: |
99 |
|
tmsg = _("Error loging into appserver") |
100 |
|
raise GDataObjects.ConnectionError, tmsg |
101 |
|
|
102 |
|
self._updateCursor = Appserver_UpdateCursor(self) |
103 |
|
|
104 |
|
def cursor(self): |
105 |
|
return self._updateCursor |
106 |
|
|
107 |
|
def request(self,table,filter,sort,fieldlist,unicodeMode=0): |
108 |
|
listid = self._sm.request(self._sess_id,table,filter,sort,fieldlist) |
109 |
|
return Appserver_ListCursor(self,listid,table,fieldlist,unicodeMode) |
110 |
|
|
111 |
|
def call(self,classname,obj_id_list,methodname,parameters): |
112 |
|
self._sm.call(self._sess_id,classname,obj_id_list,methodname,parameters) |
113 |
|
|
114 |
|
def commit(self,classname): |
115 |
|
self._updateCursor.execute(classname) |
116 |
|
self._sm.commit(self._sess_id) |
117 |
|
|
118 |
|
def rollback(self,classname): |
119 |
|
self._updateCursor.revert(classname) |
120 |
|
self._sm.rollback(self._sess_id) |
121 |
|
|
122 |
|
def close(self,commit): |
123 |
|
self._sm.close(self._sess_id,commit) |
124 |
|
|
125 |
|
# Return a sequence number from sequence 'name' |
126 |
|
# def getSequence(self, name): |
127 |
|
# !!! has to be emulated !!! |
128 |
|
# return self.__singleQuery("select nextval('%s')" % name) |
129 |
|
|
130 |
|
# Run the SQL statement 'statement' |
131 |
|
#def sql(self, statement): |
132 |
|
# !!! has to be emulated !!! |
133 |
|
# cursor = self.__connection.cursor() |
134 |
|
# try: |
135 |
|
# cursor.execute(statement) |
136 |
|
# cursor.close() |
137 |
|
# except: |
138 |
|
# cursor.close() |
139 |
|
# raise |
140 |
|
|
141 |
|
|
142 |
|
|
143 |
|
class Appserver_ListCursor: |
144 |
|
def __init__(self,dataCon,listid,classname,fieldlist,unicodeMode=0): |
145 |
|
self._dataCon=dataCon |
146 |
|
self._listid=listid |
147 |
|
self._fieldlist=fieldlist |
148 |
|
self._stackpos=0 |
149 |
|
self._unicodeMode=unicodeMode |
150 |
|
self._fieldtypes = self._dataCon._sm.load (self._dataCon._sess_id, |
151 |
|
classname, [''], self._fieldlist) |
152 |
|
self._fieldtypes = self._fieldtypes [0] |
153 |
|
|
154 |
|
# convert a value retrieved from RPC to the correct native Python type |
155 |
|
def __rpc_to_native (self, value, type): |
156 |
|
|
157 |
|
# Empty strings indicate None |
158 |
|
if value == '': |
159 |
|
return None |
160 |
|
|
161 |
|
# String: convert to unicode or local encoding |
162 |
|
elif type [:7] == 'string(': |
163 |
|
value = unicode (value, 'utf-8') |
164 |
|
if self._unicodeMode: |
165 |
|
return value |
166 |
|
else: |
167 |
|
return value.encode (gConfig ('textEncoding')) |
168 |
|
|
169 |
|
# Date: convert to mx.DateTime object |
170 |
|
elif type == 'date': |
171 |
|
return mx.DateTime.ISO.ParseDate (value) |
172 |
|
|
173 |
|
# Time: convert to mx.DateTime object |
174 |
|
elif type == 'time': |
175 |
|
return mx.DateTime.ISO.ParseTime (value) |
176 |
|
|
177 |
|
# DateTime: convert to mx.DateTime object |
178 |
|
elif type == 'datetime': |
179 |
|
return mx.DateTime.ISO.ParseDateTime (value) |
180 |
|
|
181 |
|
# All others (id, number, boolean, reference): no need to convert |
182 |
|
else: |
183 |
|
return value |
184 |
|
|
185 |
|
def fetch(self,count=5): |
186 |
|
if self._stackpos == -1: |
187 |
|
return [] |
188 |
|
|
189 |
|
result = self._dataCon._sm.fetch(self._dataCon._sess_id, |
190 |
|
self._listid,self._stackpos,count) |
191 |
|
if len(result)<count: |
192 |
|
self._stackpos=-1 |
193 |
|
|
194 |
|
else: |
195 |
|
self._stackpos=self._stackpos+len(result) |
196 |
|
|
197 |
|
list = [] |
198 |
|
for i in result: |
199 |
|
dict = {} |
200 |
|
j = 0 |
201 |
|
for fieldName in self._fieldlist: |
202 |
|
dict [fieldName] = self.__rpc_to_native (i [j+1], self._fieldtypes [j]) |
203 |
|
j += 1 |
204 |
|
|
205 |
|
dict["gnue_id"]=i[0] |
206 |
|
list.append(dict) |
207 |
|
return list |
208 |
|
|
209 |
|
def count(self): |
210 |
|
if not hasattr(self,"_count"): |
211 |
|
self._count = self._dataCon._sm.count(self._dataCon._sess_id,self._listid) |
212 |
|
|
213 |
|
return self._count |
214 |
|
|
215 |
|
def close(self): |
216 |
|
pass |
217 |
|
# TODO: Implement List Close command |
218 |
|
|
219 |
|
class Appserver_UpdateCursor: |
220 |
|
def __init__(self,dataCon,unicodeMode=0): |
221 |
|
self._dataCon=dataCon |
222 |
|
self._deleteList={} |
223 |
|
self._updateList={} |
224 |
|
self._updateKeyList={} |
225 |
|
self._unicodeMode=unicodeMode |
226 |
|
|
227 |
|
def delete(self,classname,id): |
228 |
|
if not self._deleteList.has_key(classname): |
229 |
|
self._deleteList[classname]=[] |
230 |
|
|
231 |
|
self._deleteList[classname].append(id) |
232 |
|
|
233 |
|
def update(self, classname, id, fieldDict): |
234 |
|
if not self._updateList.has_key(classname): |
235 |
|
self._updateList[classname]=[] |
236 |
|
self._updateKeyList[classname]=[] |
237 |
|
|
238 |
|
self._updateList[classname].append(fieldDict) |
239 |
|
self._updateKeyList[classname].append(id) |
240 |
|
|
241 |
|
# convert a native Python type into something transportable by RPC |
242 |
|
def __native_to_rpc (self, s): |
243 |
|
if type (s) == types.StringType: |
244 |
|
if self._unicodeMode: |
245 |
|
msg = 'WARNING: non-unicode passed to the dbdriver (%s)' % value |
246 |
|
GDebug.printMesg (0, msg) |
247 |
|
s = unicode (s, gConfig ('textEncoding')) |
248 |
|
if type (s) == types.UnicodeType: |
249 |
|
s = s.encode ('utf-8') |
250 |
|
elif type (s) == mx.DateTime.DateTimeType: |
251 |
|
s = s.date + ' ' + s.time |
252 |
|
elif s is None: |
253 |
|
s = '' |
254 |
|
return s |
255 |
|
|
256 |
|
def execute (self, classname): |
257 |
|
if self._deleteList.has_key (classname): |
258 |
|
self._dataCon._sm.delete (self._dataCon._sess_id, classname, |
259 |
|
self._deleteList [classname]) |
260 |
|
del self._deleteList [classname] |
261 |
|
|
262 |
|
if self._updateList.has_key (classname): |
263 |
|
while len (self._updateList [classname]): |
264 |
|
id = self._updateKeyList[classname].pop() |
265 |
|
dict = self._updateList[classname].pop() |
266 |
|
# TODO: merge calls with similar updated fields (=dict.values()) |
267 |
|
data = [self.__native_to_rpc (x) for x in dict.values ()] |
268 |
|
new_ids = self._dataCon._sm.store (self._dataCon._sess_id, classname, |
269 |
|
[id], dict.keys(), [data]) |
270 |
|
dict ["gnue_id"] = new_ids [0] |
271 |
|
del self._updateList [classname] |
272 |
|
del self._updateKeyList [classname] |
273 |
|
|
274 |
|
def revert (self, classname): |
275 |
|
if self._deleteList.has_key (classname): |
276 |
|
del self._deleteList [classname] |
277 |
|
if self._updateList.has_key (classname): |
278 |
|
del self._updateList [classname] |
279 |
|
del self._updateKeyList [classname] |