[Zope-Checkins] SVN: Zope/branches/2.10/ Backported r91724 from the trunk:
Tres Seaver
tseaver at palladion.com
Thu Jun 11 13:57:50 EDT 2009
Log message for revision 100882:
Backported r91724 from the trunk:
- Moved exception MountedStorageError from ZODB.POSExceptions
to Products.TemporaryFolder.mount (now its only client).
- LP #253362: Moved Zope2-specific module, ZODB/Mount.py, to
Products/TemporaryFolder/mount.py (its only client is
Products/TemporaryFolder/TemporaryFolder.py).
- Removed spurious import-time dependencies from
Products/ZODBMountPoint/MountedObject.py.
Changed:
U Zope/branches/2.10/doc/CHANGES.txt
U Zope/branches/2.10/lib/python/Products/TemporaryFolder/TemporaryFolder.py
A Zope/branches/2.10/lib/python/Products/TemporaryFolder/mount.py
U Zope/branches/2.10/lib/python/Products/ZODBMountPoint/MountedObject.py
-=-
Modified: Zope/branches/2.10/doc/CHANGES.txt
===================================================================
--- Zope/branches/2.10/doc/CHANGES.txt 2009-06-11 17:53:20 UTC (rev 100881)
+++ Zope/branches/2.10/doc/CHANGES.txt 2009-06-11 17:57:50 UTC (rev 100882)
@@ -4,6 +4,20 @@
Change information for previous versions of Zope can be found in the
file HISTORY.txt.
+ After Zope 2.10.8
+
+ Restructuring
+
+ - Moved exception MountedStorageError from ZODB.POSExceptions
+ to Products.TemporaryFolder.mount (now its only client).
+
+ - LP #253362: Moved Zope2-specific module, ZODB/Mount.py, to
+ Products/TemporaryFolder/mount.py (its only client is
+ Products/TemporaryFolder/TemporaryFolder.py).
+
+ - Removed spurious import-time dependencies from
+ Products/ZODBMountPoint/MountedObject.py.
+
Zope 2.10.8 (2009/05/04)
Features added
Modified: Zope/branches/2.10/lib/python/Products/TemporaryFolder/TemporaryFolder.py
===================================================================
--- Zope/branches/2.10/lib/python/Products/TemporaryFolder/TemporaryFolder.py 2009-06-11 17:53:20 UTC (rev 100881)
+++ Zope/branches/2.10/lib/python/Products/TemporaryFolder/TemporaryFolder.py 2009-06-11 17:57:50 UTC (rev 100882)
@@ -10,8 +10,7 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
-"""
-Mounted database support
+""" Mounted database support
A MountedTemporaryFolder is an object that is a mount point. It mounts a
TemporaryStorage-backed database and masquerades as its root object.
@@ -20,24 +19,21 @@
lives in another ZODB.
To understand this fully, you'll need to read the source of
-ZODB.Mount.MountPoint.
+Products.TemporaryFolder.mount.MountPoint.
$Id$
"""
__version__='$Revision: 1.12 $'[11:-2]
-import os, os.path
-
-import Globals
-from Globals import HTMLFile
-from ZODB.Mount import MountPoint
+from App.special_dtml import DTMLFile
+from App.special_dtml import HTMLFile
from OFS.Folder import Folder
from OFS.SimpleItem import Item
-
-from ZODB.DB import DB
from tempstorage.TemporaryStorage import TemporaryStorage
-from LowConflictConnection import LowConflictConnection
+from ZODB.DB import DB
+from Products.TemporaryFolder.mount import MountPoint
+
ADD_TEMPORARY_FOLDER_PERM="Add Temporary Folder"
@@ -48,14 +44,15 @@
if REQUEST is not None:
return self.manage_main(self, REQUEST, update_menu=1)
-
constructTemporaryFolderForm=HTMLFile('dtml/addTemporaryFolder', globals())
+
class SimpleTemporaryContainer(Folder):
# dbtab-style container class
meta_type = 'Temporary Folder'
icon = 'misc_/TemporaryFolder/tempfolder.gif'
+
class MountedTemporaryFolder(MountPoint, Item):
"""
A mounted RAM database with a basic interface for displaying the
@@ -73,7 +70,7 @@
self.title = title
MountPoint.__init__(self, path='/') # Eep
- manage_traceback = Globals.DTMLFile('dtml/mountfail', globals())
+ manage_traceback = DTMLFile('dtml/mountfail', globals())
def _createDB(self, db=None): # huh? db=db was original
""" Create a mounted RAM database """
Copied: Zope/branches/2.10/lib/python/Products/TemporaryFolder/mount.py (from rev 91724, Zope/trunk/lib/python/Products/TemporaryFolder/mount.py)
===================================================================
--- Zope/branches/2.10/lib/python/Products/TemporaryFolder/mount.py (rev 0)
+++ Zope/branches/2.10/lib/python/Products/TemporaryFolder/mount.py 2009-06-11 17:57:50 UTC (rev 100882)
@@ -0,0 +1,294 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""Mounted database support
+
+$Id: Mount.py 82361 2007-12-19 18:44:17Z jim $"""
+
+import time
+import thread
+import logging
+import persistent
+
+from Acquisition import Implicit
+from Acquisition import ImplicitAcquisitionWrapper
+from Acquisition import aq_base
+from ZODB.POSException import StorageError
+
+class MountedStorageError(StorageError):
+ """Unable to access mounted storage."""
+
+logger = logging.getLogger('ZODB.Mount')
+
+# dbs is a holder for all DB objects, needed to overcome
+# threading issues. It maps connection params to a DB object
+# and a mapping of mount points.
+dbs = {}
+
+# dblock is locked every time dbs is accessed.
+dblock = thread.allocate_lock()
+
+
+def parentClassFactory(jar, module, name):
+ # Use the class factory from the parent database.
+ parent_conn = getattr(jar, '_mount_parent_jar', None)
+ parent_db = getattr(parent_conn, '_db', None)
+ if parent_db is None:
+ _globals = {}
+ _silly = ('__doc__',)
+ return getattr(__import__(
+ module, _globals, _globals, _silly), name)
+ else:
+ return parent_db.classFactory(parent_conn, module, name)
+
+
+class MountPoint(persistent.Persistent, Implicit):
+ '''The base class for a Zope object which, when traversed,
+ accesses a different database.
+ '''
+
+ # Default values for non-persistent variables.
+ _v_db = None
+ _v_data = None
+ _v_connect_error = None
+
+ def __init__(self, path, params=None, classDefsFromRoot=1):
+ '''
+ @arg path The path within the mounted database from which
+ to derive the root.
+
+ @arg params The parameters used to connect to the database.
+ No particular format required.
+ If there is more than one mount point referring to a
+ database, MountPoint will detect the matching params
+ and use the existing database. Include the class name of
+ the storage. For example,
+ ZEO params might be "ZODB.ZEOClient localhost 1081".
+
+ @arg classDefsFromRoot If true (the default), MountPoint will
+ try to get ZClass definitions from the root database rather
+ than the mounted database.
+ '''
+ # The only reason we need a __mountpoint_id is to
+ # be sure we don't close a database prematurely when
+ # it is mounted more than once and one of the points
+ # is unmounted.
+ self.__mountpoint_id = '%s_%f' % (id(self), time.time())
+ if params is None:
+ # We still need something to use as a hash in
+ # the "dbs" dictionary.
+ params = self.__mountpoint_id
+ self._params = repr(params)
+ self._path = path
+ self._classDefsFromRoot = classDefsFromRoot
+
+ def _createDB(self):
+ '''Gets the database object, usually by creating a Storage object
+ and returning ZODB.DB(storage).
+ '''
+ raise NotImplementedError
+
+ def _getDB(self):
+ '''Creates or opens a DB object.
+ '''
+ newMount = 0
+ dblock.acquire()
+ try:
+ params = self._params
+ dbInfo = dbs.get(params, None)
+ if dbInfo is None:
+ logger.info('Opening database for mounting: %s', params)
+ db = self._createDB()
+ newMount = 1
+ dbs[params] = (db, {self.__mountpoint_id:1})
+
+ if getattr(self, '_classDefsFromRoot', 1):
+ db.classFactory = parentClassFactory
+ else:
+ db, mounts = dbInfo
+ # Be sure this object is in the list of mount points.
+ if not mounts.has_key(self.__mountpoint_id):
+ newMount = 1
+ mounts[self.__mountpoint_id] = 1
+ self._v_db = db
+ finally:
+ dblock.release()
+ return db, newMount
+
+ def _getMountpointId(self):
+ return self.__mountpoint_id
+
+ def _getMountParams(self):
+ return self._params
+
+ def __repr__(self):
+ return "%s(%s, %s)" % (self.__class__.__name__, repr(self._path),
+ self._params)
+
+ def _openMountableConnection(self, parent):
+ # Opens a new connection to the database.
+ db = self._v_db
+ if db is None:
+ self._v_close_db = 0
+ db, newMount = self._getDB()
+ else:
+ newMount = 0
+ jar = getattr(self, '_p_jar', None)
+ if jar is None:
+ # Get _p_jar from parent.
+ self._p_jar = jar = parent._p_jar
+ conn = db.open(version=jar.getVersion())
+
+ # Add an attribute to the connection which
+ # makes it possible for us to find the primary
+ # database connection. See ClassFactoryForMount().
+ conn._mount_parent_jar = jar
+
+ mcc = MountedConnectionCloser(self, conn)
+ jar.onCloseCallback(mcc)
+ return conn, newMount, mcc
+
+ def _getObjectFromConnection(self, conn):
+ obj = self._getMountRoot(conn.root())
+ data = aq_base(obj)
+ # Store the data object in a tuple to hide from acquisition.
+ self._v_data = (data,)
+ return data
+
+ def _getOrOpenObject(self, parent):
+ t = self._v_data
+ if t is None:
+ self._v_connect_error = None
+ conn = None
+ newMount = 0
+ mcc = None
+ try:
+ conn, newMount, mcc = self._openMountableConnection(parent)
+ data = self._getObjectFromConnection(conn)
+ except:
+ # Possibly broken database.
+ if mcc is not None:
+ # Note that the next line may be a little rash--
+ # if, for example, a working database throws an
+ # exception rather than wait for a new connection,
+ # this will likely cause the database to be closed
+ # prematurely. Perhaps DB.py needs a
+ # countActiveConnections() method.
+ mcc.setCloseDb()
+ logger.warning('Failed to mount database. %s (%s)',
+ exc_info=True)
+ raise
+ if newMount:
+ try: id = data.getId()
+ except: id = '???' # data has no getId() method. Bad.
+ p = '/'.join(parent.getPhysicalPath() + (id,))
+ logger.info('Mounted database %s at %s',
+ self._getMountParams(), p)
+ else:
+ data = t[0]
+
+ return data.__of__(parent)
+
+ def __of__(self, parent):
+ # Accesses the database, returning an acquisition
+ # wrapper around the connected object rather than around self.
+ try:
+ return self._getOrOpenObject(parent)
+ except:
+ return ImplicitAcquisitionWrapper(self, parent)
+
+ def _test(self, parent):
+ '''Tests the database connection.
+ '''
+ self._getOrOpenObject(parent)
+ return 1
+
+ def _getMountRoot(self, root):
+ '''Gets the object to be mounted.
+ Can be overridden to provide different behavior.
+ '''
+ try:
+ app = root['Application']
+ except:
+ raise MountedStorageError(
+ "No 'Application' object exists in the mountable database.")
+ try:
+ return app.unrestrictedTraverse(self._path)
+ except:
+ raise MountedStorageError(
+ "The path '%s' was not found in the mountable database."
+ % self._path)
+
+
+class MountedConnectionCloser:
+ '''Closes the connection used by the mounted database
+ while performing other cleanup.
+ '''
+ close_db = 0
+
+ def __init__(self, mountpoint, conn):
+ # conn is the child connection.
+ self.mp = mountpoint
+ self.conn = conn
+
+ def setCloseDb(self):
+ self.close_db = 1
+
+ def __call__(self):
+ # The onCloseCallback handler.
+ # Closes a single connection to the database
+ # and possibly the database itself.
+ conn = self.conn
+ close_db = 0
+ if conn is not None:
+ mp = self.mp
+ # Remove potential circular references.
+ self.conn = None
+ self.mp = None
+ # Detect whether we should close the database.
+ close_db = self.close_db
+ t = mp.__dict__.get('_v_data', None)
+ if t is not None:
+ del mp.__dict__['_v_data']
+ data = t[0]
+ if not close_db and data.__dict__.get(
+ '_v__object_deleted__', 0):
+ # This mount point has been deleted.
+ del data.__dict__['_v__object_deleted__']
+ close_db = 1
+ # Close the child connection.
+ try:
+ del conn._mount_parent_jar
+ except:
+ pass
+ conn.close()
+
+ if close_db:
+ # Stop using this database. Close it if no other
+ # MountPoint is using it.
+ dblock.acquire()
+ try:
+ params = mp._getMountParams()
+ mp._v_db = None
+ if dbs.has_key(params):
+ dbInfo = dbs[params]
+ db, mounts = dbInfo
+ try: del mounts[mp._getMountpointId()]
+ except: pass
+ if len(mounts) < 1:
+ # No more mount points are using this database.
+ del dbs[params]
+ db.close()
+ logger.info('Closed database: %s', params)
+ finally:
+ dblock.release()
Modified: Zope/branches/2.10/lib/python/Products/ZODBMountPoint/MountedObject.py
===================================================================
--- Zope/branches/2.10/lib/python/Products/ZODBMountPoint/MountedObject.py 2009-06-11 17:53:20 UTC (rev 100881)
+++ Zope/branches/2.10/lib/python/Products/ZODBMountPoint/MountedObject.py 2009-06-11 17:57:50 UTC (rev 100882)
@@ -24,14 +24,15 @@
import transaction
-import Globals
-import Acquisition
-from Acquisition import aq_base, aq_inner, aq_parent
+from App.class_init import default__class_init__ as InitializeClass
+from Acquisition import ImplicitAcquisitionWrapper
+from Acquisition import aq_base
+from Acquisition import aq_inner
+from Acquisition import aq_parent
from AccessControl.ZopeGuards import guarded_getattr
from OFS.SimpleItem import SimpleItem
from OFS.Folder import Folder
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-from ZODB.POSException import MountedStorageError, ConnectionStateError
LOG = getLogger('Zope.ZODBMountPoint')
@@ -215,11 +216,6 @@
def _logConnectException(self):
'''Records info about the exception that just occurred.
'''
- try:
- from cStringIO import StringIO
- except:
- from StringIO import StringIO
- import traceback
exc = sys.exc_info()
LOG.error('Failed to mount database. %s (%s)' % exc[:2], exc_info=exc)
f=StringIO()
@@ -234,7 +230,7 @@
try:
return self._getOrOpenObject(parent)
except:
- return Acquisition.ImplicitAcquisitionWrapper(self, parent)
+ return ImplicitAcquisitionWrapper(self, parent)
def _test(self, parent):
@@ -279,7 +275,7 @@
return "%s(id=%s)" % (self.__class__.__name__, repr(self.id))
-Globals.InitializeClass(MountedObject)
+InitializeClass(MountedObject)
def getMountPoint(ob):
More information about the Zope-Checkins
mailing list