Class PushEvents

java.lang.Object
com.iizix.server.push.engine.PushEvents
All Implemented Interfaces:
SubscriptionStore.Listener

public final class PushEvents extends Object implements SubscriptionStore.Listener
The engine-sourced Push Notification (PN) events, fanned out to the sessions that listen (PUSH-PROPS-DESIGN sections 4.4 and 5, P4-04): a subscription stored or removed - by whichever front end, the HTTP API or the Java API, and by the engine itself when a push service reports an endpoint gone - and a target of a send that ended in a failure. And the client-sourced receipts (design 7, P4-11): a notification shown, a notification or one of its actions clicked, reported by a client's channel and delivered by report(IPushReceipt, String) to every listening session of the receipt's user and application, including the session a click opened; a receipt nobody listens to is logged at INFO and dropped, nothing is stored (contract 5.9).

Duplicates (design 7.4, RR-13 A): one notification can be reported more than once - shown in-session and by device push on the same Browser under BOTH, or drained from the Service Worker's queue by a page that also saw it live. The hub keeps the keys (message, device, kind, action) of the receipts it delivered for RECEIPT_WINDOW_MILLIS, at most RECEIPT_KEYS_MAX of them, the oldest evicted first, and drops a report whose key it holds; a duplicate older than the window is delivered.

Another server's receipts (CC-DESIGN-PUSH-APPID-FILTER SV-5, defect F-1): a device keeps the receipts of every server it received from and reports them to whichever session drains its queue next, on whatever server. A report naming a push server ID other than this engine's (setServerId(String)) is not this server's: it is dropped, logged at FINE and counted (getForeignServerReceiptCount()), before the duplicate window, so that it is never handed to a same-named application's listeners here. A report naming none - a device half or a Service Worker from before O-4a - is accepted as before.

The hub holds one PushEvents.ISink per session API that asked for events, and matches each event to the sinks by user and application AT DELIVERY, because a session's user can sign in after its API was created. A sink only posts to its own session's worker and returns: nothing here blocks the store's or the engine's thread, and a sink that throws is logged SEVERE and does not stop the others.

The engine registers the hub with its SubscriptionStore at start, so that a row stored over HTTP reaches an application session's listener exactly as one stored from Java.

Author:
Christopher Mindus
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static interface 
    A session's receiver of the events, matched by user and application at delivery.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final Set<DeliveryOutcome>
    The outcomes a listener is told about as a failure: neither a delivery, nor held in the spool, nor a deliberate exclusion - the list of IPushNotificationListener.onSendFailed.
    static final int
    How many receipt keys are remembered at most; the oldest is evicted first (design 7.4).
    static final long
    How long the key of a delivered receipt is remembered, so that a repeat is dropped: 10 minutes (design 7.4).
  • Constructor Summary

    Constructors
    Constructor
    Description
    Constructor, over the system clock.
    Constructor.
  • Method Summary

    Modifier and Type
    Method
    Description
    long
    Gets the number of reports dropped because they named another push server (SV-5).
    int
    Gets the number of receipt keys remembered, for tests.
    int
    Gets the number of registered sinks.
    void
    onRegistered(SubscriptionRecord subscription, boolean created)
    --- The store's events ---
    void
    onRemoved(SubscriptionRecord subscription, String reason)
    Called after a row was removed.
    boolean
    Registers a sink.
    boolean
    report(IPushReceipt receipt, String action)
    Reports a receipt a client's channel delivered that names no push server: as report(IPushReceipt,String,String) with none, which is never dropped for its server.
    boolean
    report(IPushReceipt receipt, String action, String server)
    Reports a receipt a client's channel delivered: dropped when it names another push server than this engine's (SV-5, logged at FINE and counted), or when the same (message, device, kind, action) was delivered within the window; else handed to every sink of the receipt's user and application - onNotificationReceived for a notification shown, onNotificationAction for a click - and logged at INFO when no sink takes it.
    void
    sendEnded(String messageID, DeliveryTarget target, DeliveryOutcome outcome, String detail)
    Reports the outcome of one target of a send; only a failure reaches the sinks.
    void
    setServerId(String serverId)
    Sets the push server ID of the engine this hub belongs to (SV-5): a report naming another is dropped from then on.
    boolean
    Unregisters a sink.
  • Field Details

    • RECEIPT_WINDOW_MILLIS

      public static final long RECEIPT_WINDOW_MILLIS
      How long the key of a delivered receipt is remembered, so that a repeat is dropped: 10 minutes (design 7.4).
      See Also:
    • RECEIPT_KEYS_MAX

      public static final int RECEIPT_KEYS_MAX
      How many receipt keys are remembered at most; the oldest is evicted first (design 7.4).
      See Also:
    • FAILURES

      public static final Set<DeliveryOutcome> FAILURES
      The outcomes a listener is told about as a failure: neither a delivery, nor held in the spool, nor a deliberate exclusion - the list of IPushNotificationListener.onSendFailed.
  • Constructor Details

    • PushEvents

      public PushEvents()
      Constructor, over the system clock.
    • PushEvents

      public PushEvents(LongSupplier clock)
      Constructor.
      Parameters:
      clock - The clock the receipt window is measured by, epoch milliseconds; a test moves its own.
      Throws:
      NullPointerException - If clock is null.
  • Method Details

    • register

      public boolean register(PushEvents.ISink sink)
      Registers a sink. Registering one twice changes nothing.
      Parameters:
      sink - The sink.
      Returns:
      true if added, false if it was already registered.
      Throws:
      NullPointerException - If sink is null.
    • unregister

      public boolean unregister(PushEvents.ISink sink)
      Unregisters a sink.
      Parameters:
      sink - The sink.
      Returns:
      true if removed, false if it was not registered.
    • getSinkCount

      public int getSinkCount()
      Gets the number of registered sinks.
      Returns:
      The count.
    • onRegistered

      public void onRegistered(SubscriptionRecord subscription, boolean created)
      --- The store's events ---
      Specified by:
      onRegistered in interface SubscriptionStore.Listener
      Parameters:
      subscription - The row as it now stands.
      created - true for a new row, false for an existing row updated with a new subscription; an unchanged re-registration is not reported.
    • onRemoved

      public void onRemoved(SubscriptionRecord subscription, String reason)
      Description copied from interface: SubscriptionStore.Listener
      Called after a row was removed.
      Specified by:
      onRemoved in interface SubscriptionStore.Listener
      Parameters:
      subscription - The row as it was.
      reason - An IPushNotificationListener.REMOVED_* constant.
    • sendEnded

      public void sendEnded(String messageID, DeliveryTarget target, DeliveryOutcome outcome, String detail)
      Reports the outcome of one target of a send; only a failure reaches the sinks.
      Parameters:
      messageID - The message ID of the send, or null when the notification carries none.
      target - The target.
      outcome - The outcome.
      detail - The diagnostic, null for none.
    • setServerId

      public void setServerId(String serverId)
      Sets the push server ID of the engine this hub belongs to (SV-5): a report naming another is dropped from then on. Called by the engine at its start.
      Parameters:
      serverId - The ID, PushEngine.getServerId(); null for none.
    • getForeignServerReceiptCount

      public long getForeignServerReceiptCount()
      Gets the number of reports dropped because they named another push server (SV-5).
      Returns:
      The count since this hub was created.
    • report

      public boolean report(IPushReceipt receipt, String action)
      Reports a receipt a client's channel delivered that names no push server: as report(IPushReceipt,String,String) with none, which is never dropped for its server.
      Parameters:
      receipt - The receipt.
      action - Null for a notification shown; for a click the action identity, empty for the notification itself.
      Returns:
      true when delivered or found nobody listening, false when dropped as a duplicate.
      Throws:
      NullPointerException - If receipt is null.
    • report

      public boolean report(IPushReceipt receipt, String action, String server)
      Reports a receipt a client's channel delivered: dropped when it names another push server than this engine's (SV-5, logged at FINE and counted), or when the same (message, device, kind, action) was delivered within the window; else handed to every sink of the receipt's user and application - onNotificationReceived for a notification shown, onNotificationAction for a click - and logged at INFO when no sink takes it.
      Parameters:
      receipt - The receipt.
      action - Null for a notification shown; for a click the action identity, empty for the notification itself.
      server - The push server ID the report names (data.iizi.server of the notification), null or empty when it names none: a device half or a Service Worker from before O-4a, or a server that wrote none.
      Returns:
      true when delivered or found nobody listening, false when dropped as another server's or as a duplicate.
      Throws:
      NullPointerException - If receipt is null.
    • getReceiptKeyCount

      public int getReceiptKeyCount()
      Gets the number of receipt keys remembered, for tests.
      Returns:
      The count.