Author: reinhard Date: 2005-06-20 04:56:54 -0500 (Mon, 20 Jun 2005) New Revision: 7622
Added: trunk/gnue-common/src/datasources/drivers/other/ trunk/gnue-common/src/datasources/drivers/other/appserver.py Removed: trunk/gnue-common/src/datasources/drivers/appserver/ Log: Moved appserver dbdriver into a single file, too. Copied: trunk/gnue-common/src/datasources/drivers/other/appserver.py (from rev 7619, trunk/gnue-common/src/datasources/drivers/appserver/appserver/__init__.py) =================================================================== --- trunk/gnue-common/src/datasources/drivers/appserver/appserver/__init__.py 2005-06-20 09:40:30 UTC (rev 7619) +++ trunk/gnue-common/src/datasources/drivers/other/appserver.py 2005-06-20 09:56:54 UTC (rev 7622) @@ -0,0 +1,518 @@ +# GNU Enterprise Datasource Library - Driver for GNUe-AppServer +# +# Copyright 2000-2005 Free Software Foundation +# +# This file is part of GNU Enterprise. +# +# GNU Enterprise is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2, or (at your option) any later version. +# +# GNU Enterprise is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with program; see the file COPYING. If not, +# write to the Free Software Foundation, Inc., 59 Temple Place +# - Suite 330, Boston, MA 02111-1307, USA. +# +# $Id$ + +""" +Database driver plugin for GNU Enterprise Application Server backends. +""" + +import sys + +from gnue.common.apps import errors, i18n +from gnue.common.datasources import Exceptions, GSchema +from gnue.common.datasources.drivers import Base +from gnue.common.rpc import client + + +# ============================================================================= +# Driver info +# ============================================================================= + +class DriverInfo: + + name = "GNU Enterprise Application Server" + + url = "http://www.gnuenterprise.org/tools/appserver/" + + description = """ +GNUe AppServer is GNUe's middleware for database access. +""" + isfree = True + + doc = """ +Description +----------- +Python driver GNUe Application Server. + +Support +------- +POSIX Support: YES + +Win32 Support: YES + +Platforms Tested: + + - Linux/Unix (Debian, RedHat, SuSe, etc) + - Windows NT/XP/2000 + +Connection Properties +--------------------- +* host -- This is the hostname/ip address of the host running AppServer (required) +* port -- The port that AppServer is running on (required) +* timeout -- Command timeout in seconds (optional) +* transport -- Transport used for network connections (http, https) (required) +* rpctype -- RPC driver used for network communication (xmlrpc, soap, etc.) + See GNUe Common's RPC documentation for a list of all RPC types. (required) + +Examples +-------- + [appserver] + comment = Connection to the GNUe Application Server + provider = appserver + rpctype = xmlrpc + host = localhost + port = 8765 + transport = http + +Notes +----- +1. GNUe AppServer works natively in utf-8, so encoding= is not a valid property. + +2. This driver is fully functional with no known serious problems. +""" + + +# ============================================================================= +# Schema handling for GNUe-AppServer +# ============================================================================= + +class Behavior (Base.Behavior): + """ + Limitations: + - Appserver cannot reproduce indices + """ + + # --------------------------------------------------------------------------- + # Constructor + # --------------------------------------------------------------------------- + + def __init__ (self, connection): + + Base.Behavior.__init__ (self, connection) + + _RELTYPE = {'type': 'object', 'name': u_('Business Object Class')} + + + # --------------------------------------------------------------------------- + # Read the connection's schema + # --------------------------------------------------------------------------- + + def _readSchema_ (self, parent): + """ + Read the connection's schema and build a GSchema object tree connected to + the given parent object (which is of type GSSchema). + """ + + tables = self.__readTables (parent) + self.__readFields (tables) + self.__addConstraints (tables) + + # We read calculated fields after building the constraints, because this + # way a field having a reference type does not introduce a non-existing + # constraint. + self.__readCalculatedFields (tables) + + + # --------------------------------------------------------------------------- + # Read all classes + # --------------------------------------------------------------------------- + + def __readTables (self, parent): + + sm = self.__connection._sm + sid = self.__connection._sess_id + lid = sm.request (sid, u"gnue_class", [], [u"gnue_module.gnue_name", + u"gnue_name"], [u"gnue_module.gnue_name", u"gnue_name"]) + + result = {} + for (gid, module, name) in sm.fetch (sid, lid, 0, sm.count (sid, lid)): + fullname = "%s_%s" % (module, name) + master = parent.findChildOfType ('GSTables') or \ + GSchema.GSTables (parent, **self._RELTYPE) + result [gid] = GSchema.GSTable (master, name = fullname, id = gid) + + return result + + + # --------------------------------------------------------------------------- + # Read all properties + # --------------------------------------------------------------------------- + + def __readFields (self, tables): + + sm = self.__connection._sm + sid = self.__connection._sess_id + + sort = [u"gnue_module", u"gnue_name"] + props = [u"gnue_module.gnue_name", u"gnue_class", u"gnue_name", + u"gnue_length", u"gnue_nullable", u"gnue_scale", u"gnue_type"] + lid = sm.request (sid, u"gnue_property", [], sort, props) + + result = {} + for record in sm.fetch (sid, lid, 0, sm.count (sid, lid)): + (gid, module, cid, name, length, nullable, scale, ftype) = record + + result [gid] = self.__createField (tables, gid, module, cid, name, + length, nullable, scale, ftype) + + + # --------------------------------------------------------------------------- + # Add all kind of constraints + # --------------------------------------------------------------------------- + + def __addConstraints (self, tables): + + for (gid, table) in tables.items (): + # Add the primary key constraint, as it is 'well known' + pk = GSchema.GSPrimaryKey (table, name = u"pk_%s" % table.name) + GSchema.GSPKField (pk, name = u"gnue_id") + + # Iterate over all fields and create a foreign key constraint for + # reference type properties + for field in table.findChildrenOfType ('GSField', False, True): + if '_' in field.nativetype: + master = table.findChildOfType ('GSConstraints') or \ + GSchema.GSConstraints (table) + constraint = GSchema.GSForeignKey (master, + name = "fk_%s_%s" % (table.name, field.nativetype), + references = field.nativetype) + + GSchema.GSFKField (constraint, name = field.name, + references = u"gnue_id") + + + # --------------------------------------------------------------------------- + # Read all calculated fields + # --------------------------------------------------------------------------- + + def __readCalculatedFields (self, tables): + + sm = self.__connection._sm + sid = self.__connection._sess_id + + sort = [u"gnue_module", u"gnue_class", u"gnue_name"] + props = [u"gnue_module.gnue_name", u"gnue_class", u"gnue_name", + u"gnue_length", u"gnue_nullable", u"gnue_scale", u"gnue_type"] + cond = ['and', ['like', ['field', u'gnue_name'], ['const', u'get%']], + ['notnull', ['field', u'gnue_type']]] + lid = sm.request (sid, u"gnue_procedure", cond, sort, props) + + result = {} + for record in sm.fetch (sid, lid, 0, sm.count (sid, lid)): + (gid, module, cid, name, length, nullable, scale, ftype) = record + + result [gid] = self.__createField (tables, gid, module, cid, name [3:], + length, nullable, scale, ftype) + + + # --------------------------------------------------------------------------- + # Create a new GSField instance from the given arguments + # --------------------------------------------------------------------------- + + def __createField (self, tables, gid, module, cid, name, length, nullable, + scale, ftype): + + table = tables [cid] + master = table.findChildOfType ('GSFields') or GSchema.GSFields (table) + + if '_' in ftype or ftype == 'id': + dtype = 'string' + length = 32 + else: + dtype = ftype + + attrs = {'id' : gid, + 'name' : "%s_%s" % (module, name), + 'type' : dtype, + 'nativetype': ftype, + 'nullable' : nullable} + if length: + attrs ['length'] = length + if scale: + attrs ['precision'] = scale + + return GSchema.GSField (master, **attrs) + + +# ============================================================================= +# ResultSet class +# ============================================================================= + +class ResultSet (Base.ResultSet): + """ + Handles a resultset (i.e. a list) in the GNUe-AppServer backend. + """ + + # --------------------------------------------------------------------------- + # Implementation of virtual methods + # --------------------------------------------------------------------------- + + def _query_object_ (self, connection, table, fieldnames, condition, sortorder, + distinct): + self.__sm = connection._sm + self.__session_id = connection._sess_id + self.__list_id = self.__sm.request (self.__session_id, table, + condition.prefixNotation (), sortorder, + fieldnames) + self.__fieldnames = fieldnames + self.__distinct = distinct # currently not honored + + # --------------------------------------------------------------------------- + + def _count_ (self): + return self.__sm.count (self.__session_id, self.__list_id) + + # --------------------------------------------------------------------------- + + def _fetch_ (self, cachesize): + position = 0 + while True: + rows = self.__sm.fetch (self.__session_id, self.__list_id, position, + cachesize) + for row in rows: + # row [0] is the gnue_id that wasn't requested + yield dict (zip (self.__fieldnames, row[1:])) + # if fetch returned less rows than we requested, we're at the end of data + if len (rows) < cachesize: + break + position += len (rows) + + +# ============================================================================= +# GNUe-AppServer Connection class +# ============================================================================= + +class Connection (Base.Connection): + """ + Connection class for GNUe-AppServer backends. + """ + + _resultSetClass = ResultSet + _primarykeyFields = [u'gnue_id'] + _behavior = Behavior + + + # --------------------------------------------------------------------------- + # Constructor + # --------------------------------------------------------------------------- + + def __init__ (self, connections, name, parameters): + + Base.Connection.__init__ (self, connections, name, parameters) + self.__filters = None + self.__server = None + self._sm = None + + + # --------------------------------------------------------------------------- + # Create/return a connection to the appserver + # --------------------------------------------------------------------------- + + def __getSessionManager (self): + + if self.__server is None: + params = {'host' : self.parameters.get ('host'), + 'port' : self.parameters.get ('port'), + 'transport': self.parameters.get ('transport')} + rpcType = self.parameters.get ('rpctype') + self.__server = client.attach (rpcType, params) + + if self.parameters.has_key ('timeout'): + self.__server.setTimeout (float (self.parameters ['timeout'])) + + if self._sm is None: + self._sm = self.__server.request ('Session') + + return self._sm + + + # --------------------------------------------------------------------------- + # Update the given filter values + # --------------------------------------------------------------------------- + + def __updateFilters (self, connectData): + + if connectData.get ('_username') == 'foobar': + raise Exceptions.LoginError, "What a silly username ..." + + for item in self.__filters: + (filterId, filterLabel) = item [0] + + if connectData.has_key (filterId): + value = connectData [filterId] + (label, search, field) = item [1][0] + + # if there are no filter values we've to skip replacement. Maybe the + # user just wants to add new filter values + if not len (item [3].keys ()): + continue + + if item [2] is None: + masterkey = None + else: + masterkey = connectData [item [2]] + + found = False + vDict = item [3][masterkey] + for record in vDict: + # The supplied value of a filter might could be the description or + # the gnue_id. + if record [field] == value or record ['gnue_id'] == value: + connectData [filterId] = record [u'gnue_id'] + found = True + break + + if not found: + raise Exceptions.LoginError, \ + u_("'%(value)s' is not a valid filter-value for '%(filter)s'") \ + % {'value': value, + 'filter': label} + + + # --------------------------------------------------------------------------- + # Implementations of virtual methods + # --------------------------------------------------------------------------- + + def _getLoginFields (self): + result = [] + + dbauth = self.parameters.get ('authentication', 'False') + if dbauth.lower () in ['true', 'yes', 'y']: + result.extend ([(_('User Name'), '_username', 'string', None, None, []), + (_('Password'), '_password', 'password', None, None, [])]) + + self.__getSessionManager () + self.__filters = self._sm.getFilters (i18n.getuserlocale ()) + + for item in self.__filters: + (filterId, filterLabel) = item [0] + (master, allowed) = item [2:4] + + elements = [] + cdefault = self.parameters.get (filterId) + default = None + + for (label, search, field) in item [1]: + data = {} + if master is not None: + for (mkey, values) in allowed.items (): + add = data.setdefault (mkey, {}) + for vdict in values: + if vdict [field] == cdefault: + default = vdict ['gnue_id'] + add [vdict ['gnue_id']] = vdict [field] + + else: + for values in allowed.values (): + for vdict in values: + if vdict [field] == cdefault: + default = vdict ['gnue_id'] + data [vdict ['gnue_id']] = vdict [field] + + elements.append ((label, data)) + + # Make sure to have the proper default values (keys) set + if default is None and filterId in self.parameters: + del self.parameters [filterId] + elif default: + self.parameters [filterId] = default + + fielddef = [filterLabel, filterId, 'dropdown', default, master, elements] + result.append (fielddef) + + return result + + # --------------------------------------------------------------------------- + + def _connect (self, connectData): + self.__getSessionManager () + self.__updateFilters (connectData) + try: + self._sess_id = self._sm.open (connectData) + except errors.RemoteError, e: + if e.getName () == 'AuthError': + raise Exceptions.LoginError, e.getMessage () + else: + raise + + # --------------------------------------------------------------------------- + + def _initialize (self, table, fields): + id = self._sm.store (self._sess_id, table, [None], [], [[]]) [0] + return self.requery (table, {u'gnue_id': id}, fields) + + # --------------------------------------------------------------------------- + + def _insert (self, table, newfields, recno): + f = newfields.copy () + id = f.pop (u'gnue_id') + self._sm.store (self._sess_id, table, [id], f.keys (), [f.values ()]) + + # --------------------------------------------------------------------------- + + def _update (self, table, oldfields, newfields, recno): + f = newfields + id = oldfields [u'gnue_id'] + self._sm.store (self._sess_id, table, [id], f.keys (), [f.values ()]) + + # --------------------------------------------------------------------------- + + def _delete (self, table, oldfields, recno): + id = oldfields [u'gnue_id'] + self._sm.delete (self._sess_id, table, [id]) + + # --------------------------------------------------------------------------- + + def _requery (self, table, oldfields, fields): + id = oldfields [u'gnue_id'] + rows = self._sm.load (self._sess_id, table, [id], fields) + if len (rows): + row = rows [0] + result = {} + for i in range (len (fields)): + result [fields [i]] = row [i] + return result + else: + return None + + # --------------------------------------------------------------------------- + + def _call (self, table, oldfields, methodname, parameters): + id = oldfields [u'gnue_id'] + return self._sm.call (self._sess_id, table, [id], methodname, + parameters) [0] + + # --------------------------------------------------------------------------- + + def _commit (self): + self._sm.commit (self._sess_id) + + # --------------------------------------------------------------------------- + + def _rollback (self): + self._sm.rollback (self._sess_id) + + # --------------------------------------------------------------------------- + + def _close (self): + if self._sm is not None: + self._sm.close (self._sess_id, False) _______________________________________________ Commit-gnue mailing list [email protected] http://lists.gnu.org/mailman/listinfo/commit-gnue
