[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
r5094 - trunk/gnue-appserver/src
From: |
reinhard |
Subject: |
r5094 - trunk/gnue-appserver/src |
Date: |
Wed, 11 Feb 2004 18:24:41 -0600 (CST) |
Author: reinhard
Date: 2004-02-11 18:24:40 -0600 (Wed, 11 Feb 2004)
New Revision: 5094
Added:
trunk/gnue-appserver/src/data.py
Modified:
trunk/gnue-appserver/src/geasInstance.py
trunk/gnue-appserver/src/geasList.py
trunk/gnue-appserver/src/geasSession.py
Log:
Implemented per-session cache.
Added: trunk/gnue-appserver/src/data.py
===================================================================
--- trunk/gnue-appserver/src/data.py 2004-02-11 00:03:33 UTC (rev 5093)
+++ trunk/gnue-appserver/src/data.py 2004-02-12 00:24:40 UTC (rev 5094)
@@ -0,0 +1,728 @@
+# GNU Enterprise Application Server - Per-Session Cache
+#
+# Copyright 2004 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$
+
+import string
+import whrandom
+from gnue.common.datasources import GDataSource, GConditions
+
+# =============================================================================
+# Cache class
+# =============================================================================
+
+class cache:
+ """
+ This class is acutally not more than a 3-dimensional array with the
+ dimensions called "table", "row", and "field". Any combination of these can
+ either have a string value, a value of None, or not be available.
+
+ For any data item, the cache remembers the current value as well as the
+ original value.
+
+ This class doesn't do database access. It gets the values to store via the
+ "write" method.
+
+ This class is only used internally.
+ """
+
+ # ---------------------------------------------------------------------------
+ # Initalize
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self):
+ self.__old = {} # Original data
+ self.__new = {} # Changed (dirty) data
+
+ # ---------------------------------------------------------------------------
+ # Store data in the cache
+ # ---------------------------------------------------------------------------
+
+ def write (self, table, row, field, value, dirty):
+ """
+ Write data to the cache. If "dirty" is false (0), the cache takes the given
+ value as the original value for the field. If "dirty" is true (1), the
+ value is taken as the modified value for the field, and the original value
+ is remembered (if it was set before).
+
+ It is possible to set a dirty value without having set an original value
+ before.
+ """
+
+ if dirty:
+ tables = self.__new
+ else:
+ tables = self.__old
+
+ if not tables.has_key (table):
+ tables [table] = {}
+ rows = tables [table]
+
+ if not rows.has_key (row):
+ rows [row] = {}
+ fields = rows [row]
+
+ fields [field] = value
+
+ # ---------------------------------------------------------------------------
+ # Return whether a certain value is stored in the clean/dirty cache
+ # ---------------------------------------------------------------------------
+
+ def __has (self, table, row, field, dirty):
+
+ result = 0
+
+ if dirty:
+ tables = self.__new
+ else:
+ tables = self.__old
+
+ if tables.has_key (table):
+ rows = tables [table]
+ if rows.has_key (row):
+ fields = rows [row]
+ if fields.has_key (field):
+ result = 1
+
+ return result
+
+ # ---------------------------------------------------------------------------
+ # Return whether a certain value is stored in the cache or not
+ # ---------------------------------------------------------------------------
+
+ def has (self, table, row, field):
+ """
+ Return true (1) if the given item is stored in the cache (either in a clean
+ or in a dirty version). Return false (0) if it isn't.
+ """
+
+ if self.__has (table, row, field, 1) or self.__has (table, row, field, 0):
+ return 1
+ else:
+ return 0
+
+ # ---------------------------------------------------------------------------
+ # Read data from the cache
+ # ---------------------------------------------------------------------------
+
+ def read (self, table, row, field):
+ """
+ Read data from the cache. This always returns the current version, no
+ matter if it's dirty or not.
+
+ If the given item isn't available, an exception is raised.
+ """
+
+ if self.__has (table, row, field, 1):
+ tables = self.__new # Data is in dirty cache
+ else:
+ tables = self.__old # Data isn't in dirty cache, so search
+ # in clean cache
+ rows = tables [table]
+ fields = rows [row]
+ return fields [field]
+
+ # ---------------------------------------------------------------------------
+ # Get the status of a record
+ # ---------------------------------------------------------------------------
+
+ def status (self, table, row):
+ """
+ Returns the status of the given row. Returns one of the following results:
+
+ 'inserted': newly created record
+
+ 'changed': existing record with modifications
+
+ 'deleted': deleted record
+
+ For this function to work, an original value for the 'gnue_id' field must
+ be available for any record except for newly created ones, and setting
+ 'gnue_id' to None means deleting the record.
+ """
+
+ if not self.__has (table, row, 'gnue_id', 0):
+ return '' # row is not in cache at all
+
+ old_id = self.__old [table] [row] ['gnue_id']
+
+ if self.__has (table, row, 'gnue_id', 1):
+ new_id = self.__new [table] [row] ['gnue_id']
+ else:
+ new_id = old_id
+
+ if old_id is None:
+ if new_id is None:
+ return '' # row was inserted and deleted
+ else:
+ return 'inserted'
+ else:
+ if new_id is None:
+ return 'deleted'
+ else:
+ if self.__new.has_key (table):
+ rows = self.__new [table]
+ if rows.has_key (row):
+ return 'changed'
+ return '' # row has no dirty fields
+
+ # ---------------------------------------------------------------------------
+ # List all tables with dirty records
+ # ---------------------------------------------------------------------------
+
+ def dirtyTables (self):
+ """
+ Returns a dictionary of tables with dirty data (inserted, changed or
+ deleted rows), where the key is the table name and the value is a
+ dictionary of all dirty rows in the table, where the key is the row id and
+ the value is a dictionary of all dirty fields in that row, where the key is
+ the field name and the value is the current value of the field. Got it?
+ """
+
+ return self.__new
+
+ # ---------------------------------------------------------------------------
+ # Discard all dirty data from the cache - i.e. undo all writes with dirty = 1
+ # ---------------------------------------------------------------------------
+
+ def undo (self):
+ """
+ Revert all data in the cache to the remembered original value.
+ """
+
+ self.__new = {}
+
+ # ---------------------------------------------------------------------------
+ # Clear the whole cache
+ # ---------------------------------------------------------------------------
+
+ def clear (self):
+ """
+ Forget all data in the cache, original values as well as dirty values.
+ """
+
+ self.__old = {}
+ self.__new = {}
+
+# =============================================================================
+# Helper methods
+# =============================================================================
+
+# -----------------------------------------------------------------------------
+# Create a result set
+# -----------------------------------------------------------------------------
+
+def _createDatasource (connections, database, table, fields, order = None):
+
+ # prepare attributes of the datasource
+ attributes = {}
+ attributes ['name'] = ''
+ attributes ['database'] = database
+ attributes ['table'] = table
+
+ if order is not None:
+ if order != []:
+ attributes ['order_by'] = string.joinfields (order, ',')
+
+ # create the datasource
+ datasource = GDataSource.DataSourceWrapper (
+ connections = connections,
+ attributes = attributes,
+ fields = fields)
+
+ # enable unicode mode for the datasource
+ datasource._dataObject._unicodeMode = 1
+
+ return datasource
+
+# -----------------------------------------------------------------------------
+# Create an empty result set
+# -----------------------------------------------------------------------------
+
+def _createEmptyResultSet (connections, database, table, fields):
+
+ datasource = _createDatasource (connections, database, table, fields)
+ return datasource.createEmptyResultSet ()
+
+# -----------------------------------------------------------------------------
+# Create a result set with data
+# -----------------------------------------------------------------------------
+
+def _createResultSet (connections, database, table, fields, conditions, order):
+
+ datasource = _createDatasource (connections, database, table, fields, order)
+ if conditions is not None:
+ condition_tree = GConditions.buildTreeFromPrefix (conditions)
+ else:
+ condition_tree = None
+ return datasource.createResultSet (condition_tree)
+
+# -----------------------------------------------------------------------------
+# Create a result set containing only one row, identified by the gnue_id
+# -----------------------------------------------------------------------------
+
+def _find (connections, database, table, row, fields):
+
+ conditions = [['eq', ''], ['field', 'gnue_id'], ['const', row]]
+ resultSet = _createResultSet (connections, database, table, fields,
+ conditions, [])
+ resultSet.firstRecord ()
+ return resultSet
+
+# =============================================================================
+# Session class
+# =============================================================================
+
+class connection:
+ """
+ This class encapsulates a connection to the database where data is cached on
+ connection level. This means that if one query modifies data, another query
+ using the same connection reads the new version even if the changes are not
+ committed yet.
+ """
+
+ # ---------------------------------------------------------------------------
+ # Initialize
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, connections, database):
+ self.__connections = connections
+ self.__database = database
+ self.__cache = cache ()
+
+ # ---------------------------------------------------------------------------
+ # Create a recordset from a query
+ # ---------------------------------------------------------------------------
+
+ def query (self, table, fields, conditions, order):
+ """
+ Returns a recordset object. All fields given in 'fields' are fetched from
+ the database and cached, so that subsequential access to those fields won't
+ trigger another access to the db backend.
+ """
+
+ return recordset (self.__cache, self.__connections, self.__database, table,
+ fields, conditions, order)
+
+ # ---------------------------------------------------------------------------
+ # Generate a new object id
+ # ---------------------------------------------------------------------------
+
+ def __generateId (self):
+
+ # TODO: need a better algorithm here
+ result = ''
+ for i in range (0, 32):
+ result = result + str (int (whrandom.random () * 10))
+ return result
+
+ # ---------------------------------------------------------------------------
+ # Create a new record
+ # ---------------------------------------------------------------------------
+
+ def insertRecord (self, table):
+ """
+ Inserts a new record. A 'gnue_id' is assigned automatically.
+ """
+
+ id = self.__generateId ()
+ r = record (self.__cache, self.__connections, self.__database, table, id)
+ self.__cache.write (table, id, 'gnue_id', None, 0) # old id is None
+ self.__cache.write (table, id, 'gnue_id', id, 1) # new id
+ return r
+
+ # ---------------------------------------------------------------------------
+ # Delete a record
+ # ---------------------------------------------------------------------------
+
+ def deleteRecord (self, table, row):
+ """
+ Deletes the given record (acutally marks it for deletion on commit). All
+ data of the record will stay available until commit, but the field
+ 'gnue_id' will seem to have a value of None.
+ """
+
+ if not self.__cache.has (table, row, 'gnue_id'): # not yet in cache
+ self.__cache.write (table, row, 'gnue_id', row, 0)
+ self.__cache.write (table, row, 'gnue_id', None, 1)
+
+ # ---------------------------------------------------------------------------
+ # Find a record
+ # ---------------------------------------------------------------------------
+
+ def findRecord (self, table, row, fields):
+ """
+ Loads a record from the database. All fields given in 'fields' are fetched
+ from the database and cached, so that subsequential access to those fields
+ won't trigger another access to the db backend.
+
+ This method won't query the db backend for data which is already cached.
+ """
+
+ uncachedFields = []
+ for field in fields:
+ if not self.__cache.has (table, row, field):
+ uncachedFields.append(field)
+
+ if uncachedFields == []:
+ # already cached, no need to load from database
+ r = record (self.__cache, self.__connections, self.__database, table,
row)
+ else:
+ # not yet cached, need to load from database
+ resultSet = _find (self.__connections, self.__database, table, row,
+ fields)
+ if resultSet.current is None:
+ return None
+ r = record (self.__cache, self.__connections, self.__database, table,
row)
+ r._fill (fields, resultSet.current)
+ return r
+
+ # ---------------------------------------------------------------------------
+ # Write all changes back to the database
+ # ---------------------------------------------------------------------------
+
+ def commit (self):
+ """
+ Write all dirty data to the database backend by a single transaction that
+ is committed immediately. This operation invalidates the cache.
+ """
+
+ tables = self.__cache.dirtyTables ()
+ for (table, rows) in tables.items ():
+ for (row, fields) in rows.items ():
+ status = self.__cache.status (table, row)
+
+ if status == 'inserted':
+ resultSet = _createEmptyResultSet (self.__connections,
+ self.__database,
+ table, fields.keys ())
+ resultSet.insertRecord ()
+
+ for (field, value) in fields.items ():
+ resultSet.current.setField (field, value)
+
+ elif status == 'changed':
+ # TODO: gnue-common should provide a method for updating a record
+ # without reading it first. Until that is done, we have to create a
+ # temporary resultSet for every record we update
+ resultSet = _find (self.__connections, self.__database, table, row,
+ ['gnue_id'] + fields.keys ())
+
+ for (field, value) in fields.items ():
+ resultSet.current.setField (field, value)
+
+ elif status == 'deleted':
+ # TODO: gnue-common should provide a method for deleting a record
+ # without reading it first. Until that is done, we have to create a
+ # temporary resultSet for every record we delete
+ resultSet = _find (self.__connections, self.__database, table, row,
+ ['gnue_id'])
+ resultSet.current.delete ()
+
+ if status != '':
+ resultSet.post ()
+
+ # Commit the whole transaction
+ self.__connections.commitAll ()
+
+ # The transaction has ended. Changes from other transactions could become
+ # valid in this moment, so we have to clear the whole cache.
+ self.__cache.clear ()
+
+ # ---------------------------------------------------------------------------
+ # Undo all changes
+ # ---------------------------------------------------------------------------
+
+ def rollback (self):
+ """
+ Undo all uncommitted changes.
+ """
+
+ # Nothing written to the database yet, we only have to clean up the cache.
+ self.__cache.undo ()
+
+ # ---------------------------------------------------------------------------
+ # Close the connection
+ # ---------------------------------------------------------------------------
+
+ def close (self):
+ """
+ Close the connection to the database backend.
+ """
+
+ self.__connections.closeAll ()
+
+# =============================================================================
+# Recordset class
+# =============================================================================
+
+class recordset:
+ """
+ This class manages the result of a query. An instance of this class can be
+ created via the connection.query() method.
+ """
+
+ # ---------------------------------------------------------------------------
+ # Initialize
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, cache, connections, database, table, fields, conditions,
+ order):
+ self.__cache = cache
+ self.__connections = connections
+ self.__database = database
+ self.__table = table
+ self.__fields = ['gnue_id'] + fields
+ self.__resultSet = _createResultSet (self.__connections, self.__database,
+ self.__table, self.__fields,
+ conditions, order)
+
+ # ---------------------------------------------------------------------------
+ # Return the first record
+ # ---------------------------------------------------------------------------
+
+ def firstRecord (self):
+ """
+ Returns the first record or None if the set is empty.
+ """
+
+ if self.__resultSet.firstRecord () is None:
+ return None
+ else:
+ id = self.__resultSet.current ['gnue_id']
+ r = record (self.__cache, self.__connections, self.__database,
+ self.__table, id)
+ r._fill (self.__fields, self.__resultSet.current)
+ return r
+
+ # ---------------------------------------------------------------------------
+ # Return the next record
+ # ---------------------------------------------------------------------------
+
+ def nextRecord (self):
+ """
+ Returns the next record or None if nothing is left.
+ """
+
+ if self.__resultSet.nextRecord () is None:
+ return None
+ else:
+ id = self.__resultSet.current ['gnue_id']
+ r = record (self.__cache, self.__connections, self.__database,
+ self.__table, id)
+ r._fill (self.__fields, self.__resultSet.current)
+ return r
+
+ # ---------------------------------------------------------------------------
+ # Return the number of records
+ # ---------------------------------------------------------------------------
+
+ def count (self):
+
+ return self.__resultSet.getRecordCount ()
+
+# =============================================================================
+# Record class
+# =============================================================================
+
+class record:
+ """
+ This class stands for a record in a database table.
+ """
+
+ # ---------------------------------------------------------------------------
+ # Initialize
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, cache, connections, database, table, row):
+
+ self.__cache = cache
+ self.__connections = connections
+ self.__database = database
+ self.__table = table
+ self.__row = row
+
+ # ---------------------------------------------------------------------------
+ # Fill the cache for this record with data from a (gnue-common) RecordSet
+ # ---------------------------------------------------------------------------
+
+ def _fill (self, fields, RecordSet):
+
+ for field in fields:
+ # Never ever override the cache with data from the backend
+ if not self.__cache.has (self.__table, self.__row, field):
+ self.__cache.write (self.__table, self.__row, field, RecordSet [field],
+ 0)
+
+ # ---------------------------------------------------------------------------
+ # Get the value for a field
+ # ---------------------------------------------------------------------------
+
+ def getField (self, field):
+ """
+ Get the value for a field. If the value isn't cached, a new query to the
+ database is issued to get the value.
+ """
+
+ if self.__cache.has (self.__table, self.__row, field):
+ # If we find the field in the cache, use it
+ return self.__cache.read (self.__table, self.__row, field)
+ else:
+ # Not found in cache, so get it from the db
+ resultSet = _find (self.__connections, self.__database, self.__table,
+ self.__row, [field])
+ if resultSet.current is not None:
+ value = resultSet.current [field]
+ self.__cache.write (self.__table, self.__row, field, value, 0)
+ return value
+ else:
+ return None
+
+ # ---------------------------------------------------------------------------
+ # Put the value for a field
+ # ---------------------------------------------------------------------------
+
+ def putField (self, field, value):
+ """
+ Put the value for a field.
+ """
+
+ self.__cache.write (self.__table, self.__row, field, value, 1)
+
+# =============================================================================
+# Self test code
+# =============================================================================
+
+if __name__ == '__main__':
+
+ from gnue.common.apps import GClientApp
+
+ app = GClientApp.GClientApp ()
+
+ print 'create connection object ...',
+ c = connection (app.connections, 'gnue')
+ print 'Ok'
+
+ print 'connection.query for existing records ...'
+ rs = c.query ('address_person', ['address_name'], None, ['address_name'])
+ print 'Ok'
+
+ print 'recordset.firstRecord ...',
+ r = rs.firstRecord ()
+ print 'Ok'
+
+ print 'record.getField with prefetched data ...',
+ print r.getField ('address_name')
+
+ print 'record.getField with non-prefetched data ...',
+ print r.getField ('address_city')
+
+ print 'recordset.nextRecord ...',
+ r = rs.nextRecord ()
+ print 'Ok'
+
+ print 'record.getField with prefetched data ...',
+ print r.getField ('address_name')
+
+ print 'connection.insertRecord ...',
+ r = c.insertRecord ('address_person')
+ print 'Ok'
+
+ print 'record.getField ...',
+ id = r.getField ('gnue_id')
+ print id
+
+ print 'record.putField ...',
+ r.putField ('address_name', 'New Person')
+ print 'Ok'
+
+ print 'record.getField of inserted data ...',
+ print r.getField ('address_name')
+
+ print 'connection.commit with an inserted record ...',
+ c.commit ()
+ print 'Ok'
+
+ print 'connection.query for previously inserted record ...',
+ rs = c.query ('address_person', ['address_name'],
+ [['eq', ''], ['field', 'address_name'],
+ ['const', 'New Person']], None)
+ print 'Ok'
+
+ print 'recordset.firstRecord ...',
+ r = rs.firstRecord ()
+ print 'Ok'
+
+ print 'record.getField with prefetched data ...',
+ print r.getField ('address_name')
+
+ print 'record.putField of prefetched data ...',
+ r.putField ('address_name', 'New Name')
+ print 'Ok'
+
+ print 'record.putField of non-prefetched data ...',
+ r.putField ('address_city', 'New City')
+ print 'Ok'
+
+ print 'record.getField of changed data ...',
+ print r.getField ('address_name')
+
+ print 'connection.findRecord for previously changed record ...',
+ r = c.findRecord ('address_person', id, ['address_name'])
+ print 'Ok'
+
+ print 'record.getField of changed data, independent query ...',
+ print r.getField ('address_city')
+
+ print 'connection.commit with a changed record ...',
+ c.commit ()
+ print 'Ok'
+
+ print 'record.getField of prefetched data ...',
+ print r.getField ('address_name')
+
+ print 'connection.deleteRecord ...',
+ c.deleteRecord ('address_person', id)
+ print 'Ok'
+
+ print 'record.getField of deleted uncommitted record, prefetched ...',
+ print r.getField ('address_name')
+
+ print 'record.getField of deleted uncommitted record, non-prefetched ...',
+ print r.getField ('address_city')
+
+ print 'connection.commit with a deleted record ...',
+ c.commit ()
+ print 'Ok'
+
+ print 'check if the record is really gone now ...',
+ rs = c.query ('address_person', ['address_name'],
+ [['eq', ''], ['field', 'address_city'], ['const', 'New City']],
+ None)
+ if rs.firstRecord () != None:
+ raise Exception
+ print 'Ok'
+
+ print 'connection.close ...',
+ c.close ()
+ print 'Ok'
+
+ print 'Thank you for playing!'
Property changes on: trunk/gnue-appserver/src/data.py
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: trunk/gnue-appserver/src/geasInstance.py
===================================================================
--- trunk/gnue-appserver/src/geasInstance.py 2004-02-11 00:03:33 UTC (rev
5093)
+++ trunk/gnue-appserver/src/geasInstance.py 2004-02-12 00:24:40 UTC (rev
5094)
@@ -38,19 +38,18 @@
# Initalize
# ---------------------------------------------------------------------------
- def __init__ (self, session, list, record, classdef):
- self._session = session
- self._list = list
- self._record = record
- self._classdef = classdef
+ def __init__ (self, session, record, classdef):
+ self.__session = session
+ self.__record = record
+ self.__classdef = classdef
# ---------------------------------------------------------------------------
# Get the value of a property and convert it into RPC-transportable datatype
# ---------------------------------------------------------------------------
def _getValue (self, propertyname):
- propertydef = self._classdef.properties [propertyname]
- value = self._record.getField (propertydef.column)
+ propertydef = self.__classdef.properties [propertyname]
+ value = self.__record.getField (propertydef.column)
# Always return an empty string for NULL
if value is None:
@@ -139,7 +138,7 @@
"property '%s'") % (repr (value), propertyname)
# Reference property: gnue_type is a classname
- elif self._classdef.classes.has_key (propertydef.gnue_type):
+ elif self.__classdef.classes.has_key (propertydef.gnue_type):
if isinstance (value, types.UnicodeType):
# encode unicode values to utf-8 (normal case)
return value.encode ('utf-8')
@@ -172,7 +171,7 @@
# ---------------------------------------------------------------------------
def _putValue (self, propertyname, value):
- propertydef = self._classdef.properties [propertyname]
+ propertydef = self.__classdef.properties [propertyname]
# TODO: add more tests to see if the new value is valid, wrt max length
@@ -239,7 +238,7 @@
raise Exception, "Invalid value '%s' for property '%s'" % \
(value, propertyname)
- self._record.setField (propertydef.column, value)
+ self.__record.putField (propertydef.column, value)
# ---------------------------------------------------------------------------
# Set the values of a list of properties
@@ -252,13 +251,6 @@
i += 1
# ---------------------------------------------------------------------------
- # Delete this instance
- # ---------------------------------------------------------------------------
-
- def delete (self):
- self._record.delete ()
-
- # ---------------------------------------------------------------------------
# Call a procedure
# ---------------------------------------------------------------------------
@@ -269,29 +261,29 @@
# (needs to be implemented as an option in gnue-common)
# Create a session object which with the actual session id
- sess = Session.Session (self._session.sm, self._session.id)
+ sess = Session.Session (self.__session.sm, self.__session.id)
sess.setcontext(string.split(procedurename,'_')[0])
# Create an object representing the current business object
- obj = Object.Object (sess, self._classdef.fullName,
+ obj = Object.Object (sess, self.__classdef.fullName,
self._getValue ("gnue_id"))
# fetch the procedure definition
- proceduredef = self._classdef.procedures [procedurename]
+ proceduredef = self.__classdef.procedures [procedurename]
language = proceduredef.gnue_language
- rtlist = self._session.sm._langRuntimes
+ rtlist = self.__session.sm._langRuntimes
if not rtlist.has_key(language):
- self._session.sm._langRuntimes[language] = loadLanguageEngine(language)
- rtlist = self._session.sm._langRuntimes
+ self.__session.sm._langRuntimes[language] = loadLanguageEngine(language)
+ rtlist = self.__session.sm._langRuntimes
cx=rtlist[language].createNewContext()
# describe the context
- cx.setDescription('%s.%s' % (self._classdef.fullName,procedurename),
+ cx.setDescription('%s.%s' % (self.__classdef.fullName,procedurename),
'a gnue-appserver procedure')
# the object itself
@@ -303,11 +295,11 @@
cx.bindFunction('new', sess.new)
# direct access to RPC API func.
- cx.bindFunction('direct_request', self._session.request)
- cx.bindFunction('direct_fetch', self._session.fetch)
- cx.bindFunction('direct_load', self._session.load)
- cx.bindFunction('direct_store', self._session.store)
- cx.bindFunction('direct_call', self._session.call)
+ cx.bindFunction('direct_request', self.__session.request)
+ cx.bindFunction('direct_fetch', self.__session.fetch)
+ cx.bindFunction('direct_load', self.__session.load)
+ cx.bindFunction('direct_store', self.__session.store)
+ cx.bindFunction('direct_call', self.__session.call)
try:
method = cx.buildMethod(proceduredef.gnue_name, proceduredef.gnue_code)
Modified: trunk/gnue-appserver/src/geasList.py
===================================================================
--- trunk/gnue-appserver/src/geasList.py 2004-02-11 00:03:33 UTC (rev
5093)
+++ trunk/gnue-appserver/src/geasList.py 2004-02-12 00:24:40 UTC (rev
5094)
@@ -19,9 +19,8 @@
# write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
-# $Id: geasList.py,v 1.31 2003/10/07 16:03:53 siesel Exp $
+# $Id$
-from gnue.common.datasources import GDataSource, GConditions
import geasInstance
import string
@@ -35,43 +34,26 @@
# Initalize
# ---------------------------------------------------------------------------
- def __init__ (self, session, classdef, propertylist, sortorder):
- self._session = session
- self._classdef = classdef
- self._prefetch = propertylist # property names to be prefetched
- self._sort = sortorder # property names of the sort key
+ def __init__ (self, session, connection, classdef, propertylist, sortorder):
+ self.__session = session
+ self.__connection = connection
+ self.__classdef = classdef
+ self.__prefetch = propertylist # property names to be prefetched
+ self.__sort = sortorder # property names of the sort key
- self._prefColumns = [] # (db) column names to be prefetched
- self._sortColumns = [] # (db) column names of the sort key
+ self.__prefColumns = [] # (db) column names to be prefetched
+ self.__sortColumns = [] # (db) column names of the sort key
# test if all properties in prefetch list are valid and find column names
- for propertyname in self._prefetch:
- propertydef = self._classdef.properties [propertyname]
- self._prefColumns.append (propertydef.column)
+ for propertyname in self.__prefetch:
+ propertydef = self.__classdef.properties [propertyname]
+ self.__prefColumns.append (propertydef.column)
# test if all properties in sort list are valid and find column names
- for propertyname in self._sort:
- propertydef = self._classdef.properties [propertyname]
- self._sortColumns.append (propertydef.column)
+ for propertyname in self.__sort:
+ propertydef = self.__classdef.properties [propertyname]
+ self.__sortColumns.append (propertydef.column)
- # prepare attributes of the datasource
- attributes = {}
- attributes ["name"] = ""
- attributes ["database"] = self._session.database
- attributes ["table"] = self._classdef.table
-
- if self._sortColumns != []:
- attributes ["order_by"] = string.joinfields (self._sortColumns, ",")
-
- # create the datasource
- self._datasource = GDataSource.DataSourceWrapper (
- connections = self._session.connections,
- attributes = attributes,
- fields = self._prefColumns)
-
- # enable unicode mode for the datasource
- self._datasource._dataObject._unicodeMode=1
-
# ---------------------------------------------------------------------------
# Populate the list with data from the database backend
# ---------------------------------------------------------------------------
@@ -83,42 +65,19 @@
def populate (self, conditions):
# TODO: translate property names to column names in conditions
- # now building GCondition tree
- conditionTree = GConditions.buildTreeFromPrefix (conditions)
- self._resultset = self._datasource.createResultSet (conditionTree)
+ self.__recordset = self.__connection.query (self.__classdef.table,
+ self.__prefColumns,
+ conditions,
+ self.__sortColumns)
# ---------------------------------------------------------------------------
- # Populate the list with a single instance with given gnue_id, and return
- # that instance, return an exception if not found
- # ---------------------------------------------------------------------------
-
- def find (self, object_id):
- self.populate ([['eq', ''], ['field', 'gnue_id'], ['const', object_id]])
- result = self.firstInstance ()
- if result == None:
- raise Exception, "Can't find object ID '%s' in class '%s'" % \
- (object_id, self._classdef.fullName)
- return result
-
- # ---------------------------------------------------------------------------
- # Populate the list with a single empty instance, and return that instance
- # ---------------------------------------------------------------------------
-
- def newInstance (self):
- self._resultset = self._datasource.createEmptyResultSet ()
- self._resultset.insertRecord ()
- return geasInstance.geasInstance (self._session, self,
- self._resultset.current, self._classdef)
-
- # ---------------------------------------------------------------------------
# Get the first instance in the list
# ---------------------------------------------------------------------------
def firstInstance (self):
- if self._resultset.firstRecord () != None:
- return geasInstance.geasInstance (self._session, self,
- self._resultset.current,
- self._classdef)
+ record = self.__recordset.firstRecord ()
+ if record is not None:
+ return geasInstance.geasInstance (self.__session, record,
self.__classdef)
else:
return None
@@ -127,10 +86,9 @@
# ---------------------------------------------------------------------------
def nextInstance (self):
- if self._resultset.nextRecord () != None:
- return geasInstance.geasInstance (self._session, self,
- self._resultset.current,
- self._classdef)
+ record = self.__recordset.nextRecord ()
+ if record is not None:
+ return geasInstance.geasInstance (self.__session, record,
self.__classdef)
else:
return None
@@ -139,7 +97,7 @@
# ---------------------------------------------------------------------------
def count (self):
- return self._resultset.getRecordCount ()
+ return self.__recordset.count ()
# ---------------------------------------------------------------------------
# Fetch data from the database backend
@@ -154,7 +112,7 @@
instance = self.firstInstance ()
while (instance != None) and (c < start + count):
if c >= start:
- result.append (instance.get (self._prefetch))
+ result.append (instance.get (self.__prefetch))
c += 1
instance = self.nextInstance ()
return result
Modified: trunk/gnue-appserver/src/geasSession.py
===================================================================
--- trunk/gnue-appserver/src/geasSession.py 2004-02-11 00:03:33 UTC (rev
5093)
+++ trunk/gnue-appserver/src/geasSession.py 2004-02-12 00:24:40 UTC (rev
5094)
@@ -21,25 +21,11 @@
#
# $Id$
-import geasList
+from gnue.appserver import data
+import geasList, geasInstance
import whrandom
# =============================================================================
-# Helper functions
-# =============================================================================
-
-# -----------------------------------------------------------------------------
-# Generate a new object_id
-# -----------------------------------------------------------------------------
-
-def new_object_id ():
- # FIXME: need a better algorithm here
- result = ""
- for i in range (0, 32):
- result = result + str (int (whrandom.random () * 10))
- return result
-
-# =============================================================================
# Session class
# =============================================================================
@@ -50,33 +36,58 @@
# ---------------------------------------------------------------------------
def __init__ (self, connections, authAdapter, sm, id):
+
self.loggedIn = 0
self.connections = connections
try:
self.database = gConfig('database')
except:
self.database = "gnue"
- self._user = ""
- self._lists = {}
- self._listcount = 0
- self._authAdapter = authAdapter
+
+ self.__user = ""
+ self.__lists = {}
+ self.__listcount = 0
+ self.__authAdapter = authAdapter
+ self.__connection = None
+
self.sm = sm # The session manager
self.id = id # The session id
- # TODO: gnue-common should have a concept of sessions. Until that is done,
- # we have to keep a list of geasList objects that need to be committed.
- self._dirtyLists = []
+ # ---------------------------------------------------------------------------
+ # Get a class definition from a class name and check access
+ # ---------------------------------------------------------------------------
+ def __get_classdef (self, classname):
+
+ # check if user has access rights for this class
+# if not self.__authAdapter.hasAccess (self, self.__user, classname):
+# raise Exception, "Access to class '%s' denied" % classname
+
+ return self.sm.classes [classname]
+
# ---------------------------------------------------------------------------
+ # Get a list of field names from a list of property names
+ # ---------------------------------------------------------------------------
+
+ def __get_fieldlist (self, classdef, propertylist):
+
+ return ([classdef.properties[p].column for p in propertylist])
+
+ # ---------------------------------------------------------------------------
# Log into the application server
# ---------------------------------------------------------------------------
def login (self, user, password):
+
# This username/password is for the Application Server, not for the
# database.
- self._user = user
- self.loggedIn = self._authAdapter.authenticate(self, user,
- {'password':password} )
+ self.__user = user
+# self.loggedIn = self.__authAdapter.authenticate(self, user,
+# {'password':password} )
+ self.loggedIn = 1
+ if self.loggedIn:
+ self.__connection = data.connection (self.connections, self.database)
+
return self.loggedIn
# ---------------------------------------------------------------------------
@@ -84,67 +95,52 @@
# ---------------------------------------------------------------------------
def logout (self):
- # should the authAdapter be contacted?
+
+ # FIXME: should the authAdapter be contacted?
+ self.__connection.close ()
self.loggedIn = 0
# ---------------------------------------------------------------------------
- # Create a new list of business objects of a given class
- # ---------------------------------------------------------------------------
-
- def _createList (self, classname, propertylist, sortorder):
-
- # check if user has access rights for this class
- if not self._authAdapter.hasAccess (self, self._user, classname):
- raise Exception, "Access to class '%s' denied" % classname
-
- classdef = self.sm.classes [classname]
-
- # create new List
- return geasList.geasList (self, classdef, propertylist, sortorder)
-
- # ---------------------------------------------------------------------------
# Commit the active transaction
# ---------------------------------------------------------------------------
def commit (self):
- # TODO: gnue-common should support the concept of a session and commits
- # on session level
- for l in self._dirtyLists:
- if hasattr (l, "_datasource"):
- l._resultset.post ()
- l._datasource.commit ()
+ self.__connection.commit ()
+
# ---------------------------------------------------------------------------
# Rollback the active transaction
# ---------------------------------------------------------------------------
def rollback (self):
- # TODO: gnue-common should support the concept of a session and rollbacks
- # on session level
- for l in self._dirtyLists:
- if hasattr (l, "_datasource"):
- l._datasource.rollback ()
+ self.__connection.rollback ()
+
# ---------------------------------------------------------------------------
# Create a new list of business objects of a given class
# ---------------------------------------------------------------------------
def request (self, classname, conditions, sortorder, propertylist):
- # FIXME: this list needn't be considered by commit and rollback
- list = self._createList (classname, ["gnue_id"] + propertylist, sortorder)
+
+ classdef = self.__get_classdef (classname)
+ list = geasList.geasList (self, self.__connection, classdef,
+ ['gnue_id'] + propertylist,
+ sortorder)
+
list.populate (conditions)
- self._listcount += 1
- self._lists [self._listcount] = list
- list_id = self._listcount
+ self.__listcount += 1
+ self.__lists [self.__listcount] = list
+ list_id = self.__listcount
return list_id;
# ---------------------------------------------------------------------------
# Check list id and raise exception if invalid, return list otherwise
# ---------------------------------------------------------------------------
- def _getList (self, list_id):
- if self._lists.has_key (list_id):
- return self._lists [list_id]
+ def __getList (self, list_id):
+
+ if self.__lists.has_key (list_id):
+ return self.__lists [list_id]
else:
raise Exception, "Can't find a list with ID '%s'" % list_id
@@ -153,7 +149,8 @@
# ---------------------------------------------------------------------------
def count (self, list_id):
- list = self._getList (list_id)
+
+ list = self.__getList (list_id)
return list.count ();
# ---------------------------------------------------------------------------
@@ -161,23 +158,47 @@
# ---------------------------------------------------------------------------
def fetch (self, list_id, start, count):
- list = self._getList (list_id)
+
+ list = self.__getList (list_id)
return list.fetch (start, count)
# ---------------------------------------------------------------------------
+ # Create a single geasInstance object from an existing record
+ # ---------------------------------------------------------------------------
+
+ def __findInstance (self, classdef, object_id, propertylist):
+
+ table = classdef.table
+ fieldlist = self.__get_fieldlist (classdef, propertylist)
+ record = self.__connection.findRecord (table, object_id, fieldlist)
+ return geasInstance.geasInstance (self, record, classdef)
+
+ # ---------------------------------------------------------------------------
+ # Create a single geasInstance object for a new record
+ # ---------------------------------------------------------------------------
+
+ def __newInstance (self, classdef):
+
+ table = classdef.table
+ record = self.__connection.insertRecord (table)
+ return geasInstance.geasInstance (self, record, classdef)
+
+ # ---------------------------------------------------------------------------
# Load data from the database backend
# ---------------------------------------------------------------------------
def load (self, classname, obj_id_list, propertylist):
- list = self._createList (classname, ["gnue_id"] + propertylist, [])
+
+ classdef = self.__get_classdef (classname)
+
result = []
for object_id in obj_id_list:
if object_id == "":
- classdef = self.sm.classes [classname]
result.append ([classdef.properties[p].fullType for p in propertylist])
else:
- instance = list.find (object_id)
+ instance = self.__findInstance (classdef, object_id, propertylist)
result.append (instance.get (propertylist))
+
return result
# ---------------------------------------------------------------------------
@@ -185,25 +206,21 @@
# ---------------------------------------------------------------------------
def store (self, classname, obj_id_list, propertylist, data):
+
+ classdef = self.__get_classdef (classname)
+
result = []
i = 0
for object_id in obj_id_list:
- # TODO: gnue-common should provide a method for updating a record without
- # reading it first. Until that is done, we have to create a temporary
- # list for every record we update and keep it in our list of pending
- # commits.
- list = self._createList (classname, ["gnue_id"] + propertylist, [])
if object_id:
- instance = list.find (object_id)
+ instance = self.__findInstance (classdef, object_id, [])
result.append (object_id)
else:
- instance = list.newInstance ()
- newid = new_object_id ()
- instance.put (["gnue_id"], [newid])
- result.append (newid)
+ instance = self.__newInstance (classdef)
+ result.append (instance.get (['gnue_id']) [0])
instance.put (propertylist, data [i])
- self._dirtyLists.append (list)
i += 1
+
return result
# ---------------------------------------------------------------------------
@@ -211,24 +228,23 @@
# ---------------------------------------------------------------------------
def delete (self, classname, obj_id_list):
+
+ classdef = self.__get_classdef (classname)
+
for object_id in obj_id_list:
- # TODO: gnue-common should provide a method to delete a record without
- # reading it first. Until that is done, we have to create a temporary
- # list for every record we update and keep it in our list of pending
- # commits.
- list = self._createList (classname, ["gnue_id"], [])
- instance = list.find (object_id)
- instance.delete ()
- self._dirtyLists.append (list)
+ self.__connection.deleteRecord (classdef.table, object_id)
# ---------------------------------------------------------------------------
# Call a procedure of business objects
# ---------------------------------------------------------------------------
def call (self, classname, obj_id_list, procedurename, parameters):
- list = self._createList (classname, ["gnue_id"], [])
+
+ classdef = self.__get_classdef (classname)
+
result = []
for object_id in obj_id_list:
- instance = list.find (object_id)
+ instance = self.__findInstance (classdef, object_id, [])
result.append (instance.call (procedurename, parameters))
+
return result
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- r5094 - trunk/gnue-appserver/src,
reinhard <=