[Zope3-checkins] CVS: Zope3/src/zope/app/services - configure.zcml:1.48 hub.py:1.17
Anthony Baxter
anthony@interlink.com.au
Fri, 11 Jul 2003 01:51:23 -0400
Update of /cvs-repository/Zope3/src/zope/app/services
In directory cvs.zope.org:/tmp/cvs-serv13561/src/zope/app/services
Modified Files:
configure.zcml hub.py
Log Message:
Moved the Registration object from zope.app.index to zope.app.services.hub,
and changed the bootstrap code to add a Registration object if there is not
already one installed. This means that by default the ObjectHub is now
actually doing something, rather than sitting by itself feeling lonely.
When a Registration object is added by bootstrap, it's subscribed by
default.
=== Zope3/src/zope/app/services/configure.zcml 1.47 => 1.48 ===
--- Zope3/src/zope/app/services/configure.zcml:1.47 Wed Jul 2 18:10:47 2003
+++ Zope3/src/zope/app/services/configure.zcml Fri Jul 11 01:50:46 2003
@@ -14,6 +14,18 @@
/>
</content>
+<content class="zope.app.services.hub.Registration">
+ <require
+ permission="zope.ManageServices"
+ interface="zope.app.services.hub.ISubscriptionControl"
+ />
+ <factory
+ id="zope.app.services.hub.Registration"
+ permission="zope.ManageServices"
+ />
+</content>
+
+
<!-- Adapter Service -->
<content class="zope.app.services.adapter.AdapterService">
=== Zope3/src/zope/app/services/hub.py 1.16 => 1.17 ===
--- Zope3/src/zope/app/services/hub.py:1.16 Tue Jul 8 20:57:19 2003
+++ Zope3/src/zope/app/services/hub.py Fri Jul 11 01:50:46 2003
@@ -48,6 +48,16 @@
from zope.app.interfaces.traversing import ITraverser
from zope.app.interfaces.services.service import ISimpleService
from zope.interface import implements
+from zope.app.interfaces.event import ISubscriber
+from zope.app.interfaces.event import IObjectAddedEvent
+from zope.app.interfaces.content.folder import IFolder
+from zope.app.interfaces.traversing import ITraversable
+from zope.app.services.servicenames import HubIds
+
+from zope.app.traversing import traverse, traverseName, getPath, getRoot
+from zope.app.interfaces.services.hub import ObjectHubError
+from zope.app.interfaces.services.hub import ISubscriptionControl
+from persistence import Persistent
class HubEvent:
"""Convenient mix-in for HubEvents"""
@@ -375,3 +385,113 @@
index = randid()
self._v_nextid = index + 1
return index
+
+
+"""A simple-minded registration object.
+
+In order for the ObjectHub to actually serve a purpose, objects
+need to be registered with the object hub. A real site should
+define a policy for when objects are to be registered. This
+particular implementation subscribes to IObjectAddedEvent events
+from the event service, and registers absolutely everything. A
+site that wishes to implement a different subscription policy
+can write their own Registration object (at the moment this seems
+somewhat yagni to us).
+
+It also has a way of registering all pre-existing objects.
+
+This code was originally implemented for the index package, but it's
+very much ObjectHub-specific for now.
+"""
+
+
+class Registration(Persistent):
+
+ implements(ISubscriptionControl, ISubscriber)
+
+ def notify(wrapped_self, event):
+ """An event occured. Perhaps register this object with the hub."""
+ hub = getService(wrapped_self, HubIds)
+ wrapped_self._registerObject(event.location, hub)
+ notify = ContextMethod(notify)
+
+ currentlySubscribed = False # Default subscription state
+
+ def subscribe(wrapped_self):
+ if wrapped_self.currentlySubscribed:
+ raise RuntimeError, "already subscribed; please unsubscribe first"
+ # we subscribe to the HubIds service so that we're
+ # guaranteed to get exactly the events *that* service receives.
+ events = getService(wrapped_self, EventSubscription)
+ events.subscribe(wrapped_self, IObjectAddedEvent)
+ wrapped_self.currentlySubscribed = True
+ subscribe = ContextMethod(subscribe)
+
+ def unsubscribe(wrapped_self):
+ if not wrapped_self.currentlySubscribed:
+ raise RuntimeError, "not subscribed; please subscribe first"
+ events = getService(wrapped_self, EventSubscription)
+ events.unsubscribe(wrapped_self, IObjectAddedEvent)
+ wrapped_self.currentlySubscribed = False
+ unsubscribe = ContextMethod(unsubscribe)
+
+ def isSubscribed(self):
+ return self.currentlySubscribed
+
+ def registerExisting(wrapped_self):
+ object = findContentObject(wrapped_self)
+ hub = getService(wrapped_self, HubIds)
+ wrapped_self._registerTree(object, hub)
+ registerExisting = ContextMethod(registerExisting)
+
+ def _registerTree(wrapped_self, object, hub):
+ wrapped_self._registerObject(object, hub)
+ # XXX Policy decision: only traverse into folders
+ if not IFolder.isImplementedBy(object):
+ return
+ # Register subobjects
+ names = object.keys()
+ traversable = getAdapter(object, ITraversable)
+ for name in names:
+ sub_object = traverseName(object, name, traversable=traversable)
+ wrapped_self._registerTree(sub_object, hub)
+ _registerTree = ContextMethod(_registerTree)
+
+ def _registerObject(wrapped_self, location, hub):
+ # XXX Policy decision: register absolutely everything
+ try:
+ hub.register(location)
+ except ObjectHubError:
+ # Already registered
+ pass
+ _registerObject = ContextMethod(_registerObject)
+
+def findContentObject(context):
+ # We want to find the (content) Folder in whose service manager we
+ # live. There are man y way to do this. Perhaps the simplest is
+ # looking for '++etc++site' in the location. We could also
+ # walk up the path looking for something that implements IFolder;
+ # the service manager and packages don't implement this. Or
+ # (perhaps better, because a service manager might be attached to
+ # a non-folder container) assume we're in service space, and walk
+ # up until we find a service manager, and then go up one more
+ # step. Walking up the path could be done by stripping components
+ # from the end of the path one at a time and doing a lookup each
+ # time, or more directly by traversing the context. Traversing
+ # the context can be done by getting the context and following the
+ # chain back; there's a convenience class, ContainmentIterator to
+ # do that. Use the version of ContainmentIterator from
+ # zope.proxy, which is aware of the complications caused by
+ # security proxies.
+
+ # For now, we pick the first approach.
+ location = getPath(context)
+
+ index = location.find('/++etc++site/')
+ if index != -1:
+ location = location[:index]
+ else:
+ raise ValueError, "can't find '++etc++site' in path"
+ root = getRoot(context)
+ return traverse(root, location)
+