Skip to content

Commit

Permalink
Added the ability to specify an arbitrary applicationId when register…
Browse files Browse the repository at this point in the history
…ing services. This way you don't have to use multiple push broker instances, or subclass notification types to achieve multiple different mobile apps
  • Loading branch information
Redth committed Aug 8, 2013
1 parent b51e0a6 commit ad96e0c
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 50 deletions.
7 changes: 6 additions & 1 deletion PushSharp.Amazon.Adm/AdmPushBrokerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ namespace PushSharp
{
public static class AdmPushBrokerExtensions
{
public static void RegisterAdmService(this PushBroker broker, AdmPushChannelSettings channelSettings, PushServiceSettings serviceSettings = null)
public static void RegisterAdmService(this PushBroker broker, AdmPushChannelSettings channelSettings, IPushServiceSettings serviceSettings = null)
{
RegisterAdmService (broker, channelSettings, null, serviceSettings);
}

public static void RegisterAdmService(this PushBroker broker, AdmPushChannelSettings channelSettings, string applicationId, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<AdmNotification>(new AdmPushService(new AdmPushChannelFactory(), channelSettings, serviceSettings));
}
Expand Down
7 changes: 6 additions & 1 deletion PushSharp.Android/C2dm/C2dmPushBrokerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ public static class C2dmPushBrokerExtensions
{
public static void RegisterC2dmService(this PushBroker broker, C2dmPushChannelSettings channelSettings, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<C2dmNotification>(new C2dmPushService(channelSettings, serviceSettings));
RegisterC2dmService (broker, channelSettings, null, serviceSettings);
}

public static void RegisterC2dmService(this PushBroker broker, C2dmPushChannelSettings channelSettings, string applicationId, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<C2dmNotification>(new C2dmPushService(channelSettings, serviceSettings), applicationId);
}

public static C2dmNotification C2dmNotification(this PushBroker broker)
Expand Down
9 changes: 7 additions & 2 deletions PushSharp.Android/Gcm/GcmPushBrokerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ namespace PushSharp
{
public static class GcmPushBrokerExtensions
{
public static void RegisterGcmService(this PushBroker broker, GcmPushChannelSettings channelSettings, PushServiceSettings serviceSettings = null)
public static void RegisterGcmService(this PushBroker broker, GcmPushChannelSettings channelSettings, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<GcmNotification>(new GcmPushService(new GcmPushChannelFactory(), channelSettings, serviceSettings));
RegisterGcmService (broker, channelSettings, null, serviceSettings);
}

public static void RegisterGcmService(this PushBroker broker, GcmPushChannelSettings channelSettings, string applicationId, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<GcmNotification>(new GcmPushService(new GcmPushChannelFactory(), channelSettings, serviceSettings), applicationId);
}

public static GcmNotification GcmNotification(this PushBroker broker)
Expand Down
7 changes: 6 additions & 1 deletion PushSharp.Apple/ApplePushBrokerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ public static class ApplePushBrokerExtensions
{
public static void RegisterAppleService(this PushBroker broker, ApplePushChannelSettings channelSettings, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<AppleNotification>(new ApplePushService(channelSettings, serviceSettings));
RegisterAppleService (broker, channelSettings, null, serviceSettings);
}

public static void RegisterAppleService(this PushBroker broker, ApplePushChannelSettings channelSettings, string applicationId, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<AppleNotification>(new ApplePushService(channelSettings, serviceSettings), applicationId);
}

public static AppleNotification AppleNotification(this PushBroker broker)
Expand Down
7 changes: 6 additions & 1 deletion PushSharp.Blackberry/BlackberryPushBrokerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ public static class BISPushBrokerExtensions
{
public static void RegisterBlackberryService(this PushBroker broker, BlackberryPushChannelSettings channelSettings, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<BlackberryNotification>(new BlackberryPushService(channelSettings, serviceSettings));
RegisterBlackberryService (broker, channelSettings, null, serviceSettings);
}

public static void RegisterBlackberryService(this PushBroker broker, BlackberryPushChannelSettings channelSettings, string applicationId = null, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<BlackberryNotification>(new BlackberryPushService(channelSettings, serviceSettings), applicationId);
}

public static BlackberryNotification BlackberryNotification(this PushBroker broker)
Expand Down
233 changes: 205 additions & 28 deletions PushSharp.Core/PushBroker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,31 @@ public class PushBroker : IDisposable
public event DeviceSubscriptionExpiredDelegate OnDeviceSubscriptionExpired;
public event DeviceSubscriptionChangedDelegate OnDeviceSubscriptionChanged;

private Dictionary<Type, List<IPushService>> registeredServices;
readonly object serviceRegistrationsLock = new object();

List<ServiceRegistration> serviceRegistrations;

public PushBroker()
{
registeredServices = new Dictionary<Type, List<IPushService>>();
serviceRegistrations = new List<ServiceRegistration> ();
}

public void RegisterService<TPushNotification>(IPushService pushService) where TPushNotification : Notification
/// <summary>
/// Registers the service to be eligible to handle queued notifications of the specified type
/// </summary>
/// <param name="pushService">Push service to be registered</param>
/// <param name="applicationId">Arbitrary Application identifier to register this service with. When queueing notifications you can specify the same Application identifier to ensure they get queued to the same service instance </param>
/// <param name="raiseErrorOnDuplicateRegistrations">If set to <c>true</c> raises an error if there is an existing registration for the given notification type.</param>
/// <typeparam name="TPushNotification">Type of notifications to register the service for</typeparam>
public void RegisterService<TPushNotification>(IPushService pushService, string applicationId, bool raiseErrorOnDuplicateRegistrations = true) where TPushNotification : Notification
{
var pushNotificationType = typeof (TPushNotification);
if (raiseErrorOnDuplicateRegistrations && GetRegistrations<TPushNotification> (applicationId).Any ())
throw new InvalidOperationException ("There's already a service registered to handle " + typeof(TPushNotification).Name + " notification types for the Application Id: " + (applicationId ?? "[ANY]. If you want to register the service anyway, pass in the raiseErrorOnDuplicateRegistrations=true parameter to this method."));

if (registeredServices.ContainsKey(pushNotificationType))
registeredServices[pushNotificationType].Add(pushService);
else
registeredServices.Add(pushNotificationType, new List<IPushService>() { pushService });
var registration = ServiceRegistration.Create<TPushNotification> (pushService, applicationId);

lock (serviceRegistrationsLock)
serviceRegistrations.Add (registration);

pushService.OnChannelCreated += OnChannelCreated;
pushService.OnChannelDestroyed += OnChannelDestroyed;
Expand All @@ -46,40 +56,207 @@ public void RegisterService<TPushNotification>(IPushService pushService) where T
pushService.OnDeviceSubscriptionChanged += OnDeviceSubscriptionChanged;
}

/// <summary>
/// Registers the service to be eligible to handle queued notifications of the specified type
/// </summary>
/// <param name="pushService">Push service to be registered</param>
/// <param name="raiseErrorOnDuplicateRegistrations">If set to <c>true</c> raises an error if there is an existing registration for the given notification type.</param>
/// <typeparam name="TPushNotification">Type of notifications to register the service for</typeparam>
public void RegisterService<TPushNotification>(IPushService pushService, bool raiseErrorOnDuplicateRegistrations = true) where TPushNotification : Notification
{
RegisterService<TPushNotification> (pushService, null, raiseErrorOnDuplicateRegistrations);
}

/// <summary>
/// Queues a notification to ALL SERVICES registered for this type of notification
/// </summary>
/// <param name="notification">Notification.</param>
/// <typeparam name="TPushNotification">The 1st type parameter.</typeparam>
public void QueueNotification<TPushNotification>(TPushNotification notification) where TPushNotification : Notification
{
var pushNotificationType = notification.GetType ();
var services = GetRegistrations<TPushNotification> ();

if (registeredServices.ContainsKey(pushNotificationType))
registeredServices[pushNotificationType].ForEach(pushService => pushService.QueueNotification(notification));
else
foreach (var s in services)
s.QueueNotification (notification);
}

/// <summary>
/// Queues the notification to all services registered for this type of notification with a matching applicationId
/// </summary>
/// <param name="notification">Notification</param>
/// <param name="applicationId">Application identifier</param>
/// <typeparam name="TPushNotification">Type of Notification</typeparam>
public void QueueNotification<TPushNotification>(TPushNotification notification, string applicationId) where TPushNotification : Notification
{
var services = GetRegistrations<TPushNotification> (applicationId);

if (services == null || !services.Any())
throw new IndexOutOfRangeException("There are no Registered Services that handle this type of Notification");

foreach (var s in services)
s.QueueNotification (notification);
}

/// <summary>
/// Gets all the registered services
/// </summary>
/// <returns>The registered services</returns>
/// <typeparam name="TNotification">Type of notification</typeparam>
public IEnumerable<IPushService> GetAllRegistrations()
{
lock (serviceRegistrationsLock)
return from s in serviceRegistrations select s.Service;
}

/// <summary>
/// Gets all the registered services for the given notification type
/// </summary>
/// <returns>The registered services</returns>
/// <typeparam name="TNotification">Type of notification</typeparam>
public IEnumerable<IPushService> GetRegistrations<TNotification>()
{
var type = typeof(TNotification);
return GetRegistrations<TNotification> (null);
}

if (registeredServices != null && registeredServices.ContainsKey(type))
return registeredServices[type];
public IEnumerable<IPushService> GetRegistrations(string applicationId)
{
lock (serviceRegistrationsLock)
return from s in serviceRegistrations where s.ApplicationId.Equals(applicationId) select s.Service;
}

return null;
/// <summary>
/// Gets all the registered services for the given notification type and application identifier
/// </summary>
/// <returns>The registered services</returns>
/// <param name="applicationId">Application identifier </param>
/// <typeparam name="TNotification">Type of notification</typeparam>
public IEnumerable<IPushService> GetRegistrations<TNotification>(string applicationId)
{
var type = typeof(TNotification);

if (string.IsNullOrEmpty (applicationId))
{
lock (serviceRegistrationsLock)
{
return from sr in serviceRegistrations
where sr.NotificationType == type
select sr.Service;
}
}
else
{
lock (serviceRegistrationsLock)
{
return from sr in serviceRegistrations
where sr.ApplicationId.Equals (applicationId)
&& sr.NotificationType == type
select sr.Service;
}
}
}

/// <summary>
/// Stops all services that have been registered with the broker
/// </summary>
/// <param name="waitForQueuesToFinish">If set to <c>true</c> wait for queues to finish.</param>
public void StopAllServices(bool waitForQueuesToFinish = true)
{
registeredServices.Values.AsParallel().ForAll(svc => svc.ForEach(svcOn => {
svcOn.Stop(waitForQueuesToFinish);
svcOn.OnChannelCreated -= OnChannelCreated;
svcOn.OnChannelDestroyed -= OnChannelDestroyed;
svcOn.OnChannelException -= OnChannelException;
svcOn.OnDeviceSubscriptionExpired -= OnDeviceSubscriptionExpired;
svcOn.OnNotificationFailed -= OnNotificationFailed;
svcOn.OnNotificationSent -= OnNotificationSent;
svcOn.OnServiceException -= OnServiceException;
svcOn.OnDeviceSubscriptionChanged -= OnDeviceSubscriptionChanged;
}));
var stopping = new List<ServiceRegistration> ();

lock (serviceRegistrationsLock)
{
stopping.AddRange (serviceRegistrations);

serviceRegistrations.Clear ();
}

stopping.AsParallel ().ForAll (sr => StopService (sr, waitForQueuesToFinish));
}

/// <summary>
/// Stops and removes all registered services for the given notification type
/// </summary>
/// <param name="waitForQueuesToFinish">If set to <c>true</c> waits for the queues to be drained before returning.</param>
/// <typeparam name="TNotification">Notification Type</typeparam>
public void StopAllServices<TNotification>(bool waitForQueuesToFinish = true)
{
StopAllServices<TNotification> (null, waitForQueuesToFinish);
}

/// <summary>
/// Stops and removes all registered services for the given application identifier and notification type
/// </summary>
/// <param name="applicationId">Application identifier.</param>
/// <param name="waitForQueuesToFinish">If set to <c>true</c> waits for queues to be drained before returning.</param>
/// <typeparam name="TNotification">The 1st type parameter.</typeparam>
public void StopAllServices<TNotification>(string applicationId, bool waitForQueuesToFinish = true)
{
var type = typeof(TNotification);

var stopping = new List<ServiceRegistration> ();

lock (serviceRegistrationsLock)
{
if (string.IsNullOrEmpty (applicationId))
{
var services = from s in serviceRegistrations where s.NotificationType == type select s;

if (services != null && services.Any ())
stopping.AddRange (services);

serviceRegistrations.RemoveAll (s => s.NotificationType == type);
}
else
{
var services = from s in serviceRegistrations where s.NotificationType == type
&& s.ApplicationId.Equals (applicationId) select s;

if (services != null && services.Any ())
stopping.AddRange (services);

serviceRegistrations.RemoveAll (s => s.NotificationType == type && s.ApplicationId.Equals(applicationId));
}
}

if (stopping != null && stopping.Any())
stopping.AsParallel().ForAll(sr => StopService(sr, waitForQueuesToFinish));
}

/// <summary>
/// Stops and removes all registered services for the given application identifier
/// </summary>
/// <param name="applicationId">Application identifier.</param>
/// <param name="waitForQueuesToFinish">If set to <c>true</c> waits for queues to be drained before returning.</param>
public void StopAllServices(string applicationId, bool waitForQueuesToFinish = true)
{
var stopping = new List<ServiceRegistration> ();

lock (serviceRegistrationsLock)
{
var services = from s in serviceRegistrations where s.ApplicationId.Equals(applicationId) select s;

if (services != null && services.Any ())
stopping.AddRange (services);

serviceRegistrations.RemoveAll (s => s.ApplicationId.Equals(applicationId));
}

if (stopping != null && stopping.Any())
stopping.AsParallel().ForAll(sr => StopService(sr, waitForQueuesToFinish));
}

void StopService(ServiceRegistration sr, bool waitForQueuesToFinish)
{
sr.Service.Stop (waitForQueuesToFinish);

sr.Service.OnChannelCreated -= OnChannelCreated;
sr.Service.OnChannelDestroyed -= OnChannelDestroyed;
sr.Service.OnChannelException -= OnChannelException;
sr.Service.OnDeviceSubscriptionExpired -= OnDeviceSubscriptionExpired;
sr.Service.OnNotificationFailed -= OnNotificationFailed;
sr.Service.OnNotificationSent -= OnNotificationSent;
sr.Service.OnServiceException -= OnServiceException;
sr.Service.OnDeviceSubscriptionChanged -= OnDeviceSubscriptionChanged;
}

void IDisposable.Dispose()
Expand Down
1 change: 1 addition & 0 deletions PushSharp.Core/PushSharp.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<Compile Include="PushServiceSettings.cs" />
<Compile Include="SendNotificationResult.cs" />
<Compile Include="NotificationQueue.cs" />
<Compile Include="ServiceRegistration.cs" />
</ItemGroup>
<ItemGroup>
<None Include="PushSharp-Signing.snk" />
Expand Down
21 changes: 21 additions & 0 deletions PushSharp.Core/ServiceRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace PushSharp.Core
{
public class ServiceRegistration
{
public static ServiceRegistration Create<TNotification>(IPushService service, string applicationId = null)
{
return new ServiceRegistration () {
ApplicationId = applicationId,
Service = service,
NotificationType = typeof(TNotification)
};
}

public IPushService Service { get;set; }
public string ApplicationId { get;set; }
public Type NotificationType { get;set; }
}
}

11 changes: 8 additions & 3 deletions PushSharp.Google.Chrome/ChromePushBrokerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ namespace PushSharp
{
public static class ChromePushBrokerExtensions
{
public static void RegisterChromeGcmService(this PushBroker broker, ChromePushChannelSettings channelSettings, IPushServiceSettings serviceSettings = null)
public static void RegisterChromeGcmService(this PushBroker broker, ChromePushChannelSettings channelSettings, IPushServiceSettings serviceSettings = null)
{
RegisterChromeGcmService (broker, channelSettings, null, serviceSettings);
}

public static void RegisterChromeGcmService(this PushBroker broker, ChromePushChannelSettings channelSettings, string applicationId = null, IPushServiceSettings serviceSettings = null)
{
broker.RegisterService<ChromeNotification>(new ChromePushService(channelSettings, serviceSettings));
broker.RegisterService<ChromeNotification>(new ChromePushService(channelSettings, serviceSettings), applicationId);
}

public static ChromeNotification BlackberryNotification(this PushBroker broker)
public static ChromeNotification ChromeNotification(this PushBroker broker)
{
return new ChromeNotification();
}
Expand Down
Loading

0 comments on commit ad96e0c

Please sign in to comment.