Annotation of ExtFile/TM.py, revision 1.1
1.1 ! dwinter 1: """
! 2: TMRegistry and ProxyTM
! 3:
! 4: Use case
! 5: --------
! 6:
! 7: Persistent objects (ExtFiles) need to participate in ZODB transactions.
! 8: ExtFiles perform all operations using temporary files which are saved on
! 9: commit or deleted on abort.
! 10:
! 11: Constraints
! 12: -----------
! 13:
! 14: - TransactionManagers (TM) must not be persistent themselves, i.e. must
! 15: not have a _p_jar attribute.
! 16:
! 17: - We have to make sure the ExtFile's _abort is called *before* the ZODB
! 18: destroys the attributes of the persistent object.
! 19:
! 20: Solution
! 21: --------
! 22:
! 23: - ProxyTM is a subclass of TM.TM that keeps a (hard) reference to the
! 24: (wrapped) persistent object it manages. Calls to _begin, _finish, and
! 25: _abort are forwarded to the target object.
! 26:
! 27: - TMRegistry is a module-level container for ProxyTMs. It creates and
! 28: holds ProxyTMs keyed by (target_id, thread_id).
! 29:
! 30: - ExtFiles implement _finish and _abort and register with the machinery
! 31: by calling TM.register(self).
! 32:
! 33: - On commit (or abort) the ProxyTM notifies its target object and removes
! 34: itself from the registry.
! 35:
! 36: Hacks
! 37: -----
! 38:
! 39: - We manipulate the transaction's _resources attribute directly. This is
! 40: to guarantee the ProxyTM is processed before other resources. There may
! 41: be a way to achieve this using official APIs only, but I can't seem to
! 42: find one.
! 43:
! 44: """
! 45:
! 46: from Shared.DC.ZRDB.TM import TM
! 47: from Acquisition import aq_base
! 48: from thread import get_ident
! 49: from Products.ExtFile import transaction
! 50:
! 51:
! 52: class TMRegistry:
! 53:
! 54: def __init__(self):
! 55: self._tms = {}
! 56:
! 57: def getid(self, target):
! 58: return (id(aq_base(target)), get_ident())
! 59:
! 60: def register(self, target):
! 61: if not self.contains(target):
! 62: id = self.getid(target)
! 63: tm = ProxyTM(target)
! 64: tm._register()
! 65: self._tms[id] = tm
! 66: return 1
! 67: return 0
! 68:
! 69: def remove(self, target):
! 70: if self.contains(target):
! 71: id = self.getid(target)
! 72: del self._tms[id]
! 73: return 1
! 74: return 0
! 75:
! 76: def contains(self, target):
! 77: id = self.getid(target)
! 78: return self._tms.has_key(id)
! 79:
! 80: def get(self, target):
! 81: id = self.getid(target)
! 82: return self._tms.get(id)
! 83:
! 84: def __len__(self):
! 85: return len(self._tms)
! 86:
! 87: def count(self):
! 88: return len(self)
! 89:
! 90:
! 91: class ProxyTM(TM):
! 92:
! 93: def __init__(self, target):
! 94: self._target = target
! 95:
! 96: def _register(self):
! 97: TM._register(self)
! 98: # XXX Make sure we are called before the
! 99: # persistent ExtFile object is destroyed.
! 100: t = transaction.get()
! 101: if hasattr(t, '_resources'):
! 102: r = t._resources.pop()
! 103: t._resources.insert(0, r)
! 104:
! 105: def _begin(self):
! 106: return self._target._begin()
! 107:
! 108: def _finish(self):
! 109: if registry.remove(self._target):
! 110: return self._target._finish()
! 111:
! 112: def _abort(self):
! 113: if registry.remove(self._target):
! 114: return self._target._abort()
! 115:
! 116:
! 117: registry = TMRegistry()
! 118: register = registry.register
! 119: remove = registry.remove
! 120: contains = registry.contains
! 121: count = registry.count
! 122:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>