[Zope-Checkins] CVS: Zope/lib/python/Products/ZODBMountPoint -
Mount.py:1.4
Jeremy Hylton
jeremy at zope.com
Thu Apr 15 12:41:36 EDT 2004
Update of /cvs-repository/Zope/lib/python/Products/ZODBMountPoint
In directory cvs.zope.org:/tmp/cvs-serv13728/src/Products/ZODBMountPoint
Modified Files:
Mount.py
Log Message:
Change the implementation of mounted connections.
Avoid the monkey patch by creating a subclass of Connection and
installing it as the klass attribute of ZODB's DB class. I think this
works correctly in conjunction with Zope configuration (the tests
pass) -- but I'm not qualified to say for sure.
Rework the logic of MountConnection as part of the restructuring. The
_root_connection attribute always points to the root, never to None;
so the root object points to itself. This change necessitated a
chance in DBTab.ClassFactories. Add some comments there to explain
what's going on.
XXX The close() method on MountConnection is re-implementing two
methods in modules. If those implementations change, it would need to
change, too. We ought to find a better way to integrate it.
XXX Perhaps the mounted connection stuff should be folded into ZODB
proper.
=== Zope/lib/python/Products/ZODBMountPoint/Mount.py 1.3 => 1.4 ===
--- Zope/lib/python/Products/ZODBMountPoint/Mount.py:1.3 Tue Mar 2 10:36:28 2004
+++ Zope/lib/python/Products/ZODBMountPoint/Mount.py Thu Apr 15 12:41:05 2004
@@ -15,18 +15,27 @@
$Id$"""
-import time, sys
+import sys
+try:
+ from cStringIO import StringIO
+except:
+ from StringIO import StringIO
+import traceback
import Persistence, Acquisition
from Acquisition import aq_base
from ZODB.POSException import MountedStorageError
from zLOG import LOG, ERROR, INFO, WARNING
+from ZODB.DB import DB
+from ZODB.Connection import Connection
+
class MountPoint(Persistence.Persistent, Acquisition.Implicit):
- '''The base class for a Zope object which, when traversed,
- accesses a different database.
- '''
+ """An object that accesses a different database when traversed.
+
+ This class is intended to be used as a base class.
+ """
# Default values for non-persistent variables.
_v_data = None # An object in an open connection
@@ -36,23 +45,19 @@
self.id = id
def _getDB(self):
- """Hook for getting the DB object for this mount point.
- """
+ """Hook for getting the DB object for this mount point."""
raise NotImplementedError
def _getDBName(self):
- """Hook for getting the name of the database for this mount point.
- """
+ """Hook for getting the name of the database for this mount point."""
raise NotImplementedError
def _getRootDBName(self):
- """Hook for getting the name of the root database.
- """
+ """Hook for getting the name of the root database."""
raise NotImplementedError
def _traverseToMountedRoot(self, root, mount_parent):
- """Hook for getting the object to be mounted.
- """
+ """Hook for getting the object to be mounted."""
raise NotImplementedError
def __repr__(self):
@@ -115,20 +120,13 @@
def _test(self, parent):
- '''Tests the database connection.
- '''
+ """Tests the database connection."""
self._getOrOpenObject(parent)
return 1
def _logConnectException(self):
- '''Records info about the exception that just occurred.
- '''
- try:
- from cStringIO import StringIO
- except:
- from StringIO import StringIO
- import traceback
+ """Records info about the exception that just occurred."""
exc = sys.exc_info()
LOG('ZODB', ERROR, 'Failed to mount database. %s (%s)' % exc[:2],
error=exc)
@@ -137,85 +135,73 @@
self._v_connect_error = (exc[0], exc[1], f.getvalue())
exc = None
+class MountConnection(Connection):
+ """Subclass of Connection that supports mounts."""
+ # XXX perhaps the code in this subclass should be folded into
+ # ZODB proper.
-class ConnectionPatches:
- # Changes to Connection.py that might fold into ZODB
-
- _root_connection = None
- _mounted_connections = None
-
+ def __init__(self, **kwargs):
+ Connection.__init__(self, **kwargs)
+ # The _root_connection of the actual root points to self. All
+ # other connections will have _root_connection over-ridden in
+ # _addMountedConnection().
+ self._root_connection = self
+ self._mounted_connections = {}
+
def _getRootConnection(self):
- root_conn = self._root_connection
- if root_conn is None:
- return self
- else:
- return root_conn
+ return self._root_connection
def _getMountedConnection(self, name):
- conns = self._getRootConnection()._mounted_connections
- if conns is None:
- return None
- else:
- return conns.get(name)
+ return self._root_connection._mounted_connections.get(name)
def _addMountedConnection(self, name, conn):
- if conn._root_connection is not None:
- raise ValueError, 'Connection %s is already mounted' % repr(conn)
- root_conn = self._getRootConnection()
- conns = root_conn._mounted_connections
- if conns is None:
- conns = {}
- root_conn._mounted_connections = conns
- if conns.has_key(name):
- raise KeyError, 'A connection named %s already exists' % repr(name)
- conn._root_connection = root_conn
+ if conn._root_connection is not conn:
+ raise ValueError("Connection %s is already mounted" % repr(conn))
+ conns = self._root_connection._mounted_connections
+ if name in conns:
+ raise KeyError("A connection named %s already exists" % repr(name))
+ conn._root_connection = self._root_connection
conns[name] = conn
- def _setDB(self, odb):
- self._real_setDB(odb)
- conns = self._mounted_connections
- if conns:
- for conn in conns.values():
- conn._setDB(conn._db)
+ def _setDB(self, odb, **kwargs):
+ Connection._setDB(self, odb, **kwargs)
+ for conn in self._mounted_connections.values():
+ conn._setDB(odb, **kwargs)
def close(self):
- if self._root_connection is not None:
+ if self._root_connection is not self:
raise RuntimeError("Should not close mounted connections directly")
- conns = self._mounted_connections
- if conns:
- for conn in conns.values():
- # Notify the activity monitor
- db = conn.db()
- f = getattr(db, 'getActivityMonitor', None)
- if f is not None:
- am = f()
- if am is not None:
- am.closedConnection(conn)
- conn.cacheGC() # This is a good time to do some GC
- # XXX maybe we ought to call the close callbacks.
- conn._storage = conn._tmp = conn.new_oid = conn._opened = None
- conn._debug_info = ()
- # The mounted connection keeps a reference to
- # its database, but nothing else.
- # Note that mounted connections can not operate
- # independently, so don't use _closeConnection() to
- # return them to the pool. Only the root connection
- # should be returned.
+
+ # The code here duplicates much of the code in
+ # DB._closeConnection and Connection.close. A mounted
+ # connection can't operate independently, so we don't call
+ # DB._closeConnection(), which would return it to the
+ # connection pool; only the root connection should be
+ # returned.
+
+ for conn in self._mounted_connections.values():
+ # DB._closeConnection calls the activity monitor
+ am = conn._db.getActivityMonitor()
+ am.closedConnection(conn)
+ # Connection.close does GC
+ conn._cache.incrgc()
+ conn._storage = conn._tmp = conn.new_oid = conn._opened = None
+ conn._debug_info = ()
+ # The mounted connection keeps a reference to
+ # its database, but nothing else.
+
# Close this connection only after the mounted connections
# have been closed. Otherwise, this connection gets returned
# to the pool too early and another thread might use this
# connection before the mounted connections have all been
# closed.
- self._real_close()
-
-if 1:
- # patch Connection.py.
- from ZODB.Connection import Connection
- Connection._real_setDB = Connection._setDB
- Connection._real_close = Connection.close
-
- for k, v in ConnectionPatches.__dict__.items():
- if not k.startswith('__'):
- setattr(Connection, k, v)
+ Connection.close(self)
+# Replace the default database Connection
+def install():
+ DB.klass = MountConnection
+
+# XXX This shouldn't be done as a side-effect of import, but it's not
+# clear where in the Zope initialization code it should be done.
+install()
More information about the Zope-Checkins
mailing list