[CalendarServer-changes] [4644] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Oct 27 12:16:28 PDT 2009


Revision: 4644
          http://trac.macosforge.org/projects/calendarserver/changeset/4644
Author:   cdaboo at apple.com
Date:     2009-10-27 12:16:27 -0700 (Tue, 27 Oct 2009)
Log Message:
-----------
Merge deployment changes that reduce file system calls for PROPFINDs (and some other ops). Also adds
some more extended logging.

Modified Paths:
--------------
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.server.patch
    CalendarServer/trunk/twistedcaldav/cache.py
    CalendarServer/trunk/twistedcaldav/directory/principal.py
    CalendarServer/trunk/twistedcaldav/directory/resource.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py
    CalendarServer/trunk/twistedcaldav/directory/util.py
    CalendarServer/trunk/twistedcaldav/method/put.py
    CalendarServer/trunk/twistedcaldav/scheduling/caldav.py
    CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
    CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py
    CalendarServer/trunk/twistedcaldav/static.py

Added Paths:
-----------
    CalendarServer/trunk/lib-patches/Twisted/twisted.python.filepath.patch
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.fileop.patch
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.static.patch

Added: CalendarServer/trunk/lib-patches/Twisted/twisted.python.filepath.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.python.filepath.patch	                        (rev 0)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.python.filepath.patch	2009-10-27 19:16:27 UTC (rev 4644)
@@ -0,0 +1,91 @@
+Index: twisted/python/filepath.py
+===================================================================
+--- twisted/python/filepath.py	(revision 26969)
++++ twisted/python/filepath.py	(working copy)
+@@ -326,9 +326,11 @@
+     statinfo = None
+     path = None
+ 
+-    def __init__(self, path, alwaysCreate=False):
++    def __init__(self, path, alwaysCreate=False, existsCached=None, isDirCached=None):
+         self.path = abspath(path)
+         self.alwaysCreate = alwaysCreate
++        self.existsCached = existsCached
++        self.isDirCached = isDirCached
+ 
+     def __getstate__(self):
+         d = self.__dict__.copy()
+@@ -449,6 +451,11 @@
+ 
+     # stat methods below
+ 
++    def changed(self):
++        self.statinfo = None
++        self.existsCached = None
++        self.isDirCached = None
++
+     def restat(self, reraise=True):
+         """
+         Re-calculate cached effects of 'stat'.  To refresh information on this path
+@@ -460,12 +467,23 @@
+         """
+         try:
+             self.statinfo = stat(self.path)
++            self.existsCached = True
++            self.isDirCached = S_ISDIR(self.statinfo.st_mode)
+         except OSError:
+             self.statinfo = 0
++            self.existsCached = False
++            self.isDirCached = None
+             if reraise:
+                 raise
+ 
++    def getstatinfo(self):
++        st = self.statinfo
++        if not st:
++            self.restat()
++            st = self.statinfo
++        return st
+ 
++
+     def chmod(self, mode):
+         """
+         Changes the permissions on self, if possible.  Propagates errors from
+@@ -536,7 +554,9 @@
+             C{False} in the other cases.
+         @rtype: C{bool}
+         """
+-        if self.statinfo:
++        if self.existsCached is not None:
++            return self.existsCached
++        elif self.statinfo:
+             return True
+         else:
+             self.restat(False)
+@@ -547,6 +567,8 @@
+ 
+ 
+     def isdir(self):
++        if self.isDirCached is not None:
++            return self.isDirCached
+         st = self.statinfo
+         if not st:
+             self.restat(False)
+@@ -603,7 +625,7 @@
+             os.rmdir(self.path)
+         else:
+             os.remove(self.path)
+-        self.restat(False)
++        self.changed()
+ 
+ 
+     def makedirs(self):
+@@ -770,7 +792,7 @@
+         """
+         try:
+             os.rename(self.path, destination.path)
+-            self.restat(False)
++            self.changed()
+         except OSError, ose:
+             if ose.errno == errno.EXDEV:
+                 # man 2 rename, ubuntu linux 5.10 "breezy":

Added: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.fileop.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.fileop.patch	                        (rev 0)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.fileop.patch	2009-10-27 19:16:27 UTC (rev 4644)
@@ -0,0 +1,48 @@
+Index: twisted/web2/dav/fileop.py
+===================================================================
+--- twisted/web2/dav/fileop.py	(revision 26969)
++++ twisted/web2/dav/fileop.py	(working copy)
+@@ -162,8 +162,8 @@
+ 
+         response = responsecode.NO_CONTENT
+ 
+-    # Restat filepath since we deleted the backing file
+-    filepath.restat(False)
++    # Remove stat info for filepath since we deleted the backing file
++    filepath.changed()
+ 
+     return succeed(response)
+ 
+@@ -371,8 +371,8 @@
+     except OSError:
+         pass
+     else:
+-        # Restat source filepath since we moved it
+-        source_filepath.restat(False)
++        # Remove stat info from source filepath since we moved it
++        source_filepath.changed()
+         yield success_code
+         return
+ 
+@@ -462,8 +462,8 @@
+             "writing to file: %s" % (filepath.path,)
+         ))
+ 
+-    # Restat filepath since we modified the backing file
+-    filepath.restat(False)
++    # Remove stat info from filepath since we modified the backing file
++    filepath.changed()
+     yield success_code
+ 
+ put = deferredGenerator(put)
+@@ -480,8 +480,8 @@
+     """
+     try:
+         os.mkdir(filepath.path)
+-        # Restat filepath because we modified it
+-        filepath.restat(False)
++        # Remove stat info from filepath because we modified it
++        filepath.changed()
+     except:
+         raise HTTPError(statusForFailure(
+             Failure(),

Modified: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.server.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.server.patch	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.server.patch	2009-10-27 19:16:27 UTC (rev 4644)
@@ -28,3 +28,50 @@
          # This is where CONNECT would go if we wanted it
          return None
  
+@@ -386,6 +396,15 @@
+             # We found a Resource... update the request.prepath and postpath
+             for x in xrange(len(path) - len(newpath)):
+                 self.prepath.append(self.postpath.pop(0))
++            url = quote("/" + "/".join(self.prepath) + ("/" if self.prepath and self.prepath[-1] else ""))
++            self._rememberResource(newres, url)
++        else:
++            try:
++                previousURL = self.urlForResource(res)
++                url = quote(previousURL + path[0] + ("/" if path[0] and len(path) > 1 else ""))
++                self._rememberResource(newres, url)
++            except NoURLForResourceError:
++                pass
+ 
+         child = self._getChild(None, newres, newpath, updatepaths=updatepaths)
+ 
+@@ -467,13 +486,29 @@
+         segments = unquote(path).split("/")
+         assert segments[0] == "", "URL path didn't begin with '/': %s" % (path,)
+ 
++        # Walk the segments up to see if we can find a cached resource to start from
++        preSegments = segments[:-1]
++        postSegments = segments[-1:]
++        cachedParent = None
++        while(len(preSegments)):
++            parentPath = "/".join(preSegments) + "/"
++            cachedParent = self._resourcesByURL.get(parentPath, None)
++            if cachedParent is not None:
++                break
++            else:
++                postSegments.insert(0, preSegments.pop())
++        
++        if cachedParent is None:
++            cachedParent = self.site.resource
++            postSegments = segments[1:]
++
+         def notFound(f):
+             f.trap(http.HTTPError)
+             if f.value.response.code != responsecode.NOT_FOUND:
+                 return f
+             return None
+ 
+-        d = defer.maybeDeferred(self._getChild, None, self.site.resource, segments[1:], updatepaths=False)
++        d = defer.maybeDeferred(self._getChild, None, cachedParent, postSegments, updatepaths=False)
+         d.addCallback(self._rememberResource, path)
+         d.addErrback(notFound)
+         return d

Added: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.static.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.static.patch	                        (rev 0)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.static.patch	2009-10-27 19:16:27 UTC (rev 4644)
@@ -0,0 +1,56 @@
+Index: twisted/web2/static.py
+===================================================================
+--- twisted/web2/static.py	(revision 26969)
++++ twisted/web2/static.py	(working copy)
+@@ -213,13 +213,20 @@
+         if indexNames is not None:
+             self.indexNames = indexNames
+ 
++    def comparePath(self, path):
++        
++        if isinstance(path, filepath.FilePath):
++            return path.path == self.fp.path
++        else:
++            return path == self.fp.path
++
+     def exists(self):
+         return self.fp.exists()
+ 
+     def etag(self):
+         if not self.fp.exists(): return None
+ 
+-        st = self.fp.statinfo
++        st = self.fp.getstatinfo()
+ 
+         #
+         # Mark ETag as weak if it was modified more recently than we can
+@@ -317,8 +324,11 @@
+         if child: return child
+ 
+         child_fp = self.fp.child(name)
++        if hasattr(self, "knownChildren"):
++            if name in self.knownChildren:
++                child_fp.existsCached = True
+         if child_fp.exists():
+-            return self.createSimilarFile(child_fp.path)
++            return self.createSimilarFile(child_fp)
+         else:
+             return None
+ 
+@@ -329,6 +339,7 @@
+         children = self.putChildren.keys()
+         if self.fp.isdir():
+             children += [c for c in self.fp.listdir() if c not in children]
++            self.knownChildren = set(children)
+         return children
+ 
+     def locateChild(self, req, segments):
+@@ -371,7 +382,7 @@
+         return self.createSimilarFile(fpath.path), segments[1:]
+ 
+     def renderHTTP(self, req):
+-        self.fp.restat(False)
++        self.fp.changed()
+         return super(File, self).renderHTTP(req)
+ 
+     def render(self, req):

Modified: CalendarServer/trunk/twistedcaldav/cache.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/cache.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/cache.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -319,6 +319,9 @@
         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):

Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -888,11 +888,8 @@
         return succeed(inbox)
 
     def calendarHomeURLs(self):
-        home = self.calendarHome()
-        if home is None:
-            return ()
-        else:
-            return (home.url(),)
+        homeURL = self._homeChildURL(None)
+        return (homeURL,) if homeURL else ()
 
     def scheduleInboxURL(self):
         return self._homeChildURL("inbox/")
@@ -907,11 +904,19 @@
             return None
 
     def _homeChildURL(self, name):
-        home = self.calendarHome()
-        if home is None:
+        if not hasattr(self, "calendarHomeURL"):
+            home = self.calendarHome()
+            if home is None:
+                self.calendarHomeURL = None
+                return None
+            else:
+                self.calendarHomeURL = home.url()
+            
+        url = self.calendarHomeURL
+        if url is None:
             return None
         else:
-            return joinURL(home.url(), name)
+            return joinURL(url, name) if name else url
 
     def calendarHome(self):
         # FIXME: self.record.service.calendarHomesCollection smells like a hack

Modified: CalendarServer/trunk/twistedcaldav/directory/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/resource.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/directory/resource.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -20,7 +20,7 @@
 
 __all__ = ["AutoProvisioningResourceMixIn"]
 
-from twisted.internet.defer import maybeDeferred
+from twisted.internet.defer import maybeDeferred, inlineCallbacks, returnValue
 
 class AutoProvisioningResourceMixIn (object):
     """
@@ -50,16 +50,19 @@
         """
         return None
 
+    @inlineCallbacks
     def locateChild(self, request, segments):
         """
         This implementation calls L{provision}, then super's L{locateChild}, thereby
         ensuring that looked-up resources are provisioned.
         """
-        d = maybeDeferred(self.provision)
+        yield maybeDeferred(self.provision)
 
         name = segments[0]
         if name != "":
-            d.addCallback(lambda _: self.provisionChild(name))
-
-        d.addCallback(lambda _: super(AutoProvisioningResourceMixIn, self).locateChild(request, segments))
-        return d
+            child = self.provisionChild(name)
+            if child:
+                returnValue((child, segments[1:],))
+        
+        result = (yield super(AutoProvisioningResourceMixIn, self).locateChild(request, segments))
+        returnValue(result)

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -91,7 +91,7 @@
             proxyPrincipal = self._getPrincipalByShortName(proxyPrincipal[0], proxyPrincipal[1])
         members.add(proxyPrincipal)
         
-        principal.setGroupMemberSetPrincipals(members)
+        yield principal.setGroupMemberSetPrincipals(members)
 
     @inlineCallbacks
     def _removeProxy(self, recordType, recordName, subPrincipalName, proxyRecordType, proxyRecordName):
@@ -106,14 +106,14 @@
                 members.remove(p)
                 break
         
-        principal.setGroupMemberSetPrincipals(members)
+        yield principal.setGroupMemberSetPrincipals(members)
 
     def _clearProxy(self, principal, subPrincipalName):
 
         if isinstance(principal, tuple):
             principal = self._getPrincipalByShortName(principal[0], principal[1])
         principal = principal.getChild(subPrincipalName)
-        principal.setGroupMemberSetPrincipals(set())
+        yield principal.setGroupMemberSetPrincipals(set())
 
     @inlineCallbacks
     def _proxyForTest(self, recordType, recordName, expectedProxies, read_write):
@@ -483,7 +483,7 @@
                     delRec = self.directoryService.recordWithShortName(
                         DirectoryService.recordType_users, "dreid")
                     for cache in self.directoryService._recordCaches.itervalues():
-                       cache.removeRecord(delRec)
+                        cache.removeRecord(delRec)
                     del self.directoryService._accounts()[
                         DirectoryService.recordType_users]["dreid"]
 

Modified: CalendarServer/trunk/twistedcaldav/directory/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/util.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/directory/util.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -104,6 +104,9 @@
     def dirname(self):
         return ""
 
+    def changed(self):
+        pass
+
     def restat(self, reraise=True):
         pass
 

Modified: CalendarServer/trunk/twistedcaldav/method/put.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/method/put.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -40,7 +40,6 @@
     parent = (yield request.locateResource(parentURL))
 
     if isPseudoCalendarCollectionResource(parent):
-        self.fp.restat(False)
 
         # Content-type check
         content_type = request.headers.getHeader("content-type")
@@ -51,6 +50,9 @@
         # Read the calendar component from the stream
         try:
             calendardata = (yield allDataFromStream(request.stream))
+            if not hasattr(request, "extendedLogItems"):
+                request.extendedLogItems = {}
+            request.extendedLogItems["cl"] = str(len(calendardata))
 
             # We must have some data at this point
             if calendardata is None:
@@ -74,4 +76,12 @@
 
     else:
         result = (yield super(CalDAVFile, self).http_PUT(request))
+
+        if not hasattr(request, "extendedLogItems"):
+            request.extendedLogItems = {}
+        clength = request.headers.getHeader("content-length", 0)
+        if clength == 0:
+            clength = self.fp.getsize()
+        request.extendedLogItems["cl"] = str(clength)
+        
         returnValue(result)

Modified: CalendarServer/trunk/twistedcaldav/scheduling/caldav.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/caldav.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/scheduling/caldav.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -96,14 +96,18 @@
         organizerProp = self.scheduler.calendar.getOrganizerProperty()
         uid = self.scheduler.calendar.resourceUID()
 
+        organizerPrincipal = None
+        if type(self.scheduler.organizer) in (LocalCalendarUser,):
+            organizerPrincipal = davxml.Principal(davxml.HRef(self.scheduler.organizer.principal.principalURL()))
+
         for recipient in self.recipients:
 
             #
             # Check access controls
             #
-            if isinstance(self.scheduler.organizer, LocalCalendarUser):
+            if organizerPrincipal:
                 try:
-                    yield recipient.inbox.checkPrivileges(self.scheduler.request, (caldavxml.ScheduleDeliver(),), principal=davxml.Principal(davxml.HRef(self.scheduler.organizer.principal.principalURL())))
+                    yield recipient.inbox.checkPrivileges(self.scheduler.request, (caldavxml.ScheduleDeliver(),), principal=organizerPrincipal)
                 except AccessDeniedError:
                     log.err("Could not access Inbox for recipient: %s" % (recipient.cuaddr,))
                     err = HTTPError(ErrorResponse(responsecode.NOT_FOUND, (caldav_namespace, "recipient-permissions")))
@@ -127,8 +131,7 @@
     @inlineCallbacks
     def generateResponse(self, recipient, responses):
         # Hash the iCalendar data for use as the last path element of the URI path
-        calendar_str = str(self.scheduler.calendar)
-        name = md5(calendar_str + str(time.time()) + recipient.inbox.fp.path).hexdigest() + ".ics"
+        name = md5(self.scheduler.calendardata + str(time.time()) + recipient.inbox.fp.path).hexdigest() + ".ics"
     
         # Get a resource for the new item
         childURL = joinURL(recipient.inboxURL, name)
@@ -137,7 +140,7 @@
         # Do implicit scheduling message processing.
         try:
             processor = ImplicitProcessor()
-            processed, autoprocessed, changes = (yield processor.doImplicitProcessing(
+            _ignore_processed, autoprocessed, changes = (yield processor.doImplicitProcessing(
                 self.scheduler.request,
                 self.scheduler.calendar,
                 self.scheduler.originator,
@@ -152,6 +155,9 @@
         if autoprocessed:
             # No need to write the inbox item as it has already been auto-processed
             responses.add(recipient.cuaddr, responsecode.OK, reqstatus=iTIPRequestStatus.MESSAGE_DELIVERED)
+            if not hasattr(self.scheduler.request, "extendedLogItems"):
+                self.scheduler.request.extendedLogItems = {}
+            self.scheduler.request.extendedLogItems["itip.auto"] = self.scheduler.request.extendedLogItems.get("itip.auto", 0) + 1
             returnValue(True)
         else:
             # Copy calendar to inbox 
@@ -255,7 +261,7 @@
                 excludeuid = self.scheduler.excludeUID,
                 organizer = self.scheduler.organizer.cuaddr,
                 same_calendar_user = same_calendar_user,
-                servertoserver=remote
+                servertoserver=remote,
             ))
     
         # Build VFREEBUSY iTIP reply for this recipient

Modified: CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/implicit.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/scheduling/implicit.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -36,6 +36,7 @@
 from twistedcaldav.scheduling.itip import iTipGenerator
 from twistedcaldav.scheduling.scheduler import CalDAVScheduler
 from twistedcaldav.scheduling.utils import getCalendarObjectForPrincipals
+from twistedcaldav.directory.principal import DirectoryCalendarPrincipalResource
 
 __all__ = [
     "ImplicitScheduler",
@@ -328,6 +329,10 @@
             originatorPrincipalURL = str(authz_principal)
             if originatorPrincipalURL:
                 self.originatorPrincipal = (yield self.request.locateResource(originatorPrincipalURL))
+                if not isinstance(self.originatorPrincipal, DirectoryCalendarPrincipalResource):
+                    log.error("Originator '%s' is not enabled for calendaring" % (originatorPrincipalURL,))
+                    raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "invalid-originator")))
+
                 if self.originatorPrincipal:
                     # Pick the first mailto cu address or the first other type
                     for item in self.originatorPrincipal.calendarUserAddresses():

Modified: CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/scheduling/scheduler.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -95,6 +95,7 @@
         if not hasattr(self.request, "extendedLogItems"):
             self.request.extendedLogItems = {}
         self.request.extendedLogItems["recipients"] = len(self.recipients)
+        self.request.extendedLogItems["cl"] = str(len(self.calendardata))
     
         # Do some extra authorization checks
         self.checkAuthorization()
@@ -113,6 +114,7 @@
         self.originator = originator
         self.recipients = recipients
         self.calendar = calendar
+        self.calendardata = str(self.calendar)
         self.internal_request = internal_request
 
         # Do some extra authorization checks
@@ -233,6 +235,7 @@
         # Parse the calendar object from the HTTP request stream
         try:
             self.calendar = (yield Component.fromIStream(self.request.stream))
+            self.calendardata = str(self.calendar)
         except:
             # FIXME: Bare except
             log.err("Error while handling %s: %s" % (self.method, Failure(),))
@@ -330,7 +333,7 @@
                         str("".join(["    %s\n" % (recipient,) for recipient in self.recipients])),
                         str(self.request.serverInstance),
                         str(self.method),
-                        str(self.calendar)
+                        self.calendardata,
                     )
                 )
 

Modified: CalendarServer/trunk/twistedcaldav/static.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/static.py	2009-10-27 19:11:38 UTC (rev 4643)
+++ CalendarServer/trunk/twistedcaldav/static.py	2009-10-27 19:16:27 UTC (rev 4644)
@@ -43,6 +43,7 @@
 
 from twisted.internet.defer import fail, succeed, inlineCallbacks, returnValue, maybeDeferred
 from twisted.python.failure import Failure
+from twisted.python.filepath import FilePath
 from twisted.web2 import responsecode, http, http_headers
 from twisted.web2.http import HTTPError, StatusResponse
 from twisted.web2.dav import davxml
@@ -388,12 +389,19 @@
         return self._propertyCollection
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
 
         similar = super(CalDAVFile, self).createSimilarFile(path)
 
         if isCalendarCollectionResource(self):
+
+            # Short-circuit stat with information we know to be true at this point
+            if isinstance(path, FilePath) and hasattr(self, "knownChildren"):
+                if os.path.basename(path.path) in self.knownChildren:
+                    path.existsCached = True
+                    path.isDirCached = False
+
             #
             # Override the dead property store
             #
@@ -564,6 +572,13 @@
         else:
             self._provisioned_file = True
 
+        # If the file already exists we can just exit here - there is no need to go further
+        if self.fp.exists():
+            return False
+
+        # At this point the original FilePath did not indicate an existing file, but we should
+        # recheck it to see if some other request sneaked in and already created/provisioned it
+
         fp = self.fp
 
         fp.restat(False)
@@ -588,10 +603,10 @@
                 # Check our status again, and re-raise if we're not a collection.
                 if not self.isCollection():
                     raise
-            fp.restat(False)
+            fp.changed()
         else:
             fp.open("w").close()
-            fp.restat(False)
+            fp.changed()
 
         return True
 
@@ -678,7 +693,7 @@
                             responsecode.INTERNAL_SERVER_ERROR,
                             "Unable to move calendar home."
                         ))
-                    child.fp.restat(False)
+                    child.fp.changed()
                     break
             else:
                 #
@@ -754,7 +769,7 @@
         return self.createSimilarFile(self.fp.child(name).path)
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
         else:
             similar = CalDAVFile(path, principalCollections=self.principalCollections())
@@ -831,7 +846,7 @@
         return True
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
         else:
             return CalDAVFile(path, principalCollections=self.principalCollections())
@@ -927,7 +942,7 @@
         return False
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
         else:
             return responsecode.NOT_FOUND
@@ -979,7 +994,7 @@
         return False
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
         else:
             return responsecode.NOT_FOUND
@@ -1010,7 +1025,7 @@
         self.parent = parent
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
         else:
             return DropBoxCollectionFile(path, self)
@@ -1024,7 +1039,7 @@
         CalDAVFile.__init__(self, path, principalCollections=parent.principalCollections())
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
         else:
             return DropBoxChildFile(path, self)
@@ -1039,7 +1054,7 @@
         assert self.fp.isfile() or not self.fp.exists()
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
         else:
             return responsecode.NOT_FOUND
@@ -1052,7 +1067,7 @@
         assert self.fp.isfile() or not self.fp.exists()
 
     def createSimilarFile(self, path):
-        if path == self.fp.path:
+        if self.comparePath(path):
             return self
         else:
             return responsecode.NOT_FOUND
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091027/01d07167/attachment-0001.html>


More information about the calendarserver-changes mailing list