Class PushEngine

java.lang.Object
com.iizix.server.push.engine.PushEngine
All Implemented Interfaces:
IPushNotificationEngine

public final class PushEngine extends Object implements IPushNotificationEngine
The Push Notification engine: the runtime that owns the executor, the concurrency ceiling, the store, the spool, the transports and the resolver, and performs sends.

Concurrency. Sends run on a virtual-thread-per-task executor, unbounded by construction; the hard ceiling on notifications in flight is a Semaphore sized to IPushSettings.getMaxParallelPush(), acquired before every dispatch and released on completion. The setting is read once at start and held.

A send builds its RoutingPlan first (the audit record, the authorization check and the area filter all happen there, before anything leaves), then dispatches every target of every slice under a permit, records one outcome per target and completes the future with the PushResultImpl. A device target goes to its transport; an in-session target to the IInSessionDelivery seam, with the ruled fallback: when the app session closed holding the notification, a device push from this same server.

Failures become spool entries. A transient transport failure spools the notification for that target with exponential backoff; a replay loop on its own virtual thread sends due entries, re-resolving each subscription so a re-registered device is reached at its new endpoint, and persists the spool after each pass and at shutdown.

Single server. Every slice is local; the region tree names regions for the audit record and the area filter and never routes remotely. Nothing changes in the interfaces when that arrives.

Files. The engine owns one PushFileWatcher (design 7.3, ruling S-5): created at the first file to watch - the credentials file, or a key file the PushKeyFiles read - and closed at dispose(); none when nothing is watched.

Author:
Christopher Mindus
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final class 
    The collaborators of an engine, so that a test can build one without a server.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    The name of the engine's file watcher thread: "iizi-push-files".
  • Constructor Summary

    Constructors
    Constructor
    Description
    Constructor.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Disposes the engine: transitions through PushEngineState.STOPPING to PushEngineState.STOPPED, refusing new sends, allowing in-flight sends to complete, persisting the spool, and releasing the executor and transports.
    Gets the application configurations.
    Gets the credentials: the credentials file of PUSH-CREDENTIALS.md and the users registered from Java, the first sources of the HTTP API's authentication and the group definitions of the database users.
    Gets the events hub the sessions' Push Notification listeners are fed from: a subscription stored or removed, a send target that failed (P4-04).
    int
    Gets the number of sends in flight now.
    Gets the key files: the reader of every Push Notification key file of this server (PUSH-KEYFILES-DESIGN.md section 5), shared by the credentials and the application configurations and watched through this engine's watcher.
    int
    Gets the ceiling the engine started with.
    Gets the registry of the Push Notification HTTP API's program users (PUSH-CREDENTIALS.md section 7, ruling R6; declared here by CC-PUSH-SERVER-INI decision 5, 2026-09-26): server code registers a user in memory with a password and the groups whose privileges it gets, and a program then authenticates with it over Basic authentication.
    Gets the region tree the router resolves residency against.
    Gets the resolver.
    Gets the push server ID (CC-DESIGN-PUSH-APPID-FILTER SV-1): a UUID made at the first engine start on the subscription database and kept there, the same for every server sharing it (SubscriptionStore.ensureServerId()).
    --- Accessors ---
    Gets the spool.
    Gets the current lifecycle state of the engine.
    Gets the store.
    Gets a transport.
    Tells whether a transport can send for an application, and when not, why: the ONE check behind queryAvailableTransports and a client session's subscribe (CC-FIX-PUSH-V5 G-3, CC-V5-AUDIT-PUSH VP-D-3), so that a device is never subscribed under a configuration whose public half alone this server holds - a VAPID public key whose private key is not in this machine's secrets store, a Firebase project without its service account, a pair that does not match - which would store a row this server can never send to.
    Gets the user directory.
    Gets the engine's file watcher: one per engine, created at the first file to watch - the credentials file or any key file - and closed by dispose() (design 7.3, S-5).
    boolean
    Reports whether the engine has been disposed.
    Creates a new, empty message builder bound to this engine, so that defaults such as TTL are taken from this engine's settings.
    --- Planning and sending ---
    Performs a send asynchronously.
    send(String appId, String topic, long[] uids, IPushNotification message, DeliveryUrgency urgency)
    Sends a notification for an application and Topic to users by ID, or to every subscriber, with an urgency, the server's default policies otherwise and an unrestricted operator, and waits a bounded time: the HTTP push of PUSH-WIRE-CONTRACT section 5.6.
    sendToUsers(String appId, String topic, long[] uids, IPushNotification message)
    Sends a notification to users by ID for an application and Topic, with the server's default policies and an unrestricted operator, and waits a bounded time.
    static File
    spoolFile(IPushSettings settings, File serverDir)
    Gets the default spool file of a server: <spoolDirectory>/pnspool.xml, the spool directory being the setting when non-empty, else push-spool under the server's current directory.
    void
    Starts the engine: creates the ceiling and the executor, ensures the schema, makes or reads the push server ID, loads the spool, loads and watches the credentials file, and starts the replay loop.
  • Field Details

    • WATCHER_THREAD_NAME

      public static final String WATCHER_THREAD_NAME
      The name of the engine's file watcher thread: "iizi-push-files". One thread per engine, a daemon, watching the credentials file and every key file read.
      See Also:
  • Constructor Details

    • PushEngine

      public PushEngine(PushEngine.Config cfg)
      Constructor. Nothing runs until start().
      Parameters:
      cfg - The collaborators; settings, store, users, configs and spool are required.
  • Method Details

    • start

      public void start() throws SQLException
      Starts the engine: creates the ceiling and the executor, ensures the schema, makes or reads the push server ID, loads the spool, loads and watches the credentials file, and starts the replay loop.
      Throws:
      IllegalStateException - If the engine is not in PushEngineState.NOT_STARTED.
      SQLException - If the subscription table cannot be created or migrated.
    • getState

      public PushEngineState getState()
      Description copied from interface: IPushNotificationEngine
      Gets the current lifecycle state of the engine.
      Specified by:
      getState in interface IPushNotificationEngine
      Returns:
      The state; never null.
    • isDisposed

      public boolean isDisposed()
      Description copied from interface: IPushNotificationEngine
      Reports whether the engine has been disposed.

      A disposed engine is in PushEngineState.STOPPED and performs no further work, and it is never revived. PushNotificationModule disposes of the engine and unregisters it from PushNotificationEngineProvider in the same step, so until a later server start registers a new engine IServer.getPushNotificationEngine() throws UnsupportedOperationException rather than returning this one; see IPushNotificationService.

      Specified by:
      isDisposed in interface IPushNotificationEngine
      Returns:
      true if this engine has been disposed.
    • dispose

      public void dispose()
      Description copied from interface: IPushNotificationEngine
      Disposes the engine: transitions through PushEngineState.STOPPING to PushEngineState.STOPPED, refusing new sends, allowing in-flight sends to complete, persisting the spool, and releasing the executor and transports. Idempotent — disposing an already-disposed engine has no effect.
      Specified by:
      dispose in interface IPushNotificationEngine
    • getSettings

      public IPushSettings getSettings()
      --- Accessors ---
      Specified by:
      getSettings in interface IPushNotificationEngine
      Returns:
      The settings; never null.
    • getRegionTree

      public IRegionTree getRegionTree()
      Description copied from interface: IPushNotificationEngine
      Gets the region tree the router resolves residency against.
      Specified by:
      getRegionTree in interface IPushNotificationEngine
      Returns:
      The region tree; never null.
    • newMessageBuilder

      public IPushNotificationMessageBuilder newMessageBuilder()
      Description copied from interface: IPushNotificationEngine
      Creates a new, empty message builder bound to this engine, so that defaults such as TTL are taken from this engine's settings.
      Specified by:
      newMessageBuilder in interface IPushNotificationEngine
      Returns:
      A fresh IPushNotificationMessageBuilder; never null.
    • getPushUserRegistry

      public IPushUserRegistry getPushUserRegistry()
      Description copied from interface: IPushNotificationEngine
      Gets the registry of the Push Notification HTTP API's program users (PUSH-CREDENTIALS.md section 7, ruling R6; declared here by CC-PUSH-SERVER-INI decision 5, 2026-09-26): server code registers a user in memory with a password and the groups whose privileges it gets, and a program then authenticates with it over Basic authentication. Implemented by the engine's credentials, beside the users of the credentials file, which always wins over a registration.
      Specified by:
      getPushUserRegistry in interface IPushNotificationEngine
      Returns:
      The registry; never null.
    • getStore

      public SubscriptionStore getStore()
      Gets the store.
      Returns:
      The subscription store.
    • getServerId

      public String getServerId()
      Gets the push server ID (CC-DESIGN-PUSH-APPID-FILTER SV-1): a UUID made at the first engine start on the subscription database and kept there, the same for every server sharing it (SubscriptionStore.ensureServerId()). Every data.iizi this server writes carries it as server (PUSH-WIRE-CONTRACT 6.2), the channel tells the device, and a receipt naming another ID is dropped (PushEvents.report(com.iizix.push.IPushReceipt,String,String)).
      Returns:
      The ID, lower-case, 36 characters; null before start() has read it.
    • getUsers

      public UserDirectory getUsers()
      Gets the user directory.
      Returns:
      The directory.
    • getConfigs

      public AppPushConfigs getConfigs()
      Gets the application configurations.
      Returns:
      The configurations.
    • getCredentials

      public PushCredentials getCredentials()
      Gets the credentials: the credentials file of PUSH-CREDENTIALS.md and the users registered from Java, the first sources of the HTTP API's authentication and the group definitions of the database users. It is also the IPushUserRegistry, which application code reaches through getPushUserRegistry().
      Returns:
      The credentials, never null.
    • getKeyFiles

      public PushKeyFiles getKeyFiles()
      Gets the key files: the reader of every Push Notification key file of this server (PUSH-KEYFILES-DESIGN.md section 5), shared by the credentials and the application configurations and watched through this engine's watcher.
      Returns:
      The key files, never null.
    • getWatcher

      public PushFileWatcher getWatcher()
      Gets the engine's file watcher: one per engine, created at the first file to watch - the credentials file or any key file - and closed by dispose() (design 7.3, S-5).
      Returns:
      The watcher, or null while nothing has been watched or after dispose().
    • getResolver

      public PushResolver getResolver()
      Gets the resolver.
      Returns:
      The resolver.
    • getSpool

      public PushSpool getSpool()
      Gets the spool.
      Returns:
      The spool.
    • getEvents

      public PushEvents getEvents()
      Gets the events hub the sessions' Push Notification listeners are fed from: a subscription stored or removed, a send target that failed (P4-04).
      Returns:
      The events.
    • getTransport

      public IPushTransport getTransport(PushTransport kind)
      Gets a transport.
      Parameters:
      kind - The kind.
      Returns:
      The transport, or null when the engine has none of that kind.
    • getTransportState

      public TransportState getTransportState(PushTransport kind, String appId)
      Tells whether a transport can send for an application, and when not, why: the ONE check behind queryAvailableTransports and a client session's subscribe (CC-FIX-PUSH-V5 G-3, CC-V5-AUDIT-PUSH VP-D-3), so that a device is never subscribed under a configuration whose public half alone this server holds - a VAPID public key whose private key is not in this machine's secrets store, a Firebase project without its service account, a pair that does not match - which would store a row this server can never send to.
      Parameters:
      kind - The transport.
      appId - The application ID, * for the server's own configuration.
      Returns:
      The state: not configured, with the reason, when the engine has no transport of that kind.
    • getMaxParallel

      public int getMaxParallel()
      Gets the ceiling the engine started with.
      Returns:
      The maximum in flight.
    • getInFlight

      public int getInFlight()
      Gets the number of sends in flight now.
      Returns:
      The in-flight count.
    • planSend

      public IRoutingPlan planSend(IPushSendRequest request) throws PushAuthorizationException
      --- Planning and sending ---
      Specified by:
      planSend in interface IPushNotificationEngine
      Parameters:
      request - The send request (filter, notification, policies, authorization context).
      Returns:
      The routing plan that a real send of this request would follow.
      Throws:
      PushAuthorizationException - if the request's operator is not authorized for a region the plan requires.
    • send

      Description copied from interface: IPushNotificationEngine
      Performs a send asynchronously.

      Builds the routing plan, checks authorization, then dispatches each region slice — local slices through this server's send path, remote slices to the region's server over the server-to-server channel — acquiring a concurrency permit per dispatch so the in-flight count never exceeds IPushSettings.getMaxParallelPush(). Unreachable regions and unreachable endpoints spool. The returned future completes when every slice has been resolved, with a PushResult that carries the routing plan and the per-target, per-region, per-tier outcomes.

      To wait a bounded time for completion, call future.get(timeout, unit) on the result — the framework's completion handle is a standard CompletableFuture.

      Specified by:
      send in interface IPushNotificationEngine
      Parameters:
      request - The send request.
      progress - An optional progress callback invoked as slices complete, for bulk sends; may be null.
      Returns:
      A future completing with the aggregate PushResult.
    • sendToUsers

      public PushResultImpl sendToUsers(String appId, String topic, long[] uids, IPushNotification message) throws Exception
      Sends a notification to users by ID for an application and Topic, with the server's default policies and an unrestricted operator, and waits a bounded time.
      Parameters:
      appId - The application ID, * for every application.
      topic - The Topic, empty for every Topic.
      uids - The user IDs.
      message - The notification.
      Returns:
      The result.
      Throws:
      Exception - If the send fails or times out.
    • send

      public PushResultImpl send(String appId, String topic, long[] uids, IPushNotification message, DeliveryUrgency urgency) throws Exception
      Sends a notification for an application and Topic to users by ID, or to every subscriber, with an urgency, the server's default policies otherwise and an unrestricted operator, and waits a bounded time: the HTTP push of PUSH-WIRE-CONTRACT section 5.6.
      Parameters:
      appId - The application ID, * for every application.
      topic - The Topic, empty for every Topic.
      uids - The user IDs, null or empty for every subscriber (the broadcast).
      message - The notification.
      urgency - The urgency, null for the server default.
      Returns:
      The result.
      Throws:
      Exception - If the send fails or times out.
    • spoolFile

      public static File spoolFile(IPushSettings settings, File serverDir)
      Gets the default spool file of a server: <spoolDirectory>/pnspool.xml, the spool directory being the setting when non-empty, else push-spool under the server's current directory.
      Parameters:
      settings - The settings.
      serverDir - The server's current directory.
      Returns:
      The file.