Commit e6558614 authored by Tres Seaver's avatar Tres Seaver

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.
parents 69140e0c f82c774f
...@@ -4,6 +4,20 @@ Zope Changes ...@@ -4,6 +4,20 @@ Zope Changes
Change information for previous versions of Zope can be found in the Change information for previous versions of Zope can be found in the
file HISTORY.txt. file HISTORY.txt.
After Zope 2.11.3
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.11.3 (2009/05/04) Zope 2.11.3 (2009/05/04)
Features added Features added
......
...@@ -10,8 +10,7 @@ ...@@ -10,8 +10,7 @@
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE
# #
############################################################################## ##############################################################################
""" """ Mounted database support
Mounted database support
A MountedTemporaryFolder is an object that is a mount point. It mounts a A MountedTemporaryFolder is an object that is a mount point. It mounts a
TemporaryStorage-backed database and masquerades as its root object. TemporaryStorage-backed database and masquerades as its root object.
...@@ -20,23 +19,20 @@ point object is called, and that returns a Folder object that actually ...@@ -20,23 +19,20 @@ point object is called, and that returns a Folder object that actually
lives in another ZODB. lives in another ZODB.
To understand this fully, you'll need to read the source of To understand this fully, you'll need to read the source of
ZODB.Mount.MountPoint. Products.TemporaryFolder.mount.MountPoint.
$Id$ $Id$
""" """
__version__='$Revision: 1.12 $'[11:-2] __version__='$Revision: 1.12 $'[11:-2]
import os, os.path from App.special_dtml import DTMLFile
from App.special_dtml import HTMLFile
import Globals
from Globals import HTMLFile
from ZODB.Mount import MountPoint
from OFS.Folder import Folder from OFS.Folder import Folder
from OFS.SimpleItem import Item from OFS.SimpleItem import Item
from ZODB.DB import DB
from tempstorage.TemporaryStorage import TemporaryStorage 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" ADD_TEMPORARY_FOLDER_PERM="Add Temporary Folder"
...@@ -48,14 +44,15 @@ def constructTemporaryFolder(self, id, title=None, REQUEST=None): ...@@ -48,14 +44,15 @@ def constructTemporaryFolder(self, id, title=None, REQUEST=None):
if REQUEST is not None: if REQUEST is not None:
return self.manage_main(self, REQUEST, update_menu=1) return self.manage_main(self, REQUEST, update_menu=1)
constructTemporaryFolderForm=HTMLFile('dtml/addTemporaryFolder', globals()) constructTemporaryFolderForm=HTMLFile('dtml/addTemporaryFolder', globals())
class SimpleTemporaryContainer(Folder): class SimpleTemporaryContainer(Folder):
# dbtab-style container class # dbtab-style container class
meta_type = 'Temporary Folder' meta_type = 'Temporary Folder'
icon = 'misc_/TemporaryFolder/tempfolder.gif' icon = 'misc_/TemporaryFolder/tempfolder.gif'
class MountedTemporaryFolder(MountPoint, Item): class MountedTemporaryFolder(MountPoint, Item):
""" """
A mounted RAM database with a basic interface for displaying the A mounted RAM database with a basic interface for displaying the
...@@ -73,7 +70,7 @@ class MountedTemporaryFolder(MountPoint, Item): ...@@ -73,7 +70,7 @@ class MountedTemporaryFolder(MountPoint, Item):
self.title = title self.title = title
MountPoint.__init__(self, path='/') # Eep 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 def _createDB(self, db=None): # huh? db=db was original
""" Create a mounted RAM database """ """ Create a mounted RAM database """
......
##############################################################################
#
# 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()
...@@ -24,14 +24,15 @@ from logging import getLogger ...@@ -24,14 +24,15 @@ from logging import getLogger
import transaction import transaction
import Globals from App.class_init import default__class_init__ as InitializeClass
import Acquisition from Acquisition import ImplicitAcquisitionWrapper
from Acquisition import aq_base, aq_inner, aq_parent from Acquisition import aq_base
from Acquisition import aq_inner
from Acquisition import aq_parent
from AccessControl.ZopeGuards import guarded_getattr from AccessControl.ZopeGuards import guarded_getattr
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from OFS.Folder import Folder from OFS.Folder import Folder
from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from ZODB.POSException import MountedStorageError, ConnectionStateError
LOG = getLogger('Zope.ZODBMountPoint') LOG = getLogger('Zope.ZODBMountPoint')
...@@ -215,11 +216,6 @@ class MountedObject(SimpleItem): ...@@ -215,11 +216,6 @@ class MountedObject(SimpleItem):
def _logConnectException(self): def _logConnectException(self):
'''Records info about the exception that just occurred. '''Records info about the exception that just occurred.
''' '''
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
import traceback
exc = sys.exc_info() exc = sys.exc_info()
LOG.error('Failed to mount database. %s (%s)' % exc[:2], exc_info=exc) LOG.error('Failed to mount database. %s (%s)' % exc[:2], exc_info=exc)
f=StringIO() f=StringIO()
...@@ -234,7 +230,7 @@ class MountedObject(SimpleItem): ...@@ -234,7 +230,7 @@ class MountedObject(SimpleItem):
try: try:
return self._getOrOpenObject(parent) return self._getOrOpenObject(parent)
except: except:
return Acquisition.ImplicitAcquisitionWrapper(self, parent) return ImplicitAcquisitionWrapper(self, parent)
def _test(self, parent): def _test(self, parent):
...@@ -279,7 +275,7 @@ class MountedObject(SimpleItem): ...@@ -279,7 +275,7 @@ class MountedObject(SimpleItem):
return "%s(id=%s)" % (self.__class__.__name__, repr(self.id)) return "%s(id=%s)" % (self.__class__.__name__, repr(self.id))
Globals.InitializeClass(MountedObject) InitializeClass(MountedObject)
def getMountPoint(ob): def getMountPoint(ob):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment