[CalendarServer-changes] [6737] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Fri Jan 14 11:23:46 PST 2011
Revision: 6737
http://trac.macosforge.org/projects/calendarserver/changeset/6737
Author: cdaboo at apple.com
Date: 2011-01-14 11:23:39 -0800 (Fri, 14 Jan 2011)
Log Message:
-----------
Put the PROPFIND cache back in and use a config EnableXXX option to control it.
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/provision/root.py
CalendarServer/trunk/calendarserver/provision/test/test_root.py
CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist
CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist
CalendarServer/trunk/calendarserver/tools/util.py
CalendarServer/trunk/conf/caldavd-test.plist
CalendarServer/trunk/conf/resources/caldavd-resources.plist
CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
CalendarServer/trunk/twistedcaldav/directory/principal.py
CalendarServer/trunk/twistedcaldav/directory/test/resources/caldavd.plist
CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py
CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py
CalendarServer/trunk/twistedcaldav/extensions.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/twistedcaldav/storebridge.py
CalendarServer/trunk/twistedcaldav/test/test_link.py
CalendarServer/trunk/twistedcaldav/test/test_resource.py
CalendarServer/trunk/txdav/caldav/datastore/file.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/carddav/datastore/file.py
CalendarServer/trunk/txdav/carddav/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
Added Paths:
-----------
CalendarServer/trunk/twistedcaldav/cache.py
CalendarServer/trunk/twistedcaldav/test/test_cache.py
Property Changed:
----------------
CalendarServer/trunk/txdav/caldav/datastore/index_file.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
CalendarServer/trunk/txdav/carddav/datastore/index_file.py
CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py
Modified: CalendarServer/trunk/calendarserver/provision/root.py
===================================================================
--- CalendarServer/trunk/calendarserver/provision/root.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/calendarserver/provision/root.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -28,11 +28,16 @@
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.web.xmlrpc import Proxy
+from twistedcaldav.cache import _CachedResponseResource
+from twistedcaldav.cache import MemcacheResponseCache, MemcacheChangeNotifier
+from twistedcaldav.cache import DisabledCache
from twistedcaldav.config import config
from twistedcaldav.extensions import DAVFile, CachingPropertyStore
from twistedcaldav.extensions import DirectoryPrincipalPropertySearchMixIn
from twistedcaldav.extensions import ReadOnlyResourceMixIn
from twistedcaldav.resource import CalDAVComplianceMixIn
+from twistedcaldav.resource import CalendarHomeResource, AddressBookHomeResource
+from twistedcaldav.directory.principal import DirectoryPrincipalResource
log = Logger()
@@ -67,6 +72,15 @@
self.contentFilters = []
+ if config.EnableResponseCache and config.Memcached.Pools.Default.ClientEnabled:
+ self.responseCache = MemcacheResponseCache(self.fp)
+
+ CalendarHomeResource.cacheNotifierFactory = MemcacheChangeNotifier
+ AddressBookHomeResource.cacheNotifierFactory = MemcacheChangeNotifier
+ DirectoryPrincipalResource.cacheNotifierFactory = MemcacheChangeNotifier
+ else:
+ self.responseCache = DisabledCache()
+
if config.ResponseCompression:
from twext.web2.filter import gzip
self.contentFilters.append((gzip.gzipfilter, True))
@@ -297,6 +311,30 @@
request.extendedLogItems = {}
request.extendedLogItems["xff"] = remote_ip[0]
+ if request.method == "PROPFIND" and not getattr(request, "notInCache", False) and len(segments) > 1:
+ try:
+ authnUser, authzUser = (yield self.authenticate(request))
+ request.authnUser = authnUser
+ request.authzUser = authzUser
+ except (UnauthorizedLogin, LoginFailed):
+ response = (yield UnauthorizedResponse.makeResponse(
+ request.credentialFactories,
+ request.remoteAddr
+ ))
+ raise HTTPError(response)
+
+ try:
+ if not getattr(request, "checkingCache", False):
+ request.checkingCache = True
+ response = (yield self.responseCache.getResponseForRequest(request))
+ if response is None:
+ request.notInCache = True
+ raise KeyError("Not found in cache.")
+
+ returnValue((_CachedResponseResource(response), []))
+ except KeyError:
+ pass
+
child = (yield super(RootResource, self).locateChild(request, segments))
returnValue(child)
Modified: CalendarServer/trunk/calendarserver/provision/test/test_root.py
===================================================================
--- CalendarServer/trunk/calendarserver/provision/test/test_root.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/calendarserver/provision/test/test_root.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -366,7 +366,79 @@
)
return self.send(request, do_test)
+class SACLCacheTests(RootTests):
+
+ class StubResponseCacheResource(object):
+ def __init__(self):
+ self.cache = {}
+ self.responseCache = self
+ self.cacheHitCount = 0
+ def getResponseForRequest(self, request):
+ if str(request) in self.cache:
+ self.cacheHitCount += 1
+ return self.cache[str(request)]
+
+
+ def cacheResponseForRequest(self, request, response):
+ self.cache[str(request)] = response
+ return response
+
+ def setUp(self):
+ super(SACLCacheTests, self).setUp()
+ self.root.resource.responseCache = SACLCacheTests.StubResponseCacheResource()
+
+ def test_PROPFIND(self):
+ self.root.resource.useSacls = True
+
+ body = """<?xml version="1.0" encoding="utf-8" ?>
+<D:propfind xmlns:D="DAV:">
+<D:prop>
+<D:getetag/>
+<D:displayname/>
+</D:prop>
+</D:propfind>
+"""
+
+ request = SimpleRequest(
+ self.site,
+ "PROPFIND",
+ "/principals/users/dreid/",
+ headers=http_headers.Headers({
+ 'Authorization': ['basic', '%s' % ('dreid:dierd'.encode('base64'),)],
+ 'Content-Type': 'application/xml; charset="utf-8"',
+ 'Depth':'1',
+ }),
+ content=body
+ )
+
+ def gotResponse1(response):
+ if response.code != responsecode.MULTI_STATUS:
+ self.fail("Incorrect response for PROPFIND /principals/: %s" % (response.code,))
+
+ request = SimpleRequest(
+ self.site,
+ "PROPFIND",
+ "/principals/users/dreid/",
+ headers=http_headers.Headers({
+ 'Authorization': ['basic', '%s' % ('dreid:dierd'.encode('base64'),)],
+ 'Content-Type': 'application/xml; charset="utf-8"',
+ 'Depth':'1',
+ }),
+ content=body
+ )
+
+ d = self.send(request, gotResponse2)
+ return d
+
+ def gotResponse2(response):
+ if response.code != responsecode.MULTI_STATUS:
+ self.fail("Incorrect response for PROPFIND /principals/: %s" % (response.code,))
+ self.assertEqual(self.root.resource.responseCache.cacheHitCount, 1)
+
+ d = self.send(request, gotResponse1)
+ return d
+
class WikiTests(RootTests):
@inlineCallbacks
Modified: CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist 2011-01-14 19:23:39 UTC (rev 6737)
@@ -741,7 +741,13 @@
</dict>
</dict>
+ <!-- Response Caching -->
+ <key>EnableResponseCache</key>
+ <true/>
+ <key>ResponseCacheTimeout</key>
+ <integer>30</integer> <!-- in minutes -->
+
<!--
Twisted
-->
Modified: CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist 2011-01-14 19:23:39 UTC (rev 6737)
@@ -741,6 +741,13 @@
</dict>
</dict>
+ <!-- Response Caching -->
+ <key>EnableResponseCache</key>
+ <true/>
+ <key>ResponseCacheTimeout</key>
+ <integer>30</integer> <!-- in minutes -->
+
+
<!--
Twisted
-->
Modified: CalendarServer/trunk/calendarserver/tools/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/util.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/calendarserver/tools/util.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -157,7 +157,7 @@
root.putChild("principals", principalCollection)
# Need a data store
- _newStore = CommonDataStore(FilePath(config.DocumentRoot), True, False)
+ _newStore = CommonDataStore(FilePath(config.DocumentRoot), None, True, False)
from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
calendarCollection = DirectoryCalendarHomeProvisioningResource(
Modified: CalendarServer/trunk/conf/caldavd-test.plist
===================================================================
--- CalendarServer/trunk/conf/caldavd-test.plist 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/conf/caldavd-test.plist 2011-01-14 19:23:39 UTC (rev 6737)
@@ -761,6 +761,12 @@
</array>
</dict>
+ <!-- Response Caching -->
+ <key>EnableResponseCache</key>
+ <true/>
+ <key>ResponseCacheTimeout</key>
+ <integer>30</integer> <!-- in minutes -->
+
<!-- Support for Postgres -->
<key>Postgres</key>
<dict>
Modified: CalendarServer/trunk/conf/resources/caldavd-resources.plist
===================================================================
--- CalendarServer/trunk/conf/resources/caldavd-resources.plist 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/conf/resources/caldavd-resources.plist 2011-01-14 19:23:39 UTC (rev 6737)
@@ -718,7 +718,13 @@
</array>
</dict>
+ <!-- Response Caching -->
+ <key>EnableResponseCache</key>
+ <true/>
+ <key>ResponseCacheTimeout</key>
+ <integer>30</integer> <!-- in minutes -->
+
<!--
Twisted
-->
Copied: CalendarServer/trunk/twistedcaldav/cache.py (from rev 5318, CalendarServer/trunk/twistedcaldav/cache.py)
===================================================================
--- CalendarServer/trunk/twistedcaldav/cache.py (rev 0)
+++ CalendarServer/trunk/twistedcaldav/cache.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -0,0 +1,366 @@
+##
+# Copyright (c) 2008-2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+import cPickle
+import hashlib
+import uuid
+
+from zope.interface import implements
+
+from twisted.internet.defer import succeed, maybeDeferred
+from twext.web2.dav.util import allDataFromStream
+from twext.web2.http import Response
+from twext.web2.iweb import IResource
+from twext.web2.stream import MemoryStream
+
+from twext.python.log import LoggingMixIn
+
+from twistedcaldav.memcachepool import CachePoolUserMixIn, defaultCachePool
+from twistedcaldav.config import config
+
+
+class DisabledCacheNotifier(object):
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def changed(self):
+ return succeed(None)
+
+
+class DisabledCache(object):
+ def getResponseForRequest(self, request):
+ return succeed(None)
+
+ def cacheResponseForRequest(self, request, response):
+ return succeed(response)
+
+
+class URINotFoundException(Exception):
+ def __init__(self, uri):
+ self.uri = uri
+
+
+ def __repr__(self):
+ return "%s: Could not find URI %r" % (
+ self.__class__.__name__,
+ self.uri)
+
+
+class MemcacheChangeNotifier(LoggingMixIn, CachePoolUserMixIn):
+
+ def __init__(self, resource, cachePool=None, cacheHandle="Default"):
+ self._resource = resource
+ self._cachePool = cachePool
+ self._cachePoolHandle = cacheHandle
+
+ def _newCacheToken(self):
+ return str(uuid.uuid4())
+
+ def changed(self):
+ """
+ Change the cache token for a resource
+
+ return: A L{Deferred} that fires when the token has been changed.
+ """
+ url = self._resource.url()
+
+ self.log_debug("Changing Cache Token for %r" % (url,))
+ return self.getCachePool().set(
+ 'cacheToken:%s' % (url,),
+ self._newCacheToken(), expireTime=config.ResponseCacheTimeout*60)
+
+class BaseResponseCache(LoggingMixIn):
+ """
+ A base class which provides some common operations
+ """
+ def _principalURI(self, principal):
+ return str(principal.children[0])
+
+
+ def _uriNotFound(self, f, uri):
+ f.trap(AttributeError)
+ raise URINotFoundException(uri)
+
+
+ def _getRecordForURI(self, uri, request):
+ def _getRecord(resrc):
+ if hasattr(resrc, 'record'):
+ return resrc.record
+
+ try:
+ return request.locateResource(uri).addCallback(
+ _getRecord).addErrback(self._uriNotFound, uri)
+ except AssertionError:
+ raise URINotFoundException(uri)
+
+
+ def _canonicalizeURIForRequest(self, uri, request):
+ try:
+ return request.locateResource(uri).addCallback(
+ lambda resrc: resrc.url()).addErrback(self._uriNotFound, uri)
+ except AssertionError:
+ raise URINotFoundException(uri)
+
+
+ def _getURIs(self, request):
+ def _getSecondURI(rURI):
+ return self._canonicalizeURIForRequest(
+ self._principalURI(request.authnUser),
+ request).addCallback(lambda pURI: (pURI, rURI))
+
+ d = self._canonicalizeURIForRequest(request.uri, request)
+ d.addCallback(_getSecondURI)
+
+ return d
+
+
+ def _requestKey(self, request):
+ def _getBody(uris):
+ return allDataFromStream(request.stream).addCallback(
+ lambda body: (body, uris))
+
+ def _getKey((requestBody, (pURI, rURI))):
+ if requestBody is not None:
+ request.stream = MemoryStream(requestBody)
+ request.stream.doStartReading = None
+
+ request.cacheKey = (request.method,
+ pURI,
+ rURI,
+ request.headers.getHeader('depth'),
+ hash(requestBody))
+
+ return request.cacheKey
+
+ d = _getBody((self._principalURI(request.authnUser), request.uri))
+ d.addCallback(_getKey)
+ return d
+
+
+ def _getResponseBody(self, key, response):
+ d1 = allDataFromStream(response.stream)
+ d1.addCallback(lambda responseBody: (key, responseBody))
+ return d1
+
+
+class MemcacheResponseCache(BaseResponseCache, CachePoolUserMixIn):
+ def __init__(self, docroot, cachePool=None):
+ self._docroot = docroot
+ self._cachePool = cachePool
+
+
+ def _tokenForURI(self, uri, cachePoolHandle=None):
+ """
+ Get a property store for the given C{uri}.
+
+ @param uri: The URI we'd like the token for.
+ @return: A C{str} representing the token for the URI.
+ """
+
+ if cachePoolHandle:
+ return defaultCachePool(cachePoolHandle).get('cacheToken:%s' % (uri,))
+ else:
+ return self.getCachePool().get('cacheToken:%s' % (uri,))
+
+
+ def _getTokens(self, request):
+ def _tokensForURIs((pURI, rURI)):
+ tokens = []
+ d1 = self._tokenForURI(pURI, "PrincipalToken")
+ d1.addCallback(tokens.append)
+ d1.addCallback(lambda _ign: self._getRecordForURI(pURI, request))
+ d1.addCallback(lambda dToken: tokens.append(hash(dToken)))
+ d1.addCallback(lambda _ign: self._tokenForURI(rURI))
+ d1.addCallback(tokens.append)
+ d1.addCallback(lambda _ign: tokens)
+ return d1
+
+ d = self._getURIs(request)
+ d.addCallback(_tokensForURIs)
+ return d
+
+
+ def _hashedRequestKey(self, request):
+ def _hashKey(key):
+ oldkey = key
+ request.cacheKey = key = hashlib.md5(
+ ':'.join([str(t) for t in key])).hexdigest()
+ self.log_debug("hashing key for get: %r to %r" % (oldkey, key))
+ return request.cacheKey
+
+ d = self._requestKey(request)
+ d.addCallback(_hashKey)
+ return d
+
+
+ def getResponseForRequest(self, request):
+ def _checkTokens(curTokens, expectedTokens, (code, headers, body)):
+ if curTokens[0] != expectedTokens[0]:
+ self.log_debug(
+ "Principal token doesn't match for %r: %r != %r" % (
+ request.cacheKey,
+ curTokens[0],
+ expectedTokens[0]))
+ return None
+
+ if curTokens[1] != expectedTokens[1]:
+ self.log_debug(
+ "Directory Record Token doesn't match for %r: %r != %r" % (
+ request.cacheKey,
+ curTokens[1],
+ expectedTokens[1]))
+ return None
+
+ if curTokens[2] != expectedTokens[2]:
+ self.log_debug(
+ "URI token doesn't match for %r: %r != %r" % (
+ request.cacheKey,
+ curTokens[1],
+ expectedTokens[1]))
+ return None
+
+ r = Response(code,
+ stream=MemoryStream(body))
+
+ for key, value in headers.iteritems():
+ r.headers.setRawHeaders(key, value)
+
+ return r
+
+ def _unpickleResponse((flags, value), key):
+ if value is None:
+ self.log_debug("Not in cache: %r" % (key,))
+ return None
+
+ self.log_debug("Found in cache: %r = %r" % (key, value))
+
+ (principalToken, directoryToken, uriToken,
+ resp) = cPickle.loads(value)
+ d2 = self._getTokens(request)
+
+ d2.addCallback(_checkTokens,
+ (principalToken,
+ directoryToken,
+ uriToken),
+ resp)
+
+ return d2
+
+ def _getCached(key):
+ self.log_debug("Checking cache for: %r" % (key,))
+ d1 = self.getCachePool().get(key)
+ return d1.addCallback(_unpickleResponse, key)
+
+ def _handleExceptions(f):
+ f.trap(URINotFoundException)
+ self.log_debug("Could not locate URI: %r" % (f.value,))
+ return None
+
+ d = self._hashedRequestKey(request)
+ d.addCallback(_getCached)
+ d.addErrback(_handleExceptions)
+ return d
+
+
+ def cacheResponseForRequest(self, request, response):
+ def _makeCacheEntry((pToken, dToken, uToken), (key, responseBody)):
+ cacheEntry = cPickle.dumps(
+ (pToken,
+ dToken,
+ uToken,
+ (response.code,
+ dict(list(response.headers.getAllRawHeaders())),
+ responseBody)))
+
+ self.log_debug("Adding to cache: %r = %r" % (key, cacheEntry))
+ return self.getCachePool().set(key, cacheEntry,
+ expireTime=config.ResponseCacheTimeout*60).addCallback(
+ lambda _: response)
+
+ def _cacheResponse((key, responseBody)):
+
+ response.headers.removeHeader('date')
+ response.stream = MemoryStream(responseBody)
+
+ d1 = self._getTokens(request)
+ d1.addCallback(_makeCacheEntry, (key, responseBody))
+ return d1
+
+ def _handleExceptions(f):
+ f.trap(URINotFoundException)
+ self.log_debug("Could not locate URI: %r" % (f.value,))
+ return response
+
+ if hasattr(request, 'cacheKey'):
+ d = succeed(request.cacheKey)
+ else:
+ d = self._hashedRequestKey(request)
+
+ d.addCallback(self._getResponseBody, response)
+ d.addCallback(_cacheResponse)
+ d.addErrback(_handleExceptions)
+ return d
+
+
+class _CachedResponseResource(object):
+ implements(IResource)
+
+ def __init__(self, response):
+ self._response = response
+
+ def renderHTTP(self, request):
+ if not hasattr(request, "extendedLogItems"):
+ request.extendedLogItems = {}
+ request.extendedLogItems["cached"] = "1"
+ return self._response
+
+ def locateChild(self, request, segments):
+ return self, []
+
+
+class PropfindCacheMixin(object):
+ def renderHTTP(self, request):
+ def _cacheResponse(responseCache, response):
+ return responseCache.cacheResponseForRequest(request, response)
+
+ def _getResponseCache(response):
+ d1 = request.locateResource("/")
+ d1.addCallback(lambda resource: resource.responseCache)
+ d1.addCallback(_cacheResponse, response)
+ return d1
+
+ d = maybeDeferred(super(PropfindCacheMixin, self).renderHTTP, request)
+
+ if request.method == 'PROPFIND':
+ d.addCallback(_getResponseCache)
+ return d
+
+ def changeCache(self):
+ if hasattr(self, 'cacheNotifier'):
+ return self.cacheNotifier.changed()
+ else:
+ self.log_debug("%r does not have a cacheNotifier but was changed" % (self,))
+
+class CacheStoreNotifier(object):
+
+ def __init__(self, resource):
+ self.resource = resource
+
+ def notify(self, op="update"):
+ self.resource.changeCache()
+
+ def clone(self, label="default", id=None):
+ return self
Modified: CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -193,9 +193,22 @@
principals.append(principal)
newUIDs.add(principal.principalUID())
+ # Get the old set of UIDs
+ oldUIDs = (yield self._index().getMembers(self.uid))
+
# Change membership
yield self.setGroupMemberSetPrincipals(principals)
-
+
+ # Invalidate the primary principal's cache, and any principal's whose
+ # membership status changed
+ yield self.parent.cacheNotifier.changed()
+
+ changedUIDs = newUIDs.symmetric_difference(oldUIDs)
+ for uid in changedUIDs:
+ principal = self.pcollection.principalForUID(uid)
+ if principal:
+ yield principal.cacheNotifier.changed()
+
returnValue(True)
def setGroupMemberSetPrincipals(self, principals):
Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -52,6 +52,7 @@
from twistedcaldav.authkerb import NegotiateCredentials
from twistedcaldav.config import config
+from twistedcaldav.cache import DisabledCacheNotifier, PropfindCacheMixin
from twistedcaldav.directory import calendaruserproxy
from twistedcaldav.directory import augment
from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyPrincipalResource
@@ -500,7 +501,7 @@
def principalCollections(self):
return self.parent.principalCollections()
-class DirectoryPrincipalResource (PermissionsMixIn, DAVPrincipalResource):
+class DirectoryPrincipalResource (PropfindCacheMixin, PermissionsMixIn, DAVPrincipalResource):
"""
Directory principal resource.
"""
@@ -514,6 +515,8 @@
davxml.ResourceID.qname(),
)
+ cacheNotifierFactory = DisabledCacheNotifier
+
def __init__(self, parent, record):
"""
@param parent: the parent of this resource.
@@ -521,6 +524,8 @@
"""
super(DirectoryPrincipalResource, self).__init__()
+ self.cacheNotifier = self.cacheNotifierFactory(self, cacheHandle="PrincipalToken")
+
if self.isCollection():
slash = "/"
else:
Modified: CalendarServer/trunk/twistedcaldav/directory/test/resources/caldavd.plist
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/resources/caldavd.plist 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/directory/test/resources/caldavd.plist 2011-01-14 19:23:39 UTC (rev 6737)
@@ -719,7 +719,13 @@
</array>
</dict>
+ <!-- Response Caching -->
+ <key>EnableResponseCache</key>
+ <true/>
+ <key>ResponseCacheTimeout</key>
+ <integer>30</integer> <!-- in minutes -->
+
<!--
Twisted
-->
Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -23,6 +23,7 @@
from twext.web2.dav.resource import AccessDeniedError
from twext.web2.test.test_server import SimpleRequest
+from twistedcaldav.cache import DisabledCacheNotifier
from twistedcaldav.config import config
from twistedcaldav.directory import augment, calendaruserproxy
from twistedcaldav.directory.calendar import DirectoryCalendarHomeProvisioningResource
@@ -298,6 +299,15 @@
# DirectoryPrincipalResource
##
+ def test_cacheNotifier(self):
+ """
+ Each DirectoryPrincipalResource should have a cacheNotifier attribute
+ that is an instance of DisabledCacheNotifier
+ """
+ for provisioningResource, recordType, recordResource, record in self._allRecords():
+ self.failUnless(isinstance(recordResource.cacheNotifier,
+ DisabledCacheNotifier))
+
def test_displayName(self):
"""
DirectoryPrincipalResource.displayName()
@@ -367,7 +377,7 @@
os.mkdir(path)
# Need a data store
- _newStore = CommonDataStore(path, True, False)
+ _newStore = CommonDataStore(path, None, True, False)
provisioningResource = DirectoryCalendarHomeProvisioningResource(
directory,
Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -20,7 +20,8 @@
from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.test.util import xmlFile, augmentsFile, proxiesFile
-from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
+from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource,\
+ DirectoryPrincipalResource
from twistedcaldav.directory.xmlfile import XMLDirectoryService
import twistedcaldav.test.util
@@ -300,7 +301,37 @@
set(["5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1",
"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0"]))
+ @inlineCallbacks
+ def test_setGroupMemberSetNotifiesPrincipalCaches(self):
+ class StubCacheNotifier(object):
+ changedCount = 0
+ def changed(self):
+ self.changedCount += 1
+ return succeed(None)
+ user = self._getPrincipalByShortName(self.directoryService.recordType_users, "cdaboo")
+
+ proxyGroup = user.getChild("calendar-proxy-write")
+
+ notifier = StubCacheNotifier()
+
+ oldCacheNotifier = DirectoryPrincipalResource.cacheNotifierFactory
+
+ try:
+ DirectoryPrincipalResource.cacheNotifierFactory = (lambda _1, _2, **kwargs: notifier)
+
+ self.assertEquals(notifier.changedCount, 0)
+
+ yield proxyGroup.setGroupMemberSet(
+ davxml.GroupMemberSet(
+ davxml.HRef.fromString(
+ "/XMLDirectoryService/__uids__/5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1/")),
+ None)
+
+ self.assertEquals(notifier.changedCount, 1)
+ finally:
+ DirectoryPrincipalResource.cacheNotifierFactory = oldCacheNotifier
+
def test_proxyFor(self):
return self._proxyForTest(
Modified: CalendarServer/trunk/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/extensions.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/extensions.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -682,7 +682,25 @@
))
+def updateCacheTokenOnCallback(f):
+ def wrapper(self, *args, **kwargs):
+ if hasattr(self, "cacheNotifier"):
+ def updateToken(response):
+ d = self.cacheNotifier.changed()
+ d.addCallback(lambda _: response)
+ return d
+ d = maybeDeferred(f, self, *args, **kwargs)
+
+ if hasattr(self, "cacheNotifier"):
+ d.addCallback(updateToken)
+
+ return d
+ else:
+ return f(self, *args, **kwargs)
+
+ return wrapper
+
class DAVResource (DirectoryPrincipalPropertySearchMixIn,
SudoersMixin, SuperDAVResource, LoggingMixIn,
DirectoryRenderingMixIn, StaticRenderMixin):
@@ -692,6 +710,22 @@
Note we add StaticRenderMixin as a base class because we need all the etag etc behavior
that is currently in static.py but is actually applicable to any type of resource.
"""
+
+ @updateCacheTokenOnCallback
+ def http_PROPPATCH(self, request):
+ return super(DAVResource, self).http_PROPPATCH(request)
+
+
+ @updateCacheTokenOnCallback
+ def http_DELETE(self, request):
+ return super(DAVResource, self).http_DELETE(request)
+
+
+ @updateCacheTokenOnCallback
+ def http_ACL(self, request):
+ return super(DAVResource, self).http_ACL(request)
+
+
http_REPORT = http_REPORT
def davComplianceClasses(self):
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -59,6 +59,8 @@
from twistedcaldav import caldavxml, customxml
from twistedcaldav import carddavxml
+from twistedcaldav.cache import PropfindCacheMixin, DisabledCacheNotifier,\
+ CacheStoreNotifier
from twistedcaldav.caldavxml import caldav_namespace
from twistedcaldav.carddavxml import carddav_namespace
from twistedcaldav.config import config
@@ -176,6 +178,22 @@
calendarPrivilegeSet = _calendarPrivilegeSet()
+def updateCacheTokenOnCallback(f):
+ def fun(self, *args, **kwargs):
+ def _updateToken(response):
+ return self.cacheNotifier.changed().addCallback(
+ lambda _: response)
+
+ d = maybeDeferred(f, self, *args, **kwargs)
+
+ if hasattr(self, 'cacheNotifier'):
+ d.addCallback(_updateToken)
+
+ return d
+
+ return fun
+
+
class CalDAVResource (
CalDAVComplianceMixIn, SharedCollectionMixin,
DAVResourceWithChildrenMixin, DAVResource, LoggingMixIn
@@ -343,6 +361,18 @@
# End transitional new-store interface
+ @updateCacheTokenOnCallback
+ def http_PROPPATCH(self, request):
+ return super(CalDAVResource, self).http_PROPPATCH(request)
+
+ @updateCacheTokenOnCallback
+ def http_DELETE(self, request):
+ return super(CalDAVResource, self).http_DELETE(request)
+
+ @updateCacheTokenOnCallback
+ def http_ACL(self, request):
+ return super(CalDAVResource, self).http_ACL(request)
+
##
# WebDAV
##
@@ -1935,10 +1965,12 @@
"""
return None
-class CommonHomeResource(SharedHomeMixin, CalDAVResource):
+class CommonHomeResource(PropfindCacheMixin, SharedHomeMixin, CalDAVResource):
"""
Logic common to Calendar and Addressbook home resources.
"""
+ cacheNotifierFactory = DisabledCacheNotifier
+
def __init__(self, parent, name, transaction, home):
self.parent = parent
self.name = name
@@ -1947,6 +1979,8 @@
self._provisionedLinks = {}
self._setupProvisions()
self._newStoreHome = home
+ self.cacheNotifier = self.cacheNotifierFactory(self)
+ self._newStoreHome.addNotifier(CacheStoreNotifier(self))
CalDAVResource.__init__(self)
from twistedcaldav.storebridge import _NewStorePropertiesWrapper
@@ -2297,6 +2331,9 @@
def principalForRecord(self):
raise NotImplementedError("Subclass must implement principalForRecord()")
+ def notifierID(self, label="default"):
+ self._newStoreHome.notifierID(label)
+
def notifyChanged(self):
self._newStoreHome.notifyChanged()
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -623,6 +623,9 @@
"EnableKeepAlive": True,
+ "EnableResponseCache": True,
+ "ResponseCacheTimeout": 30, # Minutes
+
# Specify which opendirectory module to use:
# "opendirectory" is PyOpenDirectory (the old one which uses
# DirectoryService.framework)
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -465,6 +465,12 @@
yield self._newStoreObject.rename(basename)
returnValue(NO_CONTENT)
+ def notifierID(self, label="default"):
+ self._newStoreObject.notifierID(label)
+
+ def notifyChanged(self):
+ self._newStoreObject.notifyChanged()
+
class CalendarCollectionResource(_CommonHomeChildCollectionMixin, CalDAVResource):
"""
Wrapper around a L{txdav.caldav.icalendar.ICalendar}.
Copied: CalendarServer/trunk/twistedcaldav/test/test_cache.py (from rev 5318, CalendarServer/trunk/twistedcaldav/test/test_cache.py)
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_cache.py (rev 0)
+++ CalendarServer/trunk/twistedcaldav/test/test_cache.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -0,0 +1,479 @@
+##
+# Copyright (c) 2008-2010 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from new import instancemethod
+import hashlib
+import cPickle
+
+from twisted.internet.defer import succeed, maybeDeferred
+
+from twext.web2.dav import davxml
+from twext.web2.dav.util import allDataFromStream
+from twext.web2.stream import MemoryStream
+from twext.web2.http_headers import Headers
+
+from twistedcaldav.cache import MemcacheResponseCache
+from twistedcaldav.cache import MemcacheChangeNotifier
+from twistedcaldav.cache import PropfindCacheMixin
+
+from twistedcaldav.test.util import InMemoryMemcacheProtocol
+from twistedcaldav.test.util import TestCase
+
+
+def _newCacheToken(self):
+ called = getattr(self, '_called', 0)
+
+ token = 'token%d' % (called,)
+ setattr(self, '_called', called + 1)
+ return token
+
+
+
+class StubRequest(object):
+ resources = {}
+
+ def __init__(self, method, uri, authnUser, depth='1', body=None):
+ self.method = method
+ self.uri = uri
+ self.authnUser = davxml.Principal(davxml.HRef.fromString(authnUser))
+ self.headers = Headers({'depth': depth})
+
+ if body is None:
+ body = "foobar"
+
+ self.body = body
+ self.stream = MemoryStream(body)
+
+
+ def locateResource(self, uri):
+ assert uri[0] == '/', "URI path didn't begin with '/': %s" % (uri,)
+ return succeed(self.resources.get(uri))
+
+
+
+class StubResponse(object):
+ def __init__(self, code, headers, body):
+ self.code = code
+ self.headers = Headers(headers)
+ self.body = body
+ self.stream = MemoryStream(body)
+
+
+
+class StubURLResource(object):
+ def __init__(self, url, record=None):
+ self._url = url
+
+ if record is not None:
+ self.record = record
+
+ def url(self):
+ return self._url
+
+
+
+class MemCacheChangeNotifierTests(TestCase):
+ def setUp(self):
+ TestCase.setUp(self)
+ self.memcache = InMemoryMemcacheProtocol()
+ self.ccn = MemcacheChangeNotifier(
+ StubURLResource(':memory:'),
+ cachePool=self.memcache)
+
+ self.ccn._newCacheToken = instancemethod(_newCacheToken,
+ self.ccn,
+ MemcacheChangeNotifier)
+
+ def assertToken(self, expectedToken):
+ token = self.memcache._cache['cacheToken::memory:'][1]
+ self.assertEquals(token, expectedToken)
+
+
+ def test_cacheTokenPropertyIsProvisioned(self):
+ d = self.ccn.changed()
+ d.addCallback(lambda _: self.assertToken('token0'))
+ return d
+
+
+ def test_changedChangesToken(self):
+ d = self.ccn.changed()
+ d.addCallback(lambda _: self.ccn.changed())
+ d.addCallback(lambda _: self.assertToken('token1'))
+ return d
+
+
+ def tearDown(self):
+ for call in self.memcache._timeouts.itervalues():
+ call.cancel()
+ MemcacheChangeNotifier._memcacheProtocol = None
+
+
+
+class BaseCacheTestMixin(object):
+ def setUp(self):
+ StubRequest.resources = {
+ '/calendars/__uids__/cdaboo/': StubURLResource(
+ '/calendars/__uids__/cdaboo/'),
+ '/calendars/users/cdaboo/': StubURLResource(
+ '/calendars/__uids__/cdaboo/'),
+ '/principals/__uids__/cdaboo/': StubURLResource(
+ '/principals/__uids__/cdaboo/', record='directoryToken0'),
+ '/calendars/__uids__/dreid/': StubURLResource(
+ '/calendars/__uids__/dreid/'),
+ '/principals/__uids__/dreid/': StubURLResource(
+ '/principals/__uids__/dreid/', record='directoryToken0')}
+
+
+ def tearDown(self):
+ StubRequest.resources = {}
+
+
+ def assertResponse(self, response, expected):
+ self.assertNotEquals(response, None, "Got None instead of a response.")
+ self.assertEquals(response.code, expected[0])
+ self.assertEquals(set(response.headers.getAllRawHeaders()),
+ set(expected[1].getAllRawHeaders()))
+
+ d = allDataFromStream(response.stream)
+ d.addCallback(self.assertEquals, expected[2])
+ return d
+
+
+ def test_getResponseForRequestMultiHomedRequestURI(self):
+ request = StubRequest(
+ 'PROPFIND',
+ '/calendars/users/cdaboo/',
+ '/principals/__uids__/cdaboo/')
+
+ d = self.rc.getResponseForRequest(request)
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_getResponseForRequestURINotFound(self):
+ request = StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/wsanchez/',
+ '/calendars/__uids__/dreid/')
+
+ d = self.rc.getResponseForRequest(request)
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_getResponseForRequestMultiHomedPrincipalURI(self):
+ request = StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '/principals/users/cdaboo/')
+
+ d = self.rc.getResponseForRequest(request)
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_getResponseForRequestNotInCache(self):
+ d = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/dreid/',
+ '/principals/__uids__/dreid/'))
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_getResponseForRequestInCache(self):
+ d = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '/principals/__uids__/cdaboo/'))
+
+ d.addCallback(self.assertResponse, self.expected_response)
+ return d
+
+
+ def test_getResponseForRequestPrincipalTokenChanged(self):
+ self.tokens['/principals/__uids__/cdaboo/'] = 'principalToken1'
+
+ d = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '/principals/__uids__/cdaboo/'))
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_getResponseForRequestUriTokenChanged(self):
+ self.tokens['/calendars/__uids__/cdaboo/'] = 'uriToken1'
+
+ d = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '/principals/__uids__/cdaboo/'))
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_getResponseForDepthZero(self):
+ d = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '/principals/__uids__/cdaboo/',
+ depth='0'))
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_getResponseForBody(self):
+ d = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '/principals/__uids__/cdaboo/',
+ body='bazbax'))
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_getResponseForUnauthenticatedRequest(self):
+ d = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '{DAV:}unauthenticated',
+ body='bazbax'))
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+ def test_cacheUnauthenticatedResponse(self):
+ expected_response = StubResponse(401, {}, "foobar")
+
+ d = self.rc.cacheResponseForRequest(
+ StubRequest('PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '{DAV:}unauthenticated'),
+ expected_response)
+
+ d.addCallback(self.assertResponse,
+ (expected_response.code,
+ expected_response.headers,
+ expected_response.body))
+
+ return d
+
+
+ def test_cacheResponseForRequest(self):
+ expected_response = StubResponse(200, {}, "Foobar")
+
+ def _assertResponse(ign):
+ d1 = self.rc.getResponseForRequest(StubRequest(
+ 'PROPFIND',
+ '/principals/__uids__/dreid/',
+ '/principals/__uids__/dreid/'))
+
+
+ d1.addCallback(self.assertResponse,
+ (expected_response.code,
+ expected_response.headers,
+ expected_response.body))
+ return d1
+
+
+ d = self.rc.cacheResponseForRequest(
+ StubRequest('PROPFIND',
+ '/principals/__uids__/dreid/',
+ '/principals/__uids__/dreid/'),
+ expected_response)
+
+ d.addCallback(_assertResponse)
+ return d
+
+
+ def test_recordHashChangeInvalidatesCache(self):
+ StubRequest.resources[
+ '/principals/__uids__/cdaboo/'].record = 'directoryToken1'
+
+ d = self.rc.getResponseForRequest(
+ StubRequest(
+ 'PROPFIND',
+ '/calendars/__uids__/cdaboo/',
+ '/principals/__uids__/cdaboo/'))
+
+ d.addCallback(self.assertEquals, None)
+ return d
+
+
+
+class MemcacheResponseCacheTests(BaseCacheTestMixin, TestCase):
+ def setUp(self):
+ super(MemcacheResponseCacheTests, self).setUp()
+
+ memcacheStub = InMemoryMemcacheProtocol()
+ self.rc = MemcacheResponseCache(None, cachePool=memcacheStub)
+ self.rc.logger.setLevel('debug')
+ self.tokens = {}
+
+ self.tokens['/calendars/__uids__/cdaboo/'] = 'uriToken0'
+ self.tokens['/principals/__uids__/cdaboo/'] = 'principalToken0'
+ self.tokens['/principals/__uids__/dreid/'] = 'principalTokenX'
+
+ def _getToken(uri, cachePoolHandle=None):
+ return succeed(self.tokens.get(uri))
+
+ self.rc._tokenForURI = _getToken
+
+ self.expected_response = (200, Headers({}), "Foo")
+
+ expected_key = hashlib.md5(':'.join([str(t) for t in (
+ 'PROPFIND',
+ '/principals/__uids__/cdaboo/',
+ '/calendars/__uids__/cdaboo/',
+ '1',
+ hash('foobar'),
+ )])).hexdigest()
+
+ memcacheStub._cache[expected_key] = (
+ 0, #flags
+ cPickle.dumps((
+ 'principalToken0',
+ hash('directoryToken0'),
+ 'uriToken0',
+ (self.expected_response[0],
+ dict(list(self.expected_response[1].getAllRawHeaders())),
+ self.expected_response[2]))))
+
+ self.memcacheStub = memcacheStub
+
+ def tearDown(self):
+ for call in self.memcacheStub._timeouts.itervalues():
+ call.cancel()
+
+ def test_givenURIsForKeys(self):
+ expected_response = (200, Headers({}), "Foobarbaz")
+
+ _key = (
+ 'PROPFIND',
+ '/principals/__uids__/cdaboo/',
+ '/calendars/users/cdaboo/',
+ '1',
+ hash('foobar'),
+ )
+
+ expected_key = hashlib.md5(':'.join([str(t) for t in _key])).hexdigest()
+
+ self.memcacheStub._cache[expected_key] = (
+ 0, #flags
+ cPickle.dumps((
+ 'principalToken0',
+ hash('directoryToken0'),
+ 'uriToken0',
+ (expected_response[0],
+ dict(list(expected_response[1].getAllRawHeaders())),
+ expected_response[2]))))
+
+ d = self.rc.getResponseForRequest(
+ StubRequest('PROPFIND',
+ '/calendars/users/cdaboo/',
+ '/principals/__uids__/cdaboo/'))
+
+ d.addCallback(self.assertResponse, expected_response)
+ return d
+
+
+
+class StubResponseCacheResource(object):
+ def __init__(self):
+ self.cache = {}
+ self.responseCache = self
+
+
+ def getResponseForRequest(self, request):
+ if request in self.cache:
+ return self.cache[request]
+
+
+ def cacheResponseForRequest(self, request, response):
+ self.cache[request] = response
+ return response
+
+
+
+class TestRenderMixin(object):
+ davHeaders = ('foo',)
+
+ def renderHTTP(self, request):
+ self.response.headers.setHeader('dav', self.davHeaders)
+
+ return self.response
+
+
+
+class TestCachingResource(PropfindCacheMixin, TestRenderMixin):
+ def __init__(self, response):
+ self.response = response
+
+
+
+class PropfindCacheMixinTests(TestCase):
+ """
+ Test the PropfindCacheMixin
+ """
+ def setUp(self):
+ TestCase.setUp(self)
+ self.resource = TestCachingResource(StubResponse(200, {}, "foobar"))
+ self.responseCache = StubResponseCacheResource()
+
+ def test_DAVHeaderCached(self):
+ """
+ Test that the DAV header set in renderHTTP is cached.
+ """
+ def _checkCache(response):
+ self.assertEquals(response.headers.getHeader('dav'),
+ ('foo',))
+ self.assertEquals(
+ self.responseCache.cache[request].headers.getHeader('dav'),
+ ('foo',))
+
+ request = StubRequest('PROPFIND', '/', '/')
+ request.resources['/'] = self.responseCache
+
+ d = maybeDeferred(self.resource.renderHTTP, request)
+ d.addCallback(_checkCache)
+
+ return d
+
+
+ def test_onlyCachePropfind(self):
+ """
+ Test that we only cache the result of a propfind request.
+ """
+ def _checkCache(response):
+ self.assertEquals(self.responseCache.getResponseForRequest(request),
+ None)
+
+ request = StubRequest('GET', '/', '/')
+ request.resources['/'] = self.responseCache
+
+ d = maybeDeferred(self.resource.renderHTTP, request)
+ d.addCallback(_checkCache)
+
+ return d
Modified: CalendarServer/trunk/twistedcaldav/test/test_link.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_link.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/test/test_link.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -36,6 +36,9 @@
def calendarWithName(self, name):
return succeed(None)
+
+ def addNotifier(self, notifier):
+ pass
class StubCalendarHomeResource(CalendarHomeResource):
def principalForRecord(self):
Modified: CalendarServer/trunk/twistedcaldav/test/test_resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_resource.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/twistedcaldav/test/test_resource.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -27,6 +27,9 @@
class StubHome(object):
def properties(self):
return []
+
+ def addNotifier(self, notifier):
+ pass
class CalDAVResourceTests(TestCase):
Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/file.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/txdav/caldav/datastore/file.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -85,8 +85,8 @@
_topPath = "calendars"
_notifierPrefix = "CalDAV"
- def __init__(self, uid, path, calendarStore, transaction, notifier):
- super(CalendarHome, self).__init__(uid, path, calendarStore, transaction, notifier)
+ def __init__(self, uid, path, calendarStore, transaction, notifiers):
+ super(CalendarHome, self).__init__(uid, path, calendarStore, transaction, notifiers)
self._childClass = Calendar
Property changes on: CalendarServer/trunk/txdav/caldav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/caldav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/index_file.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/index.py:6322-6394
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -74,10 +74,10 @@
_notifierPrefix = "CalDAV"
_revisionsTable = CALENDAR_OBJECT_REVISIONS_TABLE
- def __init__(self, transaction, ownerUID, notifier):
+ def __init__(self, transaction, ownerUID, notifiers):
self._childClass = Calendar
- super(CalendarHome, self).__init__(transaction, ownerUID, notifier)
+ super(CalendarHome, self).__init__(transaction, ownerUID, notifiers)
self._shares = SQLLegacyCalendarShares(self)
createCalendarWithName = CommonHome.createChildWithName
Property changes on: CalendarServer/trunk/txdav/caldav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/caldav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/caldav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/caldav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/caldav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/caldav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/caldav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/caldav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/caldav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/caldav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/caldav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/caldav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/caldav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/caldav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/caldav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/caldav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/caldav/datastore/test/test_index_file.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_index_file.py:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/txdav/caldav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/caldav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/caldav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/caldav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/caldav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/caldav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/caldav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/caldav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/caldav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/caldav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_index.py:6322-6394
Modified: CalendarServer/trunk/txdav/carddav/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/file.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/txdav/carddav/datastore/file.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -67,8 +67,8 @@
_topPath = "addressbooks"
_notifierPrefix = "CardDAV"
- def __init__(self, uid, path, addressbookStore, transaction, notifier):
- super(AddressBookHome, self).__init__(uid, path, addressbookStore, transaction, notifier)
+ def __init__(self, uid, path, addressbookStore, transaction, notifiers):
+ super(AddressBookHome, self).__init__(uid, path, addressbookStore, transaction, notifiers)
self._childClass = AddressBook
Property changes on: CalendarServer/trunk/txdav/carddav/datastore/index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/carddav/datastore/index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/index_file.py:6592-6614
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/index_file.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/index_file.py:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/index_file.py:5032-5051
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/vcardindex.py:6322-6394
Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/sql.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -66,10 +66,10 @@
_notifierPrefix = "CardDAV"
_revisionsTable = ADDRESSBOOK_OBJECT_REVISIONS_TABLE
- def __init__(self, transaction, ownerUID, notifier):
+ def __init__(self, transaction, ownerUID, notifiers):
self._childClass = AddressBook
- super(AddressBookHome, self).__init__(transaction, ownerUID, notifier)
+ super(AddressBookHome, self).__init__(transaction, ownerUID, notifiers)
self._shares = SQLLegacyAddressBookShares(self)
Property changes on: CalendarServer/trunk/txdav/carddav/datastore/test/test_index_file.py
___________________________________________________________________
Modified: svn:mergeinfo
- /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
+ /CalendarServer/branches/config-separation/txdav/carddav/datastore/test/test_index_file.py:4379-4443
/CalendarServer/branches/egg-info-351/txdav/carddav/datastore/test/test_index_file.py:4589-4625
/CalendarServer/branches/generic-sqlstore/txdav/carddav/datastore/test/test_index_file.py:6167-6191
/CalendarServer/branches/new-store-no-caldavfile-2/txdav/carddav/datastore/test/test_index_file.py:5936-5981
/CalendarServer/branches/new-store-no-caldavfile/txdav/carddav/datastore/test/test_index_file.py:5911-5935
/CalendarServer/branches/new-store/txdav/carddav/datastore/test/test_index_file.py:5594-5934
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692/txdav/carddav/datastore/test/test_index_file.py:5693-5702
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627/txdav/carddav/datastore/test/test_index_file.py:3628-3644
/CalendarServer/branches/users/cdaboo/more-sharing-5591/txdav/carddav/datastore/test/test_index_file.py:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464/txdav/carddav/datastore/test/test_index_file.py:4465-4957
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070/txdav/carddav/datastore/test/test_index_file.py:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187/txdav/carddav/datastore/test/test_index_file.py:5188-5440
/CalendarServer/branches/users/glyph/conn-limit/txdav/carddav/datastore/test/test_index_file.py:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge/txdav/carddav/datastore/test/test_index_file.py:4971-5080
/CalendarServer/branches/users/glyph/dont-start-postgres/txdav/carddav/datastore/test/test_index_file.py:6592-6614
/CalendarServer/branches/users/glyph/more-deferreds-6/txdav/carddav/datastore/test/test_index_file.py:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_index_file.py:6369-6445
/CalendarServer/branches/users/glyph/sendfdport/txdav/carddav/datastore/test/test_index_file.py:5388-5424
/CalendarServer/branches/users/glyph/sharedpool/txdav/carddav/datastore/test/test_index_file.py:6490-6550
/CalendarServer/branches/users/glyph/sql-store/txdav/carddav/datastore/test/test_index_file.py:5929-6073
/CalendarServer/branches/users/glyph/use-system-twisted/txdav/carddav/datastore/test/test_index_file.py:5084-5149
/CalendarServer/branches/users/sagen/locations-resources-2/txdav/carddav/datastore/test/test_index_file.py:5052-5061
/CalendarServer/branches/users/sagen/locations-resources/txdav/carddav/datastore/test/test_index_file.py:5032-5051
/CalendarServer/branches/users/sagen/resource-delegates-4038/txdav/carddav/datastore/test/test_index_file.py:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066/txdav/carddav/datastore/test/test_index_file.py:4068-4075
/CalendarServer/branches/users/sagen/resources-2/txdav/carddav/datastore/test/test_index_file.py:5084-5093
/CalendarServer/branches/users/wsanchez/transations/txdav/carddav/datastore/test/test_index_file.py:5515-5593
/CalendarServer/trunk/twistedcaldav/test/test_vcardindex.py:6322-6394
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/txdav/common/datastore/file.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -238,12 +238,12 @@
_topPath = None
_notifierPrefix = None
- def __init__(self, uid, path, dataStore, transaction, notifier):
+ def __init__(self, uid, path, dataStore, transaction, notifiers):
self._dataStore = dataStore
self._uid = uid
self._path = path
self._transaction = transaction
- self._notifier = notifier
+ self._notifiers = notifiers
self._shares = SharedCollectionsDatabase(StubResource(self))
self._newChildren = {}
self._removedChildren = set()
@@ -297,12 +297,12 @@
homePath = childPath
if txn._notifierFactory:
- notifier = txn._notifierFactory.newNotifier(id=uid,
- prefix=cls._notifierPrefix)
+ notifiers = (txn._notifierFactory.newNotifier(id=uid,
+ prefix=cls._notifierPrefix),)
else:
- notifier = None
+ notifiers = None
- home = cls(uid, homePath, txn._dataStore, txn, notifier)
+ home = cls(uid, homePath, txn._dataStore, txn, notifiers)
if creating:
home.createdHome()
if withNotifications:
@@ -502,9 +502,14 @@
self.properties()[PropertyName.fromElement(TwistedQuotaUsedProperty)] = TwistedQuotaUsedProperty(str(new_used))
+ def addNotifier(self, notifier):
+ if self._notifiers is None:
+ self._notifiers = ()
+ self._notifiers += (notifier,)
+
def notifierID(self, label="default"):
- if self._notifier:
- return self._notifier.getID(label)
+ if self._notifiers:
+ return self._notifiers[0].getID(label)
else:
return None
@@ -512,8 +517,9 @@
"""
Trigger a notification of a change
"""
- if self._notifier:
- self._transaction.postCommit(self._notifier.notify)
+ if self._notifiers:
+ for notifier in self._notifiers:
+ self._transaction.postCommit(notifier.notify)
class CommonHomeChild(FileMetaDataMixin, LoggingMixIn, FancyEqMixin):
@@ -550,12 +556,12 @@
self._invites = None # Derived classes need to set this
self._renamedName = realName
- if home._notifier:
+ if home._notifiers:
childID = "%s/%s" % (home.uid(), name)
- notifier = home._notifier.clone(label="collection", id=childID)
+ notifiers = [notifier.clone(label="collection", id=childID) for notifier in home._notifiers]
else:
- notifier = None
- self._notifier = notifier
+ notifiers = None
+ self._notifiers = notifiers
@classmethod
@@ -819,9 +825,14 @@
def _doValidate(self, component):
raise NotImplementedError
+ def addNotifier(self, notifier):
+ if self._notifiers is None:
+ self._notifiers = ()
+ self._notifiers += (notifier,)
+
def notifierID(self, label="default"):
- if self._notifier:
- return self._notifier.getID(label)
+ if self._notifiers:
+ return self._notifiers[0].getID(label)
else:
return None
@@ -829,8 +840,9 @@
"""
Trigger a notification of a change
"""
- if self._notifier:
- self._transaction.postCommit(self._notifier.notify)
+ if self._notifiers:
+ for notifier in self._notifiers:
+ self._transaction.postCommit(notifier.notify)
class CommonObjectResource(FileMetaDataMixin, LoggingMixIn, FancyEqMixin):
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2011-01-14 02:19:06 UTC (rev 6736)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2011-01-14 19:23:39 UTC (rev 6737)
@@ -243,7 +243,7 @@
_revisionsTable = None
_notificationRevisionsTable = NOTIFICATION_OBJECT_REVISIONS_TABLE
- def __init__(self, transaction, ownerUID, notifier):
+ def __init__(self, transaction, ownerUID, notifiers):
self._txn = transaction
self._ownerUID = ownerUID
self._resourceID = None
@@ -251,7 +251,7 @@
self._childrenLoaded = False
self._children = {}
self._sharedChildren = {}
- self._notifier = notifier
+ self._notifiers = notifiers
self._quotaUsedBytes = None
# Needed for REVISION/BIND table join
@@ -285,12 +285,12 @@
def homeWithUID(cls, txn, uid, create=False):
if txn._notifierFactory:
- notifier = txn._notifierFactory.newNotifier(
+ notifiers = (txn._notifierFactory.newNotifier(
id=uid, prefix=cls._notifierPrefix
- )
+ ),)
else:
- notifier = None
- homeObject = cls(txn, uid, notifier)
+ notifiers = None
+ homeObject = cls(txn, uid, notifiers)
homeObject = (yield homeObject.initFromStore())
if homeObject is not None:
returnValue(homeObject)
@@ -657,9 +657,14 @@
self._quotaUsedBytes = 0
+ def addNotifier(self, notifier):
+ if self._notifiers is None:
+ self._notifiers = ()
+ self._notifiers += (notifier,)
+
def notifierID(self, label="default"):
- if self._notifier:
- return self._notifier.getID(label)
+ if self._notifiers:
+ return self._notifiers[0].getID(label)
else:
return None
@@ -667,8 +672,9 @@
"""
Trigger a notification of a change
"""
- if self._notifier:
- self._txn.postCommit(self._notifier.notify)
+ if self._notifiers:
+ for notifier in self._notifiers:
+ self._txn.postCommit(notifier.notify)
class CommonHomeChild(LoggingMixIn, FancyEqMixin):
@@ -696,12 +702,12 @@
self._objectNames = None
self._syncTokenRevision = None
- if home._notifier:
+ if home._notifiers:
childID = "%s/%s" % (home.uid(), name)
- notifier = home._notifier.clone(label="collection", id=childID)
+ notifiers = [notifier.clone(label="collection", id=childID) for notifier in home._notifiers]
else:
- notifier = None
- self._notifier = notifier
+ notifiers = None
+ self._notifiers = notifiers
self._index = None # Derived classes need to set this
self._invites = None # Derived classes need to set this
@@ -1392,19 +1398,24 @@
return datetimeMktime(datetime.datetime.strptime(self._modified, "%Y-%m-%d %H:%M:%S.%f")) if self._modified else None
+ def addNotifier(self, notifier):
+ if self._notifiers is None:
+ self._notifiers = ()
+ self._notifiers += (notifier,)
+
def notifierID(self, label="default"):
- if self._notifier:
- return self._notifier.getID(label)
+ if self._notifiers:
+ return self._notifiers[0].getID(label)
else:
return None
-
def notifyChanged(self):
"""
Trigger a notification of a change
"""
- if self._notifier:
- self._txn.postCommit(self._notifier.notify)
+ if self._notifiers:
+ for notifier in self._notifiers:
+ self._txn.postCommit(notifier.notify)
@@ -1677,9 +1688,10 @@
prefix=txn._homeClass[txn._primaryHomeType]._notifierPrefix
)
notifier.addID(id=uid)
+ notifiers = (notifier,)
else:
- notifier = None
- self._notifier = notifier
+ notifiers = None
+ self._notifiers = notifiers
@classmethod
@inlineCallbacks
@@ -1945,8 +1957,8 @@
def notifierID(self, label="default"):
- if self._notifier:
- return self._notifier.getID(label)
+ if self._notifiers:
+ return self._notifiers[0].getID(label)
else:
return None
@@ -1955,8 +1967,9 @@
"""
Trigger a notification of a change
"""
- if self._notifier:
- self._txn.postCommit(self._notifier.notify)
+ if self._notifiers:
+ for notifier in self._notifiers:
+ self._txn.postCommit(notifier.notify)
class NotificationObject(LoggingMixIn, FancyEqMixin):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110114/72a8a47e/attachment-0001.html>
More information about the calendarserver-changes
mailing list