com.relayrides.pushy.apns
Class PushManager<T extends ApnsPushNotification>

java.lang.Object
  extended by com.relayrides.pushy.apns.PushManager<T>
All Implemented Interfaces:
ApnsConnectionListener<T>

public class PushManager<T extends ApnsPushNotification>
extends Object
implements ApnsConnectionListener<T>

Push managers manage connections to the APNs gateway and send notifications from their queue. Push managers should always be created using the PushManagerFactory class. Push managers are the main public-facing point of interaction with Pushy.

Queues

A push manager has two queues: the public queue through which callers add notifications to be sent, and a private, internal "retry queue." Callers send push notifications by adding them to the push manager's public queue (see getQueue()). The push manager will take and notifications from the public queue as quickly as it is able to do so, and will never put notifications back in the public queue. Callers are free to manipulate the public queue however they see fit.

If, for any reason other than a permanent rejection, a notification could not be delivered, it will be returned to the push manager's internal retry queue. The push manager will always try to drain its retry queue before taking new notifications from the public queue.

Shutting down

A push manager can be shut down with or without a timeout, though shutting down without a timeout provides stronger guarantees with regard to the state of sent notifications. Regardless of whether a timeout is specified, push managers will stop taking notifications from the public queue as soon the shutdown process has started. Push managers shut down by asking all of their connections to shut down gracefully by sending a known-bad notification to the APNs gateway. The push manager will restore closed connections and keep trying to send notifications from its internal retry queue until either the queue is empty or the timeout expires. If the timeout expires while there are still open connections, all remaining connections are closed immediately.

When shutting down without a timeout, it is guaranteed that the push manager's internal retry queue will be empty and all sent notifications will have reached and been processed by the APNs gateway. Any notifications not rejected by the gateway by the time the shutdown process completes will have been accepted by the gateway (though no guarantees are made that they will ever be delivered to the destination device).

Error handling

Callers may register listeners to handle notifications permanently rejected by the APNs gateway and to handle failed attempts to connect to the gateway.

When a notification is rejected by the APNs gateway, the rejection should be considered permanent and callers should not try to resend the notification. When a connection fails, the push manager will report the failure to registered listeners, but will continue trying to connect until shut down. Callers should shut down the push manager in the event of a failure unlikely to be resolved by retrying the connection (the most common case is an SSLHandshakeException, which usually indicates a certificate problem of some kind).

Author:
Jon Chambers
See Also:
getQueue(), PushManagerFactory

Method Summary
 List<ExpiredToken> getExpiredTokens()
          Queries the APNs feedback service for expired tokens using a reasonable default timeout.
 List<ExpiredToken> getExpiredTokens(long timeout, TimeUnit timeoutUnit)
          Queries the APNs feedback service for expired tokens using the given timeout.
 BlockingQueue<T> getQueue()
          Returns the queue of messages to be sent to the APNs gateway.
 void handleConnectionClosure(ApnsConnection<T> connection)
          Indicates that the given connection has disconnected from the previously-connected APNs gateway and can no longer send push notifications.
 void handleConnectionFailure(ApnsConnection<T> connection, Throwable cause)
          Indicates that the given connection attempted to connect to an APNs gateway, but failed.
 void handleConnectionSuccess(ApnsConnection<T> connection)
          Indicates that the given connection successfully connected to an APNs gateway and is ready to send push notifications.
 void handleConnectionWritabilityChange(ApnsConnection<T> connection, boolean writable)
          Indicates that the given connection has changed its writability state.
 void handleRejectedNotification(ApnsConnection<T> connection, T rejectedNotification, RejectedNotificationReason reason)
          Indicates that a notification sent via the given connection was definitively rejected by the APNs gateway.
 void handleUnprocessedNotifications(ApnsConnection<T> connection, Collection<T> unprocessedNotifications)
          Indicates that notifications that had previously been sent to an APNs gateway by the given connection were not processed by the gateway and should be sent again later.
 void handleWriteFailure(ApnsConnection<T> connection, T notification, Throwable cause)
          Indicates that the given connection failed to send a push notification to an APNs gateway.
 boolean isShutDown()
          Indicates whether this push manager has been shut down (or is in the process of shutting down).
 boolean isStarted()
          Indicates whether this push manager has been started and not yet shut down.
 void registerFailedConnectionListener(FailedConnectionListener<? super T> listener)
          Registers a listener for failed attempts to connect to the APNs gateway.
 void registerRejectedNotificationListener(RejectedNotificationListener<? super T> listener)
          Registers a listener for notifications rejected by APNs for specific reasons.
 void shutdown()
          Disconnects from APNs and gracefully shuts down all connections.
 List<T> shutdown(long timeout)
          Disconnects from the APNs and gracefully shuts down all connections.
 void start()
          Opens all connections to APNs and prepares to send push notifications.
 boolean unregisterFailedConnectionListener(FailedConnectionListener<? super T> listener)
          Un-registers a connection failure listener.
 boolean unregisterRejectedNotificationListener(RejectedNotificationListener<? super T> listener)
          Un-registers a rejected notification listener.
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

start

public void start()

Opens all connections to APNs and prepares to send push notifications. Note that enqueued push notifications will not be sent until this method is called.

Push managers may only be started once and cannot be reused after being shut down.

Throws:
IllegalStateException - if the push manager has already been started or has already been shut down

isStarted

public boolean isStarted()
Indicates whether this push manager has been started and not yet shut down.

Returns:
true if this push manager has been started and has not yet been shut down or false otherwise

isShutDown

public boolean isShutDown()
Indicates whether this push manager has been shut down (or is in the process of shutting down). Once a push manager has been shut down, it may not be restarted.

Returns:
true if this push manager has been shut down or is in the process of shutting down or false otherwise

shutdown

public void shutdown()
              throws InterruptedException

Disconnects from APNs and gracefully shuts down all connections. This method will block until the internal retry queue has been emptied and until all connections have shut down gracefully. Calling this method is identical to calling shutdown(long) with a timeout of 0.

By the time this method return normally, all notifications removed from the public queue are guaranteed to have been delivered to the APNs gateway and either accepted or rejected (i.e. the state of all sent notifications is known).

Throws:
InterruptedException - if interrupted while waiting for connections to close cleanly
IllegalStateException - if this method is called before the push manager has been started

shutdown

public List<T> shutdown(long timeout)
                                              throws InterruptedException

Disconnects from the APNs and gracefully shuts down all connections. This method will wait until either the given timeout expires or until the internal retry queue has been emptied and connections have closed gracefully. If the timeout expires, the push manager will close all connections immediately.

This method returns the notifications that are still in the internal retry queue by the time this push manager has shut down. If this method is called with a non-zero timeout, a collection of notifications still in the push manager's internal retry queue will be returned. The returned collection will not contain notifications in the public queue (since callers can work with the public queue directly). When shutting down with a non-zero timeout, no guarantees are made that notifications that were sent (i.e. are in neither the public queue nor the retry queue) were actually received or processed by the APNs gateway.

If called with a timeout of 0, the returned collection of unsent notifications will be empty. By the time this method exits, all notifications taken from the public queue are guaranteed to have been delivered to the APNs gateway and either accepted or rejected (i.e. the state of all sent notifications is known).

Parameters:
timeout - the timeout, in milliseconds, after which client threads should be shut down as quickly as possible; if 0, this method will wait indefinitely for the retry queue to empty and connections to close
Returns:
a list of notifications not sent before the PushManager shut down
Throws:
InterruptedException - if interrupted while waiting for connections to close cleanly
IllegalStateException - if this method is called before the push manager has been started

registerRejectedNotificationListener

public void registerRejectedNotificationListener(RejectedNotificationListener<? super T> listener)

Registers a listener for notifications rejected by APNs for specific reasons.

Parameters:
listener - the listener to register
Throws:
IllegalStateException - if this push manager has already been shut down
See Also:
unregisterRejectedNotificationListener(RejectedNotificationListener)

unregisterRejectedNotificationListener

public boolean unregisterRejectedNotificationListener(RejectedNotificationListener<? super T> listener)

Un-registers a rejected notification listener.

Parameters:
listener - the listener to un-register
Returns:
true if the given listener was registered with this push manager and removed or false if the listener was not already registered with this push manager

registerFailedConnectionListener

public void registerFailedConnectionListener(FailedConnectionListener<? super T> listener)

Registers a listener for failed attempts to connect to the APNs gateway.

Parameters:
listener - the listener to register
Throws:
IllegalStateException - if this push manager has already been shut down
See Also:
unregisterFailedConnectionListener(FailedConnectionListener)

unregisterFailedConnectionListener

public boolean unregisterFailedConnectionListener(FailedConnectionListener<? super T> listener)

Un-registers a connection failure listener.

Parameters:
listener - the listener to un-register
Returns:
true if the given listener was registered with this push manager and removed or false if the listener was not already registered with this push manager

getQueue

public BlockingQueue<T> getQueue()

Returns the queue of messages to be sent to the APNs gateway. Callers should add notifications to this queue directly to send notifications. Notifications will be removed from this queue by Pushy when a send attempt is started, but are not guaranteed to have reached the APNs gateway until the push manager has been shut down without a timeout (see shutdown(long)). Successful delivery is not acknowledged by the APNs gateway. Notifications rejected by APNs for specific reasons will be passed to registered RejectedNotificationListeners, and notifications that could not be sent due to temporary I/O problems will be scheduled for re-transmission in a separate, internal queue.

Notifications in this queue will only be consumed when the PushManager is running, has active connections, and the internal "retry queue" is empty.

Returns:
the queue of new notifications to send to the APNs gateway
See Also:
registerRejectedNotificationListener(RejectedNotificationListener)

getExpiredTokens

public List<ExpiredToken> getExpiredTokens()
                                    throws InterruptedException,
                                           FeedbackConnectionException

Queries the APNs feedback service for expired tokens using a reasonable default timeout. Be warned that this is a destructive operation. According to Apple's documentation:

The feedback service’s list is cleared after you read it. Each time you connect to the feedback service, the information it returns lists only the failures that have happened since you last connected.

The push manager must be started before calling this method.

Returns:
a list of tokens that have expired since the last connection to the feedback service
Throws:
InterruptedException - if interrupted while waiting for a response from the feedback service
FeedbackConnectionException - if the attempt to connect to the feedback service failed for any reason

getExpiredTokens

public List<ExpiredToken> getExpiredTokens(long timeout,
                                           TimeUnit timeoutUnit)
                                    throws InterruptedException,
                                           FeedbackConnectionException

Queries the APNs feedback service for expired tokens using the given timeout. Be warned that this is a destructive operation. According to Apple's documentation:

The feedback service's list is cleared after you read it. Each time you connect to the feedback service, the information it returns lists only the failures that have happened since you last connected.

The push manager must be started before calling this method.

Parameters:
timeout - the time after the last received data after which the connection to the feedback service should be closed
timeoutUnit - the unit of time in which the given timeout is measured
Returns:
a list of tokens that have expired since the last connection to the feedback service
Throws:
InterruptedException - if interrupted while waiting for a response from the feedback service
FeedbackConnectionException - if the attempt to connect to the feedback service failed for any reason
IllegalStateException - if this push manager has not been started yet or has already been shut down

handleConnectionSuccess

public void handleConnectionSuccess(ApnsConnection<T> connection)
Description copied from interface: ApnsConnectionListener
Indicates that the given connection successfully connected to an APNs gateway and is ready to send push notifications.

Specified by:
handleConnectionSuccess in interface ApnsConnectionListener<T extends ApnsPushNotification>
Parameters:
connection - the connection that completed its connection attempt

handleConnectionFailure

public void handleConnectionFailure(ApnsConnection<T> connection,
                                    Throwable cause)
Description copied from interface: ApnsConnectionListener
Indicates that the given connection attempted to connect to an APNs gateway, but failed.

Specified by:
handleConnectionFailure in interface ApnsConnectionListener<T extends ApnsPushNotification>
Parameters:
connection - the connection that failed to connect to an APNs gateway
cause - the cause of the failure

handleConnectionWritabilityChange

public void handleConnectionWritabilityChange(ApnsConnection<T> connection,
                                              boolean writable)
Description copied from interface: ApnsConnectionListener
Indicates that the given connection has changed its writability state. Attempts to write to an unwritable connection are guaranteed to fail and should be avoided. Successful connections begin in a writable state.

Specified by:
handleConnectionWritabilityChange in interface ApnsConnectionListener<T extends ApnsPushNotification>
Parameters:
connection - the connection whose writability has changed
writable - true if the connection has become writable or false if it has become unwritable

handleConnectionClosure

public void handleConnectionClosure(ApnsConnection<T> connection)
Description copied from interface: ApnsConnectionListener
Indicates that the given connection has disconnected from the previously-connected APNs gateway and can no longer send push notifications. This may happen either when the connection is closed locally or when the APNs gateway closes the connection remotely. This method will only be called if the connection had previously succeeded and completed a TLS handshake.

Specified by:
handleConnectionClosure in interface ApnsConnectionListener<T extends ApnsPushNotification>
Parameters:
connection - the connection that has been disconnected and is no longer active

handleWriteFailure

public void handleWriteFailure(ApnsConnection<T> connection,
                               T notification,
                               Throwable cause)
Description copied from interface: ApnsConnectionListener
Indicates that the given connection failed to send a push notification to an APNs gateway. This indicates a local failure; notifications passed to this method were never transmitted to the APNs gateway, and failures of this kind generally represent temporary I/O problems (rather than permanent rejection by the gateway), and it is generally safe to try to send the failed notifications again later.

Specified by:
handleWriteFailure in interface ApnsConnectionListener<T extends ApnsPushNotification>
Parameters:
connection - the connection that attempted to deliver the notification
notification - the notification that could not be written
cause - the cause of the write failure

handleRejectedNotification

public void handleRejectedNotification(ApnsConnection<T> connection,
                                       T rejectedNotification,
                                       RejectedNotificationReason reason)
Description copied from interface: ApnsConnectionListener
Indicates that a notification sent via the given connection was definitively rejected by the APNs gateway. When an APNs gateway rejects a notification, the rejection should be considered a permanent failure and the notification should not be sent again. The APNs gateway will close the connection after rejecting a notification, and all notifications sent after the rejected notification were not processed by the gateway and should be re-sent later.

Specified by:
handleRejectedNotification in interface ApnsConnectionListener<T extends ApnsPushNotification>
Parameters:
connection - the connection that sent the notification that was rejected
rejectedNotification - the notification that was rejected
reason - the reason for the rejection
See Also:
ApnsConnectionListener.handleConnectionClosure(ApnsConnection), ApnsConnectionListener.handleUnprocessedNotifications(ApnsConnection, Collection)

handleUnprocessedNotifications

public void handleUnprocessedNotifications(ApnsConnection<T> connection,
                                           Collection<T> unprocessedNotifications)
Description copied from interface: ApnsConnectionListener
Indicates that notifications that had previously been sent to an APNs gateway by the given connection were not processed by the gateway and should be sent again later. This generally happens after a notification has been rejected by the gateway, but may also happen when a connection is closed gracefully by Pushy or closed remotely by the APNs gateway without a rejected notification.

Specified by:
handleUnprocessedNotifications in interface ApnsConnectionListener<T extends ApnsPushNotification>
Parameters:
connection - the connection that sent the notifications that were not processed
unprocessedNotifications - the notifications known to have not been processed by the APNs gateway


Copyright © 2013–2014 RelayRides. All rights reserved.