2
# This file is part of GNU Enterprise.
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.
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.
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.
19
# Copyright 2000-2005 Free Software Foundation
21
# $Id: GDataSource.py 7080 2005-03-02 22:00:12Z reinhard $
24
Class that implements a provider-independent DataSource object
27
from gnue.common.apps import i18n, errors
28
from gnue.common.definitions import GObjects
29
import sys, string, types, cStringIO
30
from gnue.common.datasources import GConnections
31
from gnue.common.formatting import GTypecast
32
from gnue.common.datasources import GConditions, Exceptions
33
from gnue.common.definitions.GParserHelpers import GContent
34
from gnue.common.definitions.GParser import MarkupError
37
########################################################################
41
########################################################################
43
class GDataSource(GObjects.GObj):
45
Class that handles DataSources. This is a subclass of GObj, which
46
means this class can be created from XML markup and stored in an
47
Object tree (e.g., a Forms tree).
49
def __init__(self, parent=None, type="GDataSource"):
50
GObjects.GObj.__init__(self, parent, type)
52
self.connection = None
53
self._connections = None
54
self._dataObject = None
55
self._connectionComment = ""
56
self._fieldReferences = {}
57
self._unboundFieldReferences = {}
58
self._defaultValues = {}
59
self._ignoreDispatchEvent = None
61
self._inits =[self.primaryInit, self.secondaryInit, self.tertiaryInit]
62
self._currentResultSet = None
63
self._resultSetListeners = []
64
self._toplevelParent = None # Needs to be set by subclass
65
# so that _topObject gets set
66
self._topObject = None
72
self._triggerGlobal = True
73
self._triggerFunctions = {'createResultSet':{'function':self.createResultSet,
75
'simpleQuery':{'function':self.triggerSimpleQuery,
77
'delete':{'function':self.deleteCurrentRecordsetEntry
79
'call':{'function':self.callFuncOfCurrentRecordsetEntry
81
'getCondition':{'function':self.getCondition},
82
'setCondition':{'function':self.setCondition},
83
'count' : {'function':self.triggerGetCount},
84
'update': {'function':
85
self.updateCurrentRecordSet}
88
self._triggerProperties = {'extensions':{'get':self.getExtensions,
90
'recordCount':{'get':self.triggerGetCount,
92
'order_by':{'get':self.triggerGetOrderBy,
93
'set':self.triggerSetOrderBy,
97
def _ignoreDispatchEvent(self, *args, **parms):
99
A dummy method that _dispatchEvent is initially set to; does nothing
103
def setEventListener(self, listener):
104
self._dispatchEvent = listener
106
self._dataObject._dispatchEvent = listener
108
def __getattr__(self, attr):
109
if self._dataObject and attr[1] != '_' and hasattr(self._dataObject,attr):
110
return getattr(self._dataObject,attr)
112
raise AttributeError, attr
114
def _buildObject(self):
116
# Added 0.5.0 -- Delete before 1.0
117
if hasattr(self,'database'):
118
self.connection = self.database
122
if len(self.explicitfields):
123
for field in string.split(self.explicitfields,','):
124
gDebug (7, "Explicit field %s" % field)
125
self._fieldReferences[field] = True
126
except AttributeError:
129
self.__getSortOrder ()
131
for field in [item ['name'] for item in self.sorting]:
132
self._fieldReferences [field] = True
134
return GObjects.GObj._buildObject(self)
136
def triggerGetOrderBy(self):
139
def triggerSetOrderBy(self,value):
140
self.sorting = self.__convertOrderBy (value)
142
self._dataObject.sorting = self.sorting
144
def triggerGetCount(self):
145
if self._currentResultSet:
146
return len(self._currentResultSet)
150
def triggerSimpleQuery(self,maskDict):
153
for key in maskDict.keys():
154
queryDict[key] = "%s" % maskDict[key]
155
if not len(queryDict[key]):
159
conditions = GConditions.buildConditionFromDict(queryDict,GConditions.GClike)
160
resultSet = self.createResultSet(conditions)
161
recordCount = resultSet.getRecordCount()
164
for count in range(recordCount):
165
record = resultSet.getRecord(count)
167
for key in record._fields.keys():
168
resultDict[key]=record.getField(key) or ""
169
returnList.append(resultDict)
172
def deleteCurrentRecordsetEntry(self):
173
self._currentResultSet.getPostingRecordset().delete()
175
def callFuncOfCurrentRecordsetEntry(self,name,params):
176
n=self._currentResultSet.getRecordNumber()
177
rset=self._currentResultSet.getRecord(n)
178
if hasattr(rset,'callFunc'):
179
return rset.callFunc(name,params)
181
tmsg = u_("Backend doesn't support the trigger 'call' function")
182
raise StandardError, tmsg
185
# ---------------------------------------------------------------------------
186
# Update the current record set
187
# ---------------------------------------------------------------------------
189
def updateCurrentRecordSet (self):
191
If a result set is available having a record all fields of this record
192
would be updated. If the backend does not support this operation an
193
ApplicationError will be raised.
196
if self._currentResultSet is not None:
197
nr = self._currentResultSet.getRecordNumber ()
198
rs = self._currentResultSet.getRecord (nr)
200
if hasattr (rs, 'updateRecordSet'):
201
rs.updateRecordSet ()
204
raise errors.ApplicationError, \
205
u_("Backend does not support the 'update' function")
208
# get/set the static condition assosiated with a datasource
209
# the static condition is build out of the <condition> child
210
# elements below a datasource XML definition
212
def setCondition(self, mycondition):
213
self._dataObject._staticCondition = mycondition
214
# dataObject.invalidateCachedConditions()
216
def getCondition(self):
217
return self._dataObject._staticCondition
220
# get the dbdriver extension object
222
def getExtensions(self):
223
return self.extensions
226
# This method should be called after the object is created
227
# but before any other methods are called
229
def setConnectionManager(self, connectionManager):
230
self._connections = connectionManager
232
def initialize(self):
233
if not self.connection:
234
# We are a connectionless datasource (virtual?)
235
# We have to bind to something, so bind to empty or static driver
236
if not self.type=="static":
237
from gnue.common.datasources.drivers.special.unbound import Driver
238
gDebug (7, 'Using empty data driver')
239
dataObject = Driver.supportedDataObjects['object'](None)
242
from gnue.common.datasources.drivers.special.static import Connection
243
gDebug (7, 'Using static data driver')
244
dataObject = Connection.supportedDataObjects['object'](None)
246
for child in self._children:
247
if isinstance(child, GStaticSet):
248
dataObject._staticSet = child
252
elif self._connections:
253
self.connection = string.lower(self.connection)
254
# This will throw a GConnections.NotFoundError if an unknown
255
# connection name is specified. The calling method should
256
# catch this exception and handle it properly (exit w/message)
258
self._connections.getDataObject(self.connection, self.type)
259
gDebug (7, "GDataSource.py bound to %s " % dataObject)
261
self.name = string.lower(self.name)
262
self._topObject._datasourceDictionary[self.name]=self
264
dataObject._fieldReferences = self._fieldReferences
265
dataObject._unboundFieldReferences = self._unboundFieldReferences
266
dataObject._defaultValues = self._defaultValues
267
dataObject._dataSource = self
269
# TODO: Short-term hack to allow primary key support
271
dataObject._primaryIdField = self.primarykey
272
dataObject._primaryIdFormat = "%s = '%%s'" % self.primarykey
273
dataObject._primaryIdChecked = True
274
except AttributeError:
278
for child in self._children:
279
if isinstance(child, GConditions.GCondition):
280
dataObject._staticCondition = child
282
elif isinstance(child, GSql):
283
dataObject._rawSQL = child
287
if self.type == "sql" and not hasRaw:
288
raise Exceptions.InvalidDatasourceDefintion, \
289
u_("Datasource %s is sql-based, but has no <sql> definition.") \
291
elif not self.type == "sql" and hasRaw:
292
raise Exceptions.InvalidDatasourceDefintion, \
293
u_("Datasource %s is not sql-based, but has a <sql> definition.") \
297
# Copy all attributes from XML to the dataObject
298
tagAttributes = getXMLelements()['datasource']['Attributes']
299
for attribute in tagAttributes.keys():
300
if self.__dict__.has_key(attribute):
301
dataObject.__dict__[attribute] = self.__dict__[attribute]
304
dataObject.__dict__[attribute] = tagAttributes[attribute]['Default']
307
self._dataObject = dataObject
308
self._dataObject.sorting = self.sorting
312
if self.connection != None:
313
self._connections.requestConnection(self._dataObject)
316
def getDataObject(self):
317
return self._dataObject
320
def referenceField(self, field, defaultValue=None):
322
gDebug (7, 'Field %s implicitly referenced' % field)
323
self._fieldReferences[field] = ""
325
if defaultValue != None:
326
self._defaultValues[field] = defaultValue
328
def referenceFields(self, fields):
330
if isinstance (field, types.StringType) or \
331
isinstance (field, types.UnicodeType):
332
self.referenceField(field)
334
self.referenceField(*field)
336
def referenceUnboundField(self, field, defaultValue=None):
338
gDebug (7,'Unbound Field %s implicitly referenced' % field)
339
self._unboundFieldReferences[field] = True
341
if defaultValue != None:
342
self._defaultValues[field] = defaultValue
346
# The following is a simple wrapper around the datasource's dataobject
347
# to hide the dataobject from the app programmer
350
return self._dataObject != None and self._dataObject.hasMaster()
352
def createResultSet(self, conditions={}, readOnly=False, sql=""):
353
resultSet= self._dataObject.createResultSet(conditions,readOnly,sql=sql)
354
self.__setResultSet( resultSet )
357
def addDetailDataObject(self, dataObject, handler=None):
358
self._dataObject.addDetailDataObject(dataObject, handler)
360
def createEmptyResultSet(self, readOnly=False,masterRecordSet=None):
361
resultSet = self._dataObject.createEmptyResultSet(readOnly, masterRecordSet=masterRecordSet)
362
self.__setResultSet( resultSet )
365
def getQueryString(self,conditions={},forDetailSQL=None,additionalSQL=""):
366
return self._dataObject.getQueryString(conditions, forDetailSQL,additionalSQL)
369
# Master/detail stuff
372
# Called by dbdrivers whenever this datasource's master has changed
373
def masterResultSetChanged(self, masterResultSet, detailResultSet):
374
self._masterResultSet = masterResultSet
375
self.__setResultSet( detailResultSet )
377
def __setResultSet(self, resultSet):
378
self._currentResultSet = resultSet
379
# Notify all the listeners (i.e., blocks) that the result set changed
380
for listener in self._resultSetListeners:
383
def registerResultSetListener(self, listener):
384
self._resultSetListeners.append(listener)
386
def primaryInit(self):
387
self._topObject = self.findParentOfType(self._toplevelParent)
388
gDebug (9, "Setting %s to connect mgr %s" \
389
% (self.name, self._topObject._connections))
390
self.__getSortOrder ()
391
self.setConnectionManager(self._topObject._connections)
394
self.extensions = self._dataObject.triggerExtensions
397
# TODO: Merged into GDataSource per the TODOs in reports and forms however
398
# TODO: self._topObject._datasourceDictionary implies that the top object
399
# TODO: always has a specifc structure. This is a bad thing :( Maybe GRootObj
400
# TODO: should contain a getDatasourceDict()?
402
def secondaryInit(self):
404
if hasattr(self, 'master') and self.master:
406
self.master = string.lower(self.master)
407
gDebug (7, "Linking detail '%s' to master '%s'" \
408
% (self.name, self.master))
410
if self._topObject._datasourceDictionary.has_key(self.master):
411
self._topObject._datasourceDictionary[self.master] \
412
.getDataObject().addDetailDataObject(self.getDataObject(),
415
tmsg = u_("Detail source '%(source)s' references non-existant master "
417
% {'source': self.name,
418
'master': self.master}
419
raise StandardError, tmsg
423
def tertiaryInit(self):
424
if hasattr(self, 'prequery'):
425
if not self.hasMaster() and self.prequery:
426
self.createResultSet()
429
# ---------------------------------------------------------------------------
430
# Make sure we have either no sort order, or one in the proper format
431
# ---------------------------------------------------------------------------
433
def __getSortOrder (self):
435
# If there is both, an order_by attribute *and* a sorting-tag, we've stop
436
child = self.findChildOfType ('GCSortOrder')
437
if child is not None and hasattr (self, 'order_by'):
439
(u_("The use of order_by is depreciated. Please use <sortorder> "
440
"instead"), self._url, self._lineNumber)
442
# If there's a sorting tag, we'll use this first
443
if child is not None:
445
for item in child.findChildrenOfType ('GCSortField'):
446
self.sorting.append ({'name' : item.name,
447
'descending': item.descending,
448
'ignorecase': item.ignorecase})
450
# otherwise let's investigate the order_by attribute given
451
elif hasattr (self, 'order_by'):
452
self.sorting = self.__convertOrderBy (self.order_by)
453
delattr (self, 'order_by')
456
# ---------------------------------------------------------------------------
457
# convert an order_by rule into the new 'sorting' format
458
# ---------------------------------------------------------------------------
460
def __convertOrderBy (self, order_by):
462
This function transforms an order_by rule into a proper 'sorting' sequence,
463
made of tuples with fieldname and sort-direction.
465
@param order_by: string, unicode-string, sequence with sort-order
466
@return: sequence of tuples (field, direction)
471
# If it's a string or a unicode string, we transform it into a tuple
472
# sequence, where all items are treated to be in 'ascending' order
473
if isinstance (order_by, types.StringType) or \
474
isinstance (order_by, types.UnicodeType):
475
gDebug (1, "DEPRECIATION WARNING: use of 'order_by' attribute is " \
476
"depreciated. Please use <sortorder> instead.")
477
for field in string.split (order_by, ','):
478
(item, desc) = (field, field [-5:].lower () == ' desc')
480
item = item [:-5].strip ()
482
# Since the old 'order_by' format does *not* support 'ignorecase' we
483
# will use 'False' as default
484
result.append ({'name': item, 'descending': desc})
486
# Well, order_by is already a sequence. So we've to make sure it's a
487
# sequence of tuples with fieldname and direction.
488
elif isinstance (order_by, types.ListType):
489
for item in order_by:
490
if isinstance (item, types.StringType) or \
491
isinstance (item, types.UnicodeType):
492
result.append ({'name':item})
494
elif isinstance (item, types.DictType):
498
element = {'name': item [0]}
499
if len (item) > 1: element ['descending'] = item [1]
500
if len (item) > 2: element ['ignorecase'] = item [2]
504
(u_("Unknown type/format of 'order-by' attribute"), self._url,
511
# Find a specific record in the resultset by field values
512
def findRecord(self, fieldValues):
513
self._currentResultSet.findRecord(fieldValues)
516
# Hooks for record-level triggers
519
def _beforeCommitInsert(self, record):
522
def _beforeCommitUpdate(self, record):
525
def _beforeCommitDelete(self, record):
528
def _onModification(self, record):
531
def _onRecordLoaded(self, record):
537
# If we have a dataObject available, make sure it's reference to the
538
# datasource will be cleared, so garbage collection can do it's job
539
if self._dataObject is not None:
540
self._dataObject._dataSource = None
542
self._dataObject = None
544
# Make sure we leave no unreachable reference !
545
if self._topObject._datasourceDictionary.has_key (self.name):
546
del self._topObject._datasourceDictionary
548
self._fieldReferences = None
549
self._unboundFieldReferences = None
550
self._defaultValues = None
551
self._ignoreDispatchEvent = None
554
self._currentResultSet = None
555
self._resultSetListeners = None
556
self._toplevelParent = None
557
self._topObject = None
560
self._triggerFunctions = None
561
self._triggerProperties = None
569
class GSql(GObjects.GObj):
570
def __init__(self, parent=None):
571
GObjects.GObj.__init__(self, parent, type="GDSql")
575
# Static Datasource Support
578
class GStaticSet(GObjects.GObj):
579
def __init__(self, parent=None):
580
GObjects.GObj.__init__(self, parent, type="GDStaticSet")
582
class GStaticSetRow(GObjects.GObj):
583
def __init__(self, parent=None):
584
GObjects.GObj.__init__(self, parent, type="GDStaticSetRow")
586
class GStaticSetField(GObjects.GObj):
587
def __init__(self, parent=None):
588
GObjects.GObj.__init__(self, parent, type="GDStaticSetField")
596
class GConnection(GObjects.GObj):
597
def __init__(self, parent=None):
598
GObjects.GObj.__init__(self, parent, "GCConnection")
601
self._inits =[self.initialize]
603
def _buildObject(self):
604
self.name = string.lower(self.name)
605
return GObjects.GObj._buildObject(self)
607
def initialize(self):
608
# Add our database connection information to the connections
609
# manager, then let it handle everything from there.
610
root = self.findParentOfType(None)
611
root._instance.connections.\
612
addConnectionSpecification(self.name, {
614
'provider': self.provider,
615
'dbname': self.dbname,
616
'host': self.host } )
622
# Used by client GParsers to automatically pull supported xml tags
627
# Return any XML elements associated with
628
# GDataSources. Bases is a dictionary of tags
629
# whose values are update dictionaries.
630
# For example: bases={'datasource': {'BaseClass':myDataSource}}
631
# sets xmlElements['datasource']['BaseClass'] = myDataSource
633
def getXMLelements(updates={}):
637
'BaseClass': GDataSource,
639
'Description': 'A datasource provides a link to a database table '
640
'or some similar data store.',
645
'Typecast': GTypecast.name,
646
'Description': 'Unique name of the datasource.' },
648
'Label': _('Data Object Type'),
649
'Typecast': GTypecast.name,
650
'Default': "object" },
652
'Label': _('Connection Name'),
653
'Typecast': GTypecast.name,
654
'Description': 'The name of the connection as in '
655
'connections.conf that points to '
656
'a valid database.' },
658
'Typecast': GTypecast.name,
659
'Deprecated': 'Use {connection} attribute instead' },
661
'Label': _('Table Name'),
662
'Typecast': GTypecast.name,
663
'Description': 'The table in the database this datasource '
666
'Label': _('Cache Size'),
667
'Description': 'Cache this number of records',
668
'Typecast': GTypecast.whole,
671
'Label': _('Query on Startup'),
672
'Description': 'If true, the datasource is populated on '
673
'form startup. If false (default), the form '
674
'starts out with an empty record until the user '
675
'or a trigger queries the database.',
676
'Typecast': GTypecast.boolean,
679
'Typecast': GTypecast.boolean,
681
'Description': 'TODO' },
683
'Typecast': GTypecast.text ,
684
'Deprecated': 'Use {sortorder} tag instead' },
686
'Label': _('M/D Master DataSource'),
687
'Description': 'If this datasource is the child in a '
688
'master/detail relationship, this property '
689
'contains the name of the master datasource.',
690
'Typecast': GTypecast.name },
692
'Label': _('M/D Master Field(s)'),
693
'Description': 'If this datasource is the child in a '
694
'master/detail relationship, this property '
695
'contains a comma-separated list of the '
696
'master datasource\'s field(s) used for '
698
'Typecast': GTypecast.text },
700
'Label': _('M/D Detail Field(s)'),
701
'Description': 'If this datasource is the child in a '
702
'master/detail relationship, this property '
703
'contains a comma-separated list of the '
704
'this (child\'s) datasource\'s field(s) used '
706
'Typecast': GTypecast.text },
707
# TODO: Short-term hack
709
'Label': _('Explicit Fields'),
710
'Typecast': GTypecast.text,
711
'Description': 'TODO' },
713
'Label': _('Primary Key Field(s)'),
714
'Description': 'Comma-separated list of the fields that '
715
'make up the primary key.',
716
'Typecast': GTypecast.text },
718
'Label': _('Primary Key Sequence'),
719
'Description': 'Name of the sequence used to populate a '
720
'primary key (only applies to relational '
721
'backends that support sequences; requires '
722
'a single {primarykey} value.',
723
'Typecast': GTypecast.text },
725
'Label': _('Re-query on commit?'),
727
'Description': 'Requery a record after posting it; requires '
728
'{primarykey} support and a non-null primary '
729
'key value at the time of update (whether '
730
'via a trigger or by the use of '
732
'Typecast': GTypecast.text }
734
'ParentTags': None },
737
'BaseClass': GCSortOrder,
739
'ParentTags': ('datasource',),
742
'BaseClass': GCSortField,
747
'Description': 'The name of the field by which the datasource '
749
'Typecast': GTypecast.name,
752
'Description': 'Selects if the ordering is done in ascending '
753
'(default) or in descending order.',
755
'Typecast': GTypecast.boolean,
759
'Typecast': GTypecast.boolean,
760
'Description': 'Selects wether the ordering is case-sensitive '
764
'ParentTags': ('sortorder',),
768
'BaseClass': GStaticSet,
769
# TODO: This should be replaced by a SingleInstanceInParentObject
770
# instead of SingleInstance (in the whole file)
771
# 'SingleInstance': True,
774
'Typecast': GTypecast.text,
775
'Required': True } },
776
'ParentTags': ('datasource',) },
778
'BaseClass': GStaticSetRow,
779
'ParentTags': ('staticset',) },
781
'BaseClass': GStaticSetField,
784
'Typecast': GTypecast.text,
787
'Typecast': GTypecast.text,
788
'Required': True } },
789
'ParentTags': ('staticsetrow',) },
792
'MixedContent': True,
793
'KeepWhitespace': True,
794
'ParentTags': ('datasource',) },
796
'BaseClass': GConnection,
801
'Typecast': GTypecast.name,
802
'Description': 'TODO' },
805
'Typecast': GTypecast.name,
806
'Description': 'TODO' },
809
'Typecast': GTypecast.text,
810
'Description': 'TODO' },
813
'Typecast': GTypecast.text,
814
'Description': 'TODO' },
817
'Typecast': GTypecast.text,
818
'Description': 'TODO' },
821
'Typecast': GTypecast.text,
822
'Description': 'TODO' } },
824
'Description': 'TODO' },
827
# Add conditional elements
829
GConditions.getXMLelements(
830
{'condition':{'ParentTags':('datasource',) } } ))
832
for alteration in updates.keys():
833
xmlElements[alteration].update(updates[alteration])
835
# Connections will have the same parent as datasources
836
xmlElements['connection']['ParentTags'] = xmlElements['datasource']['ParentTags']
841
# =============================================================================
842
# Classes for implementing sort options
843
# =============================================================================
845
class GCSortOrder (GObjects.GObj):
846
def __init__ (self, parent = None):
847
GObjects.GObj.__init__ (self, parent, 'GCSortOrder')
849
self._inits = [self._build]
852
for item in self.findChildrenOfType ('GCSortField'):
853
self.sorting.append ({'name': item.name,
854
'descending': item.descending,
855
'ignorecase': item.ignorecase})
858
class GCSortField (GObjects.GObj):
859
def __init__ (self, parent = None):
860
GObjects.GObj.__init__ (self, parent, 'GCSortField')
864
# Wrapper for standalone DataSources
865
# (i.e., not in context of a GObj tree)
867
def DataSourceWrapper(connections=None, fields=(), attributes={}, init=True, unicodeMode=False, sql=""):
868
source = _DataSourceWrapper()
873
attributes['type'] = 'sql'
877
source.setConnectionManager(connections)
880
source.buildAndInitObject(**attributes)
882
source.buildObject(**attributes)
885
source.referenceFields(fields)
890
class _DataSourceWrapper(GDataSource):
891
def __init__(self, *args, **parms):
892
GDataSource.__init__(self, *args, **parms)
893
self._datasourceDictionary={}
894
self._toplevelParent = self._type
896
def getIntrospector (self):
897
return self._dataObject._connection.introspector
899
def getSchemaCreator (self):
900
return self._dataObject._connection.getSchemaCreator ()
903
# =============================================================================
904
# Load AppServer specific resources
905
# =============================================================================
907
class AppServerResourceError (errors.AdminError):
910
class InvalidURLError (AppServerResourceError):
911
def __init__ (self, url):
912
msg = u_("The URL '%s' is not a valid application server resource "
914
AppServerResourceError.__init__ (self, msg)
916
class InvalidResourceTypeError (AppServerResourceError):
917
def __init__ (self, resourceType):
918
msg = u_("Resource type '%s' is not supported") % resourceType
919
AppServerResourceError.__init__ (self, msg)
921
class ResourceNotFoundError (AppServerResourceError):
922
def __init__ (self, resType, resName):
923
msg = u_("Resource '%(name)s' of type '%(type)s' not found") \
926
AppServerResourceError.__init__ (self, msg)
928
# -----------------------------------------------------------------------------
929
# Load a resource from appserver and return it as a file-like object
930
# -----------------------------------------------------------------------------
932
def getAppserverResource (url, paramDict, connections):
933
if url [:12].lower () != 'appserver://':
934
raise InvalidURLError, url
937
(appUrl, options) = url [12:].split ('?')
942
parts = appUrl.split ('/')
944
raise InvalidURLError, url
945
(connection, element, elementName) = parts [:3]
947
if not len (connection) or not len (element) or not len (elementName):
948
raise InvalidURLError, url
950
if not element in ['form']:
951
raise InvalidResourceTypeError, element
956
for part in options.split (';'):
957
(item, value) = part.split ('=')
958
elementParams [item] = value
961
if elementParams.has_key ('debug-file'):
962
debugFileName = elementParams ['debug-file']
963
del elementParams ['debug-file']
965
paramDict.update (elementParams)
967
attrs = {'name' : 'dtsClass',
968
'database': connection,
969
'table' : 'gnue_class'}
970
fieldList = ['gnue_id', 'gnue_name', 'gnue_module']
972
dts = DataSourceWrapper (
973
connections = connections,
978
parts = elementName.split ('_')
980
raise ResourceNotFoundError, (element, elementName)
982
(moduleName, className) = parts
983
mc = GConditions.buildConditionFromDict ({'gnue_name': className,
984
'gnue_module.gnue_name': moduleName})
986
rs = dts.createResultSet (mc)
987
if rs.firstRecord ():
988
paramDict ['connection'] = connection
990
res = rs.current.callFunc ("gnue_%s" % element, paramDict)
992
if debugFileName is not None:
993
dfile = open (debugFileName, 'w')
994
dfile.write (res.encode ('utf-8'))
997
return cStringIO.StringIO (res.encode ('utf-8'))
1000
raise ResourceNotFoundError, (element, elementName)