[CalendarServer-changes] [10838] CalendarServer/branches/users/sagen/testing

source_changes at macosforge.org source_changes at macosforge.org
Mon Mar 4 13:56:46 PST 2013


Revision: 10838
          http://trac.calendarserver.org//changeset/10838
Author:   sagen at apple.com
Date:     2013-03-04 13:56:45 -0800 (Mon, 04 Mar 2013)
Log Message:
-----------
Merging from trunk

Modified Paths:
--------------
    CalendarServer/branches/users/sagen/testing/calendarserver/push/amppush.py
    CalendarServer/branches/users/sagen/testing/calendarserver/push/applepush.py
    CalendarServer/branches/users/sagen/testing/calendarserver/push/notifier.py
    CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_amppush.py
    CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_applepush.py
    CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_notifier.py
    CalendarServer/branches/users/sagen/testing/calendarserver/tap/caldav.py
    CalendarServer/branches/users/sagen/testing/calendarserver/tap/util.py
    CalendarServer/branches/users/sagen/testing/conf/caldavd-apple.plist
    CalendarServer/branches/users/sagen/testing/conf/caldavd-test.plist
    CalendarServer/branches/users/sagen/testing/twistedcaldav/datafilters/hiddeninstance.py
    CalendarServer/branches/users/sagen/testing/twistedcaldav/datafilters/test/test_hiddeninstances.py
    CalendarServer/branches/users/sagen/testing/twistedcaldav/resource.py
    CalendarServer/branches/users/sagen/testing/twistedcaldav/scheduling/icaldiff.py
    CalendarServer/branches/users/sagen/testing/twistedcaldav/scheduling/test/test_icaldiff.py
    CalendarServer/branches/users/sagen/testing/twistedcaldav/stdconfig.py

Property Changed:
----------------
    CalendarServer/branches/users/sagen/testing/
    CalendarServer/branches/users/sagen/testing/calendarserver/tools/
    CalendarServer/branches/users/sagen/testing/conf/


Property changes on: CalendarServer/branches/users/sagen/testing
___________________________________________________________________
Modified: svn:mergeinfo
   - /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:10827-10832
   + /CalendarServer/branches/config-separation:4379-4443
/CalendarServer/branches/egg-info-351:4589-4625
/CalendarServer/branches/generic-sqlstore:6167-6191
/CalendarServer/branches/new-store:5594-5934
/CalendarServer/branches/new-store-no-caldavfile:5911-5935
/CalendarServer/branches/new-store-no-caldavfile-2:5936-5981
/CalendarServer/branches/release/CalendarServer-4.3-dev:10180-10190,10192
/CalendarServer/branches/users/cdaboo/batchupload-6699:6700-7198
/CalendarServer/branches/users/cdaboo/cached-subscription-calendars-5692:5693-5702
/CalendarServer/branches/users/cdaboo/component-set-fixes:8130-8346
/CalendarServer/branches/users/cdaboo/directory-cache-on-demand-3627:3628-3644
/CalendarServer/branches/users/cdaboo/implicituidrace:8137-8141
/CalendarServer/branches/users/cdaboo/ischedule-dkim:9747-9979
/CalendarServer/branches/users/cdaboo/managed-attachments:9985-10145
/CalendarServer/branches/users/cdaboo/more-sharing-5591:5592-5601
/CalendarServer/branches/users/cdaboo/partition-4464:4465-4957
/CalendarServer/branches/users/cdaboo/pods:7297-7377
/CalendarServer/branches/users/cdaboo/pycalendar:7085-7206
/CalendarServer/branches/users/cdaboo/pycard:7227-7237
/CalendarServer/branches/users/cdaboo/queued-attendee-refreshes:7740-8287
/CalendarServer/branches/users/cdaboo/relative-config-paths-5070:5071-5105
/CalendarServer/branches/users/cdaboo/shared-calendars-5187:5188-5440
/CalendarServer/branches/users/cdaboo/timezones:7443-7699
/CalendarServer/branches/users/cdaboo/txn-debugging:8730-8743
/CalendarServer/branches/users/glyph/always-abort-txn-on-error:9958-9969
/CalendarServer/branches/users/glyph/case-insensitive-uid:8772-8805
/CalendarServer/branches/users/glyph/conn-limit:6574-6577
/CalendarServer/branches/users/glyph/contacts-server-merge:4971-5080
/CalendarServer/branches/users/glyph/dalify:6932-7023
/CalendarServer/branches/users/glyph/db-reconnect:6824-6876
/CalendarServer/branches/users/glyph/deploybuild:7563-7572
/CalendarServer/branches/users/glyph/digest-auth-redux:10624-10635
/CalendarServer/branches/users/glyph/disable-quota:7718-7727
/CalendarServer/branches/users/glyph/dont-start-postgres:6592-6614
/CalendarServer/branches/users/glyph/imip-and-admin-html:7866-7984
/CalendarServer/branches/users/glyph/ipv6-client:9054-9105
/CalendarServer/branches/users/glyph/linux-tests:6893-6900
/CalendarServer/branches/users/glyph/migrate-merge:8690-8713
/CalendarServer/branches/users/glyph/misc-portability-fixes:7365-7374
/CalendarServer/branches/users/glyph/more-deferreds-6:6322-6368
/CalendarServer/branches/users/glyph/more-deferreds-7:6369-6445
/CalendarServer/branches/users/glyph/multiget-delete:8321-8330
/CalendarServer/branches/users/glyph/new-export:7444-7485
/CalendarServer/branches/users/glyph/one-home-list-api:10048-10073
/CalendarServer/branches/users/glyph/oracle:7106-7155
/CalendarServer/branches/users/glyph/oracle-nulls:7340-7351
/CalendarServer/branches/users/glyph/other-html:8062-8091
/CalendarServer/branches/users/glyph/parallel-sim:8240-8251
/CalendarServer/branches/users/glyph/parallel-upgrade:8376-8400
/CalendarServer/branches/users/glyph/parallel-upgrade_to_1:8571-8583
/CalendarServer/branches/users/glyph/q:9560-9688
/CalendarServer/branches/users/glyph/queue-locking-and-timing:10204-10289
/CalendarServer/branches/users/glyph/quota:7604-7637
/CalendarServer/branches/users/glyph/sendfdport:5388-5424
/CalendarServer/branches/users/glyph/shared-pool-fixes:8436-8443
/CalendarServer/branches/users/glyph/shared-pool-take2:8155-8174
/CalendarServer/branches/users/glyph/sharedpool:6490-6550
/CalendarServer/branches/users/glyph/sharing-api:9192-9205
/CalendarServer/branches/users/glyph/skip-lonely-vtimezones:8524-8535
/CalendarServer/branches/users/glyph/sql-store:5929-6073
/CalendarServer/branches/users/glyph/subtransactions:7248-7258
/CalendarServer/branches/users/glyph/table-alias:8651-8664
/CalendarServer/branches/users/glyph/uidexport:7673-7676
/CalendarServer/branches/users/glyph/unshare-when-access-revoked:10562-10595
/CalendarServer/branches/users/glyph/use-system-twisted:5084-5149
/CalendarServer/branches/users/glyph/uuid-normalize:9268-9296
/CalendarServer/branches/users/glyph/xattrs-from-files:7757-7769
/CalendarServer/branches/users/sagen/applepush:8126-8184
/CalendarServer/branches/users/sagen/inboxitems:7380-7381
/CalendarServer/branches/users/sagen/locations-resources:5032-5051
/CalendarServer/branches/users/sagen/locations-resources-2:5052-5061
/CalendarServer/branches/users/sagen/purge_old_events:6735-6746
/CalendarServer/branches/users/sagen/resource-delegates-4038:4040-4067
/CalendarServer/branches/users/sagen/resource-delegates-4066:4068-4075
/CalendarServer/branches/users/sagen/resources-2:5084-5093
/CalendarServer/branches/users/wsanchez/transations:5515-5593
/CalendarServer/trunk:10827-10837

Modified: CalendarServer/branches/users/sagen/testing/calendarserver/push/amppush.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/calendarserver/push/amppush.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/calendarserver/push/amppush.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -16,9 +16,8 @@
 
 from calendarserver.push.util import PushScheduler
 from twext.python.log import Logger, LoggingMixIn
-from twisted.application.internet import StreamServerEndpointService
 from twisted.internet.defer import inlineCallbacks, returnValue
-from twisted.internet.endpoints import TCP4ClientEndpoint, TCP4ServerEndpoint
+from twisted.internet.endpoints import TCP4ClientEndpoint 
 from twisted.internet.protocol import Factory, ServerFactory
 from twisted.protocols import amp
 import time
@@ -28,6 +27,10 @@
 log = Logger()
 
 
+# Control socket message-routing constants
+PUSH_ROUTE = "push"
+
+
 # AMP Commands sent to server
 
 class SubscribeToID(amp.Command):
@@ -40,7 +43,7 @@
     response = [('status', amp.String())]
 
 
-# AMP Commands sent to client
+# AMP Commands sent to client (and forwarded to Master)
 
 class NotificationForID(amp.Command):
     arguments = [('id', amp.String()), ('dataChangedTimestamp', amp.Integer())]
@@ -49,29 +52,88 @@
 
 # Server classes
 
+class AMPPushForwardingFactory(Factory, LoggingMixIn):
 
-class AMPPushNotifierService(StreamServerEndpointService, LoggingMixIn):
+    def __init__(self, forwarder):
+        self.forwarder = forwarder
+
+    def buildProtocol(self, addr):
+        protocol = amp.AMP()
+        self.forwarder.protocols.append(protocol)
+        return protocol
+
+class AMPPushForwarder(LoggingMixIn):
     """
+    Runs in the slaves, forwards notifications to the master via AMP
+    """
+    def __init__(self, controlSocket):
+        self.protocols = []
+        controlSocket.addFactory(PUSH_ROUTE, AMPPushForwardingFactory(self))
+
+    @inlineCallbacks
+    def enqueue(self, id, dataChangedTimestamp=None):
+        if dataChangedTimestamp is None:
+            dataChangedTimestamp = int(time.time())
+        for protocol in self.protocols:
+            yield protocol.callRemote(NotificationForID, id=id,
+                dataChangedTimestamp=dataChangedTimestamp)
+
+
+
+class AMPPushMasterListeningProtocol(amp.AMP, LoggingMixIn):
+    """
+    Listens for notifications coming in over AMP from the slaves
+    """
+    def __init__(self, master):
+        super(AMPPushMasterListeningProtocol, self).__init__()
+        self.master = master
+
+    @NotificationForID.responder
+    def enqueueFromWorker(self, id, dataChangedTimestamp=None):
+        if dataChangedTimestamp is None:
+            dataChangedTimestamp = int(time.time())
+        self.master.enqueue(id, dataChangedTimestamp=dataChangedTimestamp)
+        return {"status" : "OK"}
+ 
+
+class AMPPushMasterListenerFactory(Factory, LoggingMixIn):
+
+    def __init__(self, master):
+        self.master = master
+
+    def buildProtocol(self, addr):
+        protocol = AMPPushMasterListeningProtocol(self.master)
+        return protocol
+
+
+class AMPPushMaster(LoggingMixIn):
+    """
     AMPPushNotifierService allows clients to use AMP to subscribe to,
     and receive, change notifications.
     """
 
-    @classmethod
-    def makeService(cls, settings, ignored, reactor=None):
-        return cls(settings, reactor=reactor)
-
-    def __init__(self, settings, reactor=None):
+    def __init__(self, controlSocket, parentService, port, enableStaggering,
+        staggerSeconds, reactor=None):
         if reactor is None:
             from twisted.internet import reactor
-        factory = AMPPushNotifierFactory(self)
-        endpoint = TCP4ServerEndpoint(reactor, settings["Port"])
-        super(AMPPushNotifierService, self).__init__(endpoint, factory)
+        from twisted.application.strports import service as strPortsService
+
+        if port:
+            # Service which listens for client subscriptions and sends
+            # notifications to them
+            strPortsService(str(port), AMPPushNotifierFactory(self),
+                reactor=reactor).setServiceParent(parentService)
+
+        if controlSocket is not None:
+            # Set up the listener which gets notifications from the slaves
+            controlSocket.addFactory(PUSH_ROUTE,
+                AMPPushMasterListenerFactory(self))
+
         self.subscribers = []
-        self.dataHost = settings["DataHost"]
 
-        if settings["EnableStaggering"]:
+        if enableStaggering:
             self.scheduler = PushScheduler(reactor, self.sendNotification,
-                staggerSeconds=settings["StaggerSeconds"])
+                staggerSeconds=staggerSeconds)
         else:
             self.scheduler = None
 
@@ -83,45 +145,32 @@
         self.log_debug("Removed subscriber")
         self.subscribers.remove(p)
 
-    def enqueue(self, id, dataChangedTimestamp=None):
+    def enqueue(self, pushKey, dataChangedTimestamp=None):
         """
-        Sends an AMP push notification to any clients subscribing to this id.
+        Sends an AMP push notification to any clients subscribing to this pushKey.
 
-        @param id: The identifier of the resource that was updated, including
+        @param pushKey: The identifier of the resource that was updated, including
             a prefix indicating whether this is CalDAV or CardDAV related.
-            The prefix is separated from the id with "|", e.g.:
 
-            "CalDAV|abc/def"
+            "/CalDAV/abc/def/"
 
-            The id is an opaque token as far as this code is concerned, and
-            is used in conjunction with the prefix and the server hostname
-            to build the actual key value that devices subscribe to.
-        @type id: C{str}
+        @type pushKey: C{str}
         @param dataChangedTimestamp: Timestamp (epoch seconds) for the data change
             which triggered this notification (Only used for unit tests)
             @type key: C{int}
         """
 
-        try:
-            protocol, id = id.split("|", 1)
-        except ValueError:
-            # id has no protocol, so we can't do anything with it
-            self.log_error("Notification id '%s' is missing protocol" % (id,))
-            return
-
         # Unit tests can pass this value in; otherwise it defaults to now
         if dataChangedTimestamp is None:
             dataChangedTimestamp = int(time.time())
 
-        id = "/%s/%s/%s/" % (protocol, self.dataHost, id)
-
         tokens = []
         for subscriber in self.subscribers:
-            token = subscriber.subscribedToID(id)
+            token = subscriber.subscribedToID(pushKey)
             if token is not None:
                 tokens.append(token)
         if tokens:
-            return self.scheduleNotifications(tokens, id, dataChangedTimestamp)
+            return self.scheduleNotifications(tokens, pushKey, dataChangedTimestamp)
 
 
     @inlineCallbacks

Modified: CalendarServer/branches/users/sagen/testing/calendarserver/push/applepush.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/calendarserver/push/applepush.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/calendarserver/push/applepush.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -88,7 +88,6 @@
         service.store = store
         service.providers = {}
         service.feedbacks = {}
-        service.dataHost = settings["DataHost"]
         service.purgeCall = None
         service.purgeIntervalSeconds = settings["SubscriptionPurgeIntervalSeconds"]
         service.purgeSeconds = settings["SubscriptionPurgeSeconds"]
@@ -177,31 +176,27 @@
 
 
     @inlineCallbacks
-    def enqueue(self, id, dataChangedTimestamp=None):
+    def enqueue(self, pushKey, dataChangedTimestamp=None):
         """
         Sends an Apple Push Notification to any device token subscribed to
-        this id.
+        this pushKey.
 
-        @param id: The identifier of the resource that was updated, including
+        @param pushKey: The identifier of the resource that was updated, including
             a prefix indicating whether this is CalDAV or CardDAV related.
-            The prefix is separated from the id with "|", e.g.:
 
-            "CalDAV|abc/def"
+            "/CalDAV/abc/def/"
 
-            The id is an opaque token as far as this code is concerned, and
-            is used in conjunction with the prefix and the server hostname
-            to build the actual key value that devices subscribe to.
-        @type id: C{str}
+        @type pushKey: C{str}
         @param dataChangedTimestamp: Timestamp (epoch seconds) for the data change
             which triggered this notification (Only used for unit tests)
         @type key: C{int}
         """
 
         try:
-            protocol, id = id.split("|", 1)
+            protocol = pushKey.split("/")[1]
         except ValueError:
-            # id has no protocol, so we can't do anything with it
-            self.log_error("Notification id '%s' is missing protocol" % (id,))
+            # pushKey has no protocol, so we can't do anything with it
+            self.log_error("Push key '%s' is missing protocol" % (pushKey,))
             return
 
         # Unit tests can pass this value in; otherwise it defaults to now
@@ -210,23 +205,22 @@
 
         provider = self.providers.get(protocol, None)
         if provider is not None:
-            key = "/%s/%s/%s/" % (protocol, self.dataHost, id)
 
             # Look up subscriptions for this key
             txn = self.store.newTransaction()
-            subscriptions = (yield txn.apnSubscriptionsByKey(key))
+            subscriptions = (yield txn.apnSubscriptionsByKey(pushKey))
             yield txn.commit()
 
             numSubscriptions = len(subscriptions)
             if numSubscriptions > 0:
                 self.log_debug("Sending %d APNS notifications for %s" %
-                    (numSubscriptions, key))
+                    (numSubscriptions, pushKey))
                 tokens = []
                 for token, uid in subscriptions:
                     if token and uid:
                         tokens.append(token)
                 if tokens:
-                    provider.scheduleNotifications(tokens, key, dataChangedTimestamp)
+                    provider.scheduleNotifications(tokens, pushKey, dataChangedTimestamp)
 
 
 

Modified: CalendarServer/branches/users/sagen/testing/calendarserver/push/notifier.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/calendarserver/push/notifier.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/calendarserver/push/notifier.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -24,8 +24,6 @@
 from twext.enterprise.dal.record import fromTable
 from twext.enterprise.queue import WorkItem
 from txdav.common.datastore.sql_tables import schema
-from twisted.application import service
-from twisted.python.reflect import namedClass
 
 
 log = Logger()
@@ -38,9 +36,9 @@
 
         # FIXME: Coalescing goes here?
 
-        pushService = self.transaction._pushService
-        if pushService is not None:
-            yield pushService.enqueue(self.pushID)
+        pushDistributor = self.transaction._pushDistributor
+        if pushDistributor is not None:
+            yield pushDistributor.enqueue(self.pushID)
 
 
 
@@ -130,7 +128,7 @@
     @inlineCallbacks
     def send(self, id):
         txn = self.store.newTransaction()
-        yield txn.enqueue(PushNotificationWork, pushID=id)
+        yield txn.enqueue(PushNotificationWork, pushID=self.pushKeyForId(id))
         yield txn.commit()
 
     def newNotifier(self, label="default", id=None, prefix=None):
@@ -153,22 +151,21 @@
 
 
 
-def getPubSubAPSConfiguration(id, config):
+def getPubSubAPSConfiguration(pushKey, config):
     """
-    Returns the Apple push notification settings specific to the notifier
-    ID, which includes a prefix that is either "CalDAV" or "CardDAV"
+    Returns the Apple push notification settings specific to the pushKey
     """
     try:
-        prefix, id = id.split("|", 1)
+        protocol, ignored = pushKey.split("|", 1)
     except ValueError:
-        # id has no prefix, so we can't look up APS config
+        # id has no protocol, so we can't look up APS config
         return None
 
     # If we are directly talking to apple push, advertise those settings
-    applePushSettings = config.Notifications.Services.ApplePushNotifier
+    applePushSettings = config.Notifications.Services.APNS
     if applePushSettings.Enabled:
         settings = {}
-        settings["APSBundleID"] = applePushSettings[prefix]["Topic"]
+        settings["APSBundleID"] = applePushSettings[protocol]["Topic"]
         if config.EnableSSL:
             url = "https://%s:%s/%s" % (config.ServerHostName, config.SSLPort,
                 applePushSettings.SubscriptionURL)
@@ -183,27 +180,26 @@
     return None
 
 
-class PushService(service.MultiService):
+class PushDistributor(object):
     """
-    A Service which passes along notifications to the protocol-specific subservices
+    Distributes notifications to the protocol-specific subservices
     """
 
-    @classmethod
-    def makeService(cls, settings, store):
-        multiService = cls()
-        for key, subSettings in settings.Services.iteritems():
-            if subSettings["Enabled"]:
-                subService = namedClass(subSettings["Service"]).makeService(
-                    subSettings, store)
-                subService.setServiceParent(multiService)
-                multiService.subServices.append(subService)            
-        return multiService
+    def __init__(self, observers):
+        """
+        @param observers: the list of observers to distribute pushKeys to
+        @type observers: C{list} 
+        """
+        # TODO: add an IPushObservers interface?
+        self.observers = observers 
 
-    def __init__(self):
-        service.MultiService.__init__(self)
-        self.subServices = []
-
     @inlineCallbacks
-    def enqueue(self, id):
-        for subService in self.subServices:
-            yield subService.enqueue(id)
+    def enqueue(self, pushKey):
+        """
+        Pass along enqueued pushKey to any observers
+
+        @param pushKey: the push key to distribute to the observers
+        @type pushKey: C{str}
+        """
+        for observer in self.observers:
+            yield observer.enqueue(pushKey)

Modified: CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_amppush.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_amppush.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_amppush.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -14,30 +14,18 @@
 # limitations under the License.
 ##
 
-from calendarserver.push.amppush import AMPPushNotifierService, AMPPushNotifierProtocol
+from calendarserver.push.amppush import AMPPushMaster, AMPPushNotifierProtocol
 from calendarserver.push.amppush import NotificationForID
 from twistedcaldav.test.util import TestCase
-from twisted.internet.defer import inlineCallbacks
 from twisted.internet.task import Clock
 
-class AMPPushNotifierServiceTests(TestCase):
+class AMPPushMasterTests(TestCase):
 
-    @inlineCallbacks
-    def test_AMPPushNotifierService(self):
+    def test_AMPPushMaster(self):
 
-        settings = {
-            "Service" : "calendarserver.push.amppush.AMPPushNotifierService",
-            "Enabled" : True,
-            "Port" : 62311,
-            "EnableStaggering" : True,
-            "StaggerSeconds" : 3,
-            "DataHost" : "localhost",
-        }
-
         # Set up the service
         clock = Clock()
-        service = (yield AMPPushNotifierService.makeService(settings,
-            None, reactor=clock))
+        service = AMPPushMaster(None, None, 0, True, 3, reactor=clock)
 
         self.assertEquals(service.subscribers, [])
 
@@ -69,7 +57,7 @@
         self.assertTrue(client3.subscribedToID("/CalDAV/localhost/user03/"))
 
         dataChangedTimestamp = 1354815999
-        service.enqueue("CalDAV|user01", dataChangedTimestamp=dataChangedTimestamp)
+        service.enqueue("/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
         self.assertEquals(len(client1.history), 0)
         self.assertEquals(len(client2.history), 0)
         self.assertEquals(len(client3.history), 0)
@@ -86,7 +74,7 @@
         client1.reset()
         client2.reset()
         client2.unsubscribe("token2", "/CalDAV/localhost/user01/")
-        service.enqueue("CalDAV|user01", dataChangedTimestamp=dataChangedTimestamp)
+        service.enqueue("/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
         self.assertEquals(len(client1.history), 0)
         clock.advance(1)
         self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
@@ -99,7 +87,7 @@
         client1.reset()
         client2.reset()
         client2.subscribe("token2", "/CalDAV/localhost/user01/")
-        service.enqueue("CalDAV|user01", dataChangedTimestamp=dataChangedTimestamp)
+        service.enqueue("/CalDAV/localhost/user01/", dataChangedTimestamp=dataChangedTimestamp)
         self.assertEquals(client1.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
         self.assertEquals(client2.history, [(NotificationForID, {'id': '/CalDAV/localhost/user01/', 'dataChangedTimestamp' : 1354815999})])
 

Modified: CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_applepush.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_applepush.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_applepush.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -38,12 +38,10 @@
     def test_ApplePushNotifierService(self):
 
         settings = {
-            "Service" : "calendarserver.push.applepush.ApplePushNotifierService",
             "Enabled" : True,
             "SubscriptionURL" : "apn",
             "SubscriptionPurgeSeconds" : 24 * 60 * 60,
             "SubscriptionPurgeIntervalSeconds" : 24 * 60 * 60,
-            "DataHost" : "calendars.example.com",
             "ProviderHost" : "gateway.push.apple.com",
             "ProviderPort" : 2195,
             "FeedbackHost" : "feedback.push.apple.com",
@@ -127,7 +125,7 @@
 
         # Notification arrives from calendar server
         dataChangedTimestamp = 1354815999
-        yield service.enqueue("CalDAV|user01/calendar",
+        yield service.enqueue("/CalDAV/calendars.example.com/user01/calendar/",
             dataChangedTimestamp=dataChangedTimestamp)
 
         # The notifications should be in the queue
@@ -167,7 +165,7 @@
         # Reset sent data
         providerConnector.transport.data = None
         # Send notification while service is connected
-        yield service.enqueue("CalDAV|user01/calendar")
+        yield service.enqueue("/CalDAV/calendars.example.com/user01/calendar/")
         clock.advance(1) # so that first push is sent
         self.assertEquals(len(providerConnector.transport.data), 183)
         # Reset sent data

Modified: CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_notifier.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_notifier.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/calendarserver/push/test/test_notifier.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -15,15 +15,12 @@
 ##
 
 from twistedcaldav.test.util import TestCase
+from calendarserver.push.notifier import PushDistributor, getPubSubAPSConfiguration
+from twisted.internet.defer import inlineCallbacks, succeed
 from twistedcaldav.config import ConfigDict
-from calendarserver.push.notifier import PushService
-from twisted.internet.defer import inlineCallbacks, succeed
-from twisted.application import service
 
-class StubService(service.Service):
-    def __init__(self, settings, store):
-        self.settings = settings
-        self.store = store
+class StubService(object):
+    def __init__(self):
         self.reset()
 
     def reset(self):
@@ -33,25 +30,42 @@
         self.history.append(id)
         return(succeed(None))
 
-    @classmethod
-    def makeService(cls, settings, store):
-        return cls(settings, store)
+class PushDistributorTests(TestCase):
 
-class PushServiceTests(TestCase):
-
     @inlineCallbacks
     def test_enqueue(self):
-        settings = ConfigDict({
-            "Services" : {
-                "Stub" : {
-                    "Service" : "calendarserver.push.test.test_notifier.StubService",
-                    "Enabled" : True,
-                    "Foo" : "Bar",
+        stub = StubService()
+        dist = PushDistributor([stub])
+        yield dist.enqueue("testing")
+        self.assertEquals(stub.history, ["testing"])
+
+    def test_getPubSubAPSConfiguration(self):
+        config = ConfigDict({
+            "EnableSSL" : True,
+            "ServerHostName" : "calendars.example.com",
+            "SSLPort" : 8443,
+            "HTTPPort" : 8008,
+            "Notifications" : {
+                "Services" : {
+                    "APNS" : {
+                        "CalDAV" : {
+                            "Topic" : "test topic",
+                        },
+                        "SubscriptionRefreshIntervalSeconds" : 42,
+                        "SubscriptionURL" : "apns",
+                        "Environment" : "prod",
+                        "Enabled" : True,
+                    },
                 },
             },
         })
-        svc = PushService.makeService(settings, None)
-        yield svc.enqueue("testing")
-        self.assertEquals(svc.subServices[0].history, ["testing"])
-
-
+        result = getPubSubAPSConfiguration("CalDAV|foo", config)
+        self.assertEquals(
+            result,
+            {
+                "SubscriptionRefreshIntervalSeconds": 42, 
+                "SubscriptionURL": "https://calendars.example.com:8443/apns", 
+                "APSBundleID": "test topic", 
+                "APSEnvironment": "prod"
+            }
+        )

Modified: CalendarServer/branches/users/sagen/testing/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/calendarserver/tap/caldav.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/calendarserver/tap/caldav.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -106,7 +106,9 @@
 from calendarserver.tap.util import oracleConnectorFromConfig
 from calendarserver.tap.cfgchild import ConfiguredChildSpawner
 from calendarserver.tools.util import checkDirectory
-from calendarserver.push.notifier import PushService
+from calendarserver.push.notifier import PushDistributor
+from calendarserver.push.amppush import AMPPushMaster, AMPPushForwarder
+from calendarserver.push.applepush import ApplePushNotifierService
 from twistedcaldav.scheduling.imip.inbound import MailRetriever
 
 try:
@@ -710,17 +712,56 @@
         """
         pool, txnFactory = getDBPool(config)
         store = storeFromConfig(config, txnFactory)
-        result = self.requestProcessingService(options, store)
+        logObserver = AMPCommonAccessLoggingObserver()
+        result = self.requestProcessingService(options, store, logObserver)
         directory = result.rootResource.getDirectory()
         if pool is not None:
             pool.setServiceParent(result)
 
+        if config.ControlSocket:
+            id = config.ControlSocket
+            self.log_info("Control via AF_UNIX: %s" % (id,))
+            endpointFactory = lambda reactor: UNIXClientEndpoint(
+                reactor, id)
+        else:
+            id = int(config.ControlPort)
+            self.log_info("Control via AF_INET: %d" % (id,))
+            endpointFactory = lambda reactor: TCP4ClientEndpoint(
+                reactor, "127.0.0.1", id)
+        controlSocketClient = ControlSocket()
+        class LogClient(AMP):
+            def startReceivingBoxes(self, sender):
+                super(LogClient, self).startReceivingBoxes(sender)
+                logObserver.addClient(self)
+        f = Factory()
+        f.protocol = LogClient
+        controlSocketClient.addFactory(_LOG_ROUTE, f)
+        from txdav.common.datastore.sql import CommonDataStore as SQLStore
+        if isinstance(store, SQLStore):
+            def queueMasterAvailable(connectionFromMaster):
+                store.queuer = store.queuer.transferProposalCallbacks(connectionFromMaster)
+            queueFactory = QueueWorkerFactory(store.newTransaction, schema,
+                                              queueMasterAvailable)
+            controlSocketClient.addFactory(_QUEUE_ROUTE, queueFactory)
+        controlClient = ControlSocketConnectingService(
+            endpointFactory, controlSocketClient
+        )
+        controlClient.setServiceParent(result)
+
         # Optionally set up push notifications
+        pushDistributor = None
         if config.Notifications.Enabled:
-            pushService = PushService.makeService(config.Notifications, store)
-            pushService.setServiceParent(result)
-        else:
-            pushService = None
+            observers = []
+            if config.Notifications.Services.APNS.Enabled:
+                pushSubService = ApplePushNotifierService.makeService(
+                    config.Notifications.Services.APNS, store)
+                observers.append(pushSubService)
+                pushSubService.setServiceParent(result)
+            if config.Notifications.Services.AMP.Enabled:
+                pushSubService = AMPPushForwarder(controlSocketClient)
+                observers.append(pushSubService)
+            if observers:
+                pushDistributor = PushDistributor(observers)
 
         # Optionally set up mail retrieval
         if config.Scheduling.iMIP.Enabled:
@@ -731,7 +772,7 @@
             mailRetriever = None
 
         def decorateTransaction(txn):
-            txn._pushService = pushService
+            txn._pushDistributor = pushDistributor
             txn._rootResource = result.rootResource
             txn._mailRetriever = mailRetriever
 
@@ -762,7 +803,7 @@
         return result
 
 
-    def requestProcessingService(self, options, store):
+    def requestProcessingService(self, options, store, logObserver):
         """
         Make a service that will actually process HTTP requests.
 
@@ -786,52 +827,8 @@
         #
         self.log_info("Setting up service")
 
-        bonusServices = []
-
-        if config.ProcessType == "Slave":
-            logObserver = AMPCommonAccessLoggingObserver()
-
-            if config.ControlSocket:
-                id = config.ControlSocket
-                self.log_info("Control via AF_UNIX: %s" % (id,))
-                endpointFactory = lambda reactor: UNIXClientEndpoint(
-                    reactor, id)
-            else:
-                id = int(config.ControlPort)
-                self.log_info("Control via AF_INET: %d" % (id,))
-                endpointFactory = lambda reactor: TCP4ClientEndpoint(
-                    reactor, "127.0.0.1", id)
-            controlSocketClient = ControlSocket()
-            class LogClient(AMP):
-                def startReceivingBoxes(self, sender):
-                    super(LogClient, self).startReceivingBoxes(sender)
-                    logObserver.addClient(self)
-            f = Factory()
-            f.protocol = LogClient
-            controlSocketClient.addFactory(_LOG_ROUTE, f)
-            from txdav.common.datastore.sql import CommonDataStore as SQLStore
-            if isinstance(store, SQLStore):
-                def queueMasterAvailable(connectionFromMaster):
-                    store.queuer = store.queuer.transferProposalCallbacks(connectionFromMaster)
-                queueFactory = QueueWorkerFactory(store.newTransaction, schema,
-                                                  queueMasterAvailable)
-                controlSocketClient.addFactory(_QUEUE_ROUTE, queueFactory)
-            controlClient = ControlSocketConnectingService(
-                endpointFactory, controlSocketClient
-            )
-            bonusServices.append(controlClient)
-        elif config.ProcessType == "Single":
-            # Make sure no old socket files are lying around.
-            self.deleteStaleSocketFiles()
-            logObserver = RotatingFileAccessLoggingObserver(
-                config.AccessLogFile,
-            )
-
         self.log_info("Configuring access log observer: %s" % (logObserver,))
-
         service = CalDAVService(logObserver)
-        for bonus in bonusServices:
-            bonus.setServiceParent(service)
 
         rootResource = getRootResource(config, store, additional)
         service.rootResource = rootResource
@@ -1018,13 +1015,57 @@
         Create a service to be used in a single-process, stand-alone
         configuration.
         """
-        def slaveSvcCreator(pool, store):
-            return self.requestProcessingService(options, store)
+        def slaveSvcCreator(pool, store, logObserver):
+            result = self.requestProcessingService(options, store, logObserver)
 
+            # Optionally set up push notifications
+            pushDistributor = None
+            if config.Notifications.Enabled:
+                observers = []
+                if config.Notifications.Services.APNS.Enabled:
+                    pushSubService = ApplePushNotifierService.makeService(
+                        config.Notifications.Services.APNS, store)
+                    observers.append(pushSubService)
+                    pushSubService.setServiceParent(result)
+                if config.Notifications.Services.AMP.Enabled:
+                    pushSubService = AMPPushMaster(None, result,
+                        config.Notifications.Services.AMP.Port,
+                        config.Notifications.Services.AMP.EnableStaggering,
+                        config.Notifications.Services.AMP.StaggerSeconds
+                        )
+                    observers.append(pushSubService)
+                if observers:
+                    pushDistributor = PushDistributor(observers)
+
+            # Optionally set up mail retrieval
+            if config.Scheduling.iMIP.Enabled:
+                directory = result.rootResource.getDirectory()
+                mailRetriever = MailRetriever(store, directory,
+                    config.Scheduling.iMIP.Receiving)
+                mailRetriever.setServiceParent(result)
+            else:
+                mailRetriever = None
+
+            def decorateTransaction(txn):
+                txn._pushDistributor = pushDistributor
+                txn._rootResource = result.rootResource
+                txn._mailRetriever = mailRetriever
+
+            store.callWithNewTransactions(decorateTransaction)
+
+            return result 
+
         uid, gid = getSystemIDs(config.UserName, config.GroupName)
-        return self.storageService(slaveSvcCreator, uid=uid, gid=gid)
 
+        # Make sure no old socket files are lying around.
+        self.deleteStaleSocketFiles()
+        logObserver = RotatingFileAccessLoggingObserver(
+            config.AccessLogFile,
+        )
 
+        return self.storageService(slaveSvcCreator, logObserver, uid=uid, gid=gid)
+
+
     def makeService_Utility(self, options):
         """
         Create a service to be used in a command-line utility
@@ -1033,14 +1074,14 @@
         When created, that service will have access to the storage facilities.
         """
 
-        def toolServiceCreator(pool, store):
+        def toolServiceCreator(pool, store, ignored):
             return config.UtilityServiceClass(store)
 
         uid, gid = getSystemIDs(config.UserName, config.GroupName)
-        return self.storageService(toolServiceCreator, uid=uid, gid=gid)
+        return self.storageService(toolServiceCreator, None, uid=uid, gid=gid)
 
 
-    def storageService(self, createMainService, uid=None, gid=None):
+    def storageService(self, createMainService, logObserver, uid=None, gid=None):
         """
         If necessary, create a service to be started used for storage; for
         example, starting a database backend.  This service will then start the
@@ -1079,7 +1120,7 @@
                                     maxConnections=config.MaxDBConnectionsPerPool)
                 cp.setServiceParent(ms)
                 store = storeFromConfig(config, cp.connection)
-                mainService = createMainService(cp, store)
+                mainService = createMainService(cp, store, logObserver)
                 if config.SharedConnectionPool:
                     dispenser = ConnectionDispenser(cp)
                 else:
@@ -1153,7 +1194,7 @@
                 raise UsageError("Unknown database type %r" (config.DBType,))
         else:
             store = storeFromConfig(config, None)
-            return createMainService(None, store)
+            return createMainService(None, store, logObserver)
 
 
     def makeService_Combined(self, options):
@@ -1196,6 +1237,17 @@
 
         controlSocket = ControlSocket()
         controlSocket.addFactory(_LOG_ROUTE, logger)
+
+        # Optionally set up AMPPushMaster
+        if config.Notifications.Enabled and config.Notifications.Services.AMP.Enabled:
+            ampSettings = config.Notifications.Services.AMP
+            AMPPushMaster(
+                controlSocket,
+                s,
+                ampSettings["Port"],
+                ampSettings["EnableStaggering"],
+                ampSettings["StaggerSeconds"]
+            )
         if config.ControlSocket:
             controlSocketService = GroupOwnedUNIXServer(
                 gid, config.ControlSocket, controlSocket, mode=0660
@@ -1338,7 +1390,7 @@
         # to), and second, the service which does an upgrade from the
         # filesystem to the database (if that's necessary, and there is
         # filesystem data in need of upgrading).
-        def spawnerSvcCreator(pool, store):
+        def spawnerSvcCreator(pool, store, ignored):
             from twisted.internet import reactor
             pool = PeerConnectionPool(reactor, store.newTransaction,
                                       7654, schema)

Modified: CalendarServer/branches/users/sagen/testing/calendarserver/tap/util.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/calendarserver/tap/util.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/calendarserver/tap/util.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -654,7 +654,7 @@
     #
     # Apple Push Notification Subscriptions
     #
-    apnConfig = config.Notifications.Services["ApplePushNotifier"]
+    apnConfig = config.Notifications.Services.APNS
     if apnConfig.Enabled:
         log.info("Setting up APNS resource at /%s" %
             (apnConfig["SubscriptionURL"],))


Property changes on: CalendarServer/branches/users/sagen/testing/calendarserver/tools
___________________________________________________________________
Modified: svn:ignore
   - *.pyc

   + *.pyc
config.py.edited



Property changes on: CalendarServer/branches/users/sagen/testing/conf
___________________________________________________________________
Modified: svn:ignore
   - caldavd-dev.plist

   + caldavd-dev.plist
caldavd-apns.plist
caldavd-corpds-ldap.plist.latest
caldavd-included.plist
caldavd-ldap-separate-groupcacher-memcache.plist
caldavd-server.plist
caldavd-with-imip.plist


Modified: CalendarServer/branches/users/sagen/testing/conf/caldavd-apple.plist
===================================================================
--- CalendarServer/branches/users/sagen/testing/conf/caldavd-apple.plist	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/conf/caldavd-apple.plist	2013-03-04 21:56:45 UTC (rev 10838)
@@ -355,30 +355,6 @@
 
       <key>Services</key>
       <dict>
-        <key>XMPPNotifier</key>
-        <dict>
-          <!-- XMPP notification service -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.XMPPNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-
-          <!-- XMPP host and port to contact -->
-          <key>Host</key>
-          <string>xmpp.host.name</string>
-          <key>Port</key>
-          <integer>5222</integer>
-
-          <!-- Jabber ID and password for the server -->
-          <key>JID</key>
-          <string>jid at xmpp.host.name/resource</string>
-          <key>Password</key>
-          <string>password_goes_here</string>
-
-          <!-- PubSub service address -->
-          <key>ServiceAddress</key>
-          <string>pubsub.xmpp.host.name</string>
-        </dict>
       </dict>
     </dict>
 

Modified: CalendarServer/branches/users/sagen/testing/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/users/sagen/testing/conf/caldavd-test.plist	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/conf/caldavd-test.plist	2013-03-04 21:56:45 UTC (rev 10838)
@@ -656,19 +656,11 @@
       <key>CoalesceSeconds</key>
       <integer>3</integer>
 
-      <key>InternalNotificationHost</key>
-      <string>localhost</string>
-
-      <key>InternalNotificationPort</key>
-      <integer>62309</integer>
-
       <key>Services</key>
       <dict>
 
-        <key>AMPNotifier</key>
+        <key>AMP</key>
         <dict>
-          <key>Service</key>
-          <string>calendarserver.push.amppush.AMPPushNotifierService</string>
           <key>Enabled</key>
           <false/>
           <key>Port</key>
@@ -679,81 +671,6 @@
           <integer>3</integer>
         </dict>
 
-        <key>SimpleLineNotifier</key>
-        <dict>
-          <!-- Simple line notification service (for testing) -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.SimpleLineNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-          <key>Port</key>
-          <integer>62308</integer>
-        </dict>
-
-        <key>XMPPNotifier</key>
-        <dict>
-          <!-- XMPP notification service -->
-          <key>Service</key>
-          <string>twistedcaldav.notify.XMPPNotifierService</string>
-          <key>Enabled</key>
-          <false/>
-
-          <!-- XMPP host and port to contact -->
-          <key>Host</key>
-          <string>xmpp.host.name</string>
-          <key>Port</key>
-          <integer>5222</integer>
-
-          <!-- Jabber ID and password for the server -->
-          <key>JID</key>
-          <string>jid at xmpp.host.name/resource</string>
-          <key>Password</key>
-          <string>password_goes_here</string>
-
-          <!-- PubSub service address -->
-          <key>ServiceAddress</key>
-          <string>pubsub.xmpp.host.name</string>
-
-          <!-- Apple-specific config -->
-          <key>CalDAV</key>
-          <dict>
-              <key>APSBundleID</key>
-              <string></string>
-              <key>SubscriptionURL</key>
-              <string></string>
-          </dict>
-          <key>CardDAV</key>
-          <dict>
-              <key>APSBundleID</key>
-              <string></string>
-              <key>SubscriptionURL</key>
-              <string></string>
-          </dict>
-
-          <key>NodeConfiguration</key>
-          <dict>
-            <key>pubsub#deliver_payloads</key>
-            <string>1</string>
-            <key>pubsub#persist_items</key>
-            <string>1</string>
-          </dict>
-
-          <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
-          <key>KeepAliveSeconds</key>
-          <integer>120</integer>
-
-          <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
-          <key>HeartbeatMinutes</key>
-          <integer>30</integer>
-
-          <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
-          <key>AllowedJIDs</key>
-          <array>
-            <!--
-            <string>*.example.com</string>
-             -->
-          </array>
-        </dict>
       </dict>
     </dict>
 

Modified: CalendarServer/branches/users/sagen/testing/twistedcaldav/datafilters/hiddeninstance.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/twistedcaldav/datafilters/hiddeninstance.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/twistedcaldav/datafilters/hiddeninstance.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -33,13 +33,11 @@
 
         @param ical: iCalendar object
         @type ical: L{Component} or C{str}
-        
+
         @return: L{Component} for the filtered calendar data
         """
-        
+
         master = ical.masterComponent()
-        if master is None:
-            return ical
         for component in tuple(ical.subcomponents()):
             if component.name() in ignoredComponents:
                 continue
@@ -49,15 +47,17 @@
             if component.hasProperty(Component.HIDDEN_INSTANCE_PROPERTY):
                 rid = component.getRecurrenceIDUTC()
                 ical.removeComponent(component)
-                
+
                 # Add EXDATE and try to preserve same timezone as DTSTART
-                dtstart = master.getProperty("DTSTART")
-                if dtstart is not None and not dtstart.value().isDateOnly() and dtstart.value().local():
-                    rid.adjustTimezone(dtstart.value().getTimezone())
-                master.addProperty(Property("EXDATE", [rid,]))
-        
+                if master is not None:
+                    dtstart = master.getProperty("DTSTART")
+                    if dtstart is not None and not dtstart.value().isDateOnly() and dtstart.value().local():
+                        rid.adjustTimezone(dtstart.value().getTimezone())
+                    master.addProperty(Property("EXDATE", [rid, ]))
+
         return ical
-   
+
+
     def merge(self, icalnew, icalold):
         """
         Private event merging does not happen

Modified: CalendarServer/branches/users/sagen/testing/twistedcaldav/datafilters/test/test_hiddeninstances.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/twistedcaldav/datafilters/test/test_hiddeninstances.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/twistedcaldav/datafilters/test/test_hiddeninstances.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -21,7 +21,7 @@
 class HiddenInstanceFilterTest (twistedcaldav.test.util.TestCase):
 
     def test_public_default(self):
-        
+
         data = (
             (
                 "Nothing hidden, no recurrence",
@@ -378,7 +378,7 @@
 """,
             ),
             (
-                "No master, one hidden - not really",
+                "No master, one hidden",
                 """BEGIN:VCALENDAR
 VERSION:2.0
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
@@ -415,21 +415,11 @@
 ATTENDEE:mailto:user2 at example.com
 ORGANIZER;CN=User 01:mailto:user1 at example.com
 END:VEVENT
-BEGIN:VEVENT
-UID:12345-67890
-RECURRENCE-ID:20080603T120000Z
-DTSTART:20080603T123000Z
-DTEND:20080601T133000Z
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ORGANIZER;CN=User 01:mailto:user1 at example.com
-X-CALENDARSERVER-HIDDEN-INSTANCE:T
-END:VEVENT
 END:VCALENDAR
 """,
             ),
         )
-        
+
         for title, test, result in data:
             ics = Component.fromString(test.replace("\n", "\r\n"))
             self.assertEqual(str(HiddenInstanceFilter().filter(ics)), result.replace("\n", "\r\n"), msg="Failed: %s" % (title,))

Modified: CalendarServer/branches/users/sagen/testing/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/twistedcaldav/resource.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/twistedcaldav/resource.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -2320,7 +2320,7 @@
 
         elif qname == (customxml.calendarserver_namespace, "push-transports"):
 
-            if config.Notifications.Services.ApplePushNotifier.Enabled:
+            if config.Notifications.Services.APNS.Enabled:
 
                 nodeName = (yield self._newStoreHome.nodeName())
                 if nodeName:
@@ -2355,8 +2355,8 @@
             returnValue(None)
 
         elif qname == (customxml.calendarserver_namespace, "pushkey"):
-            if (config.Notifications.Services.AMPNotifier.Enabled or
-                config.Notifications.Services.ApplePushNotifier.Enabled):
+            if (config.Notifications.Services.AMP.Enabled or
+                config.Notifications.Services.APNS.Enabled):
                 nodeName = (yield self._newStoreHome.nodeName())
                 if nodeName:
                     returnValue(customxml.PubSubXMPPPushKeyProperty(nodeName))

Modified: CalendarServer/branches/users/sagen/testing/twistedcaldav/scheduling/icaldiff.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/twistedcaldav/scheduling/icaldiff.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/twistedcaldav/scheduling/icaldiff.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -339,11 +339,9 @@
                         changedRids.append(rid.getText() if rid else "")
 
                     # When a master component is present we keep the missing override in place but mark it as hidden.
-                    # When no master is present we remove the override,
-                    if exdatesnew is not None:
-                        overridden.replaceProperty(Property(Component.HIDDEN_INSTANCE_PROPERTY, "T"))
-                    else:
-                        returnCalendar.removeComponent(overridden)
+                    # When no master is present we now do the same so we can track updates to the override correctly.
+                    overridden.replaceProperty(Property(Component.HIDDEN_INSTANCE_PROPERTY, "T"))
+
                 else:
                     # We used to generate a 403 here - but instead we now ignore this error and let the server data
                     # override the client

Modified: CalendarServer/branches/users/sagen/testing/twistedcaldav/scheduling/test/test_icaldiff.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/twistedcaldav/scheduling/test/test_icaldiff.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/twistedcaldav/scheduling/test/test_icaldiff.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -2014,6 +2014,17 @@
 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
 BEGIN:VEVENT
 UID:12345-67890
+RECURRENCE-ID:20080601T120000Z
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE;PARTSTAT=DECLINED;RSVP=TRUE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+TRANSP:TRANSPARENT
+X-CALENDARSERVER-HIDDEN-INSTANCE:T
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
 RECURRENCE-ID:20080604T120000Z
 DTSTART:20080604T130000Z
 DTEND:20080604T140000Z

Modified: CalendarServer/branches/users/sagen/testing/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/sagen/testing/twistedcaldav/stdconfig.py	2013-03-04 20:05:53 UTC (rev 10837)
+++ CalendarServer/branches/users/sagen/testing/twistedcaldav/stdconfig.py	2013-03-04 21:56:45 UTC (rev 10838)
@@ -736,14 +736,12 @@
         "CoalesceSeconds" : 3,
 
         "Services" : {
-            "ApplePushNotifier" : {
-                "Service" : "calendarserver.push.applepush.ApplePushNotifierService",
+            "APNS" : {
                 "Enabled" : False,
                 "SubscriptionURL" : "apns",
                 "SubscriptionRefreshIntervalSeconds" : 2 * 24 * 60 * 60, # How often the client should re-register (2 days)
                 "SubscriptionPurgeIntervalSeconds" : 12 * 60 * 60, # How often a purge is done (12 hours)
                 "SubscriptionPurgeSeconds" : 14 * 24 * 60 * 60, # How old a subscription must be before it's purged (14 days)
-                "DataHost" : "",
                 "ProviderHost" : "gateway.push.apple.com",
                 "ProviderPort" : 2195,
                 "FeedbackHost" : "feedback.push.apple.com",
@@ -767,13 +765,11 @@
                     "Topic" : "",
                 },
             },
-            "AMPNotifier" : {
-                "Service" : "calendarserver.push.amppush.AMPPushNotifierService",
+            "AMP" : {
                 "Enabled" : False,
                 "Port" : 62311,
                 "EnableStaggering" : False,
                 "StaggerSeconds" : 3,
-                "DataHost" : "",
             },
         }
     },
@@ -1354,16 +1350,9 @@
     else:
         configDict.Notifications["Enabled"] = False
 
-    for _ignore_key, service in configDict.Notifications["Services"].iteritems():
+    for key, service in configDict.Notifications["Services"].iteritems():
 
-        if (
-            service["Service"] == "calendarserver.push.applepush.ApplePushNotifierService" and
-            service["Enabled"]
-        ):
-            # The default for apple push DataHost is ServerHostName
-            if service["DataHost"] == "":
-                service["DataHost"] = configDict.ServerHostName
-
+        if (key == "APNS" and service["Enabled"]):
             # Retrieve APN topics from certificates if not explicitly set
             for protocol, accountName in (
                 ("CalDAV", "apns:com.apple.calendar"),
@@ -1392,16 +1381,8 @@
                     # The password doesn't exist in the keychain.
                     log.info("%s APN certificate passphrase not found in keychain" % (protocol,))
 
-        if (
-            service["Service"] == "calendarserver.push.amppush.AMPPushNotifierService" and
-            service["Enabled"]
-        ):
-            # The default for apple push DataHost is ServerHostName
-            if service["DataHost"] == "":
-                service["DataHost"] = configDict.ServerHostName
 
 
-
 def _updateScheduling(configDict, reloading=False):
     #
     # Scheduling
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130304/9e428569/attachment-0001.html>


More information about the calendarserver-changes mailing list