Class PNSpoolEntry

All Implemented Interfaces:
EventListener, IGProp<GProp<?>[]>, IPropCnr, Cloneable

public class PNSpoolEntry extends PropCnr
One pending delivery in the push spool: a PNMessageProp payload plus the bookkeeping that says where it was going and when to try again.

Why a wrapper. Where a notification was going and when to retry it are properties of the attempt, not of the notification, so they live here and PNMessageProp stays pure payload. The same message object can therefore be spooled for several targets without any of them contaminating it.

Server-only. A spool entry never reaches a JavaScript client: it goes to disk as XML and comes back, Java to Java. Its own leaves may therefore use any Java property type, unlike the message it wraps. It is nevertheless registered in com/iizix/schema/base-properties.xml like every other serialised class, because PropFactory refuses to write an element for a class it has no entry for.

AN ENTRY IS IMMUTABLE ONCE IT HAS BEEN ADDED TO A PNSpoolerRoot. This is not a style preference; it is what makes the spool safe to snapshot. PropCnr.clone() takes the root's child array inside the tree and instance monitors but deep-clones the children outside them, so a clone-then-write persist is consistent only if no child mutates while it is being copied. Consequently:

  • a retry is remove-then-add-new: build a fresh entry carrying the incremented retry count and the new next-retry time, add it, and remove the old one;
  • never setInt(RETRY_COUNT,...), setNextRetryTime(...) or any other in-place edit on an entry that is already a child of the spool root.
The setters on this class exist to build an entry, before it is added. After that they must not be called. Everything else about concurrency is already solved by the framework - every structural mutation of a container takes the tree lock and then the container's own monitor - so the spool needs no locking layer of its own; it needs only this rule.

Temporal fields are DateTimeProp holding a ZonedDateTime, never a long and never a NumberProp. A ZonedDateTime carries the zone's identity and its rules, so a reader in another region can derive the correct offset for any date, daylight saving included - which a frozen OffsetDateTime cannot, and which a naked millisecond count loses entirely. It also sidesteps the NumberProp double precision cliff at 253, because it is a temporal object and not a number.

Author:
Christopher Mindus
  • Field Details

    • MESSAGE

      public static final String MESSAGE
      The notification payload property: "message". A PNMessageProp.
      See Also:
    • TARGET

      public static final String TARGET
      The delivery target property: "target", i.e. the subscription, user or region this entry was going to. A StringProp.
      See Also:
    • RETRY_COUNT

      public static final String RETRY_COUNT
      The attempt counter property: "retryCount". An IntProp.
      See Also:
    • CREATED_AT

      public static final String CREATED_AT
      The creation timestamp property: "createdAt". A DateTimeProp holding a ZonedDateTime. This is the primary key of the spool's replay order.
      See Also:
    • NEXT_RETRY_TIME

      public static final String NEXT_RETRY_TIME
      The next-attempt timestamp property: "nextRetryTime". A DateTimeProp holding a ZonedDateTime.
      See Also:
    • TTL_DEADLINE

      public static final String TTL_DEADLINE
      The expiry timestamp property: "ttlDeadline". A DateTimeProp holding a ZonedDateTime. An entry past this instant is dropped rather than replayed.
      See Also:
    • MESSAGE_ATOM

      public static final Atom MESSAGE_ATOM
      The atom of the notification payload property, i.e. Atom.get(MESSAGE ). A PNMessageProp added to a spool entry must carry this atom, so it is public: construct the payload as new PNMessageProp(PNSpoolEntry.MESSAGE_ATOM).
  • Constructor Details

    • PNSpoolEntry

      public PNSpoolEntry()
      Creates a spool entry container without name and with a null value.
    • PNSpoolEntry

      public PNSpoolEntry(Atom propertyAtom)
      Creates a spool entry container with the specified name with a null value.
      Parameters:
      propertyAtom - The property atom.
    • PNSpoolEntry

      public PNSpoolEntry(Atom propertyAtom, PNMessageProp message, String target, ZonedDateTime createdAt, ZonedDateTime nextRetryTime, ZonedDateTime ttlDeadline, int retryCount) throws PropException
      Creates a complete spool entry ready to be added to a PNSpoolerRoot.

      Once it has been added, the entry must not be modified: see the class documentation.

      Parameters:
      propertyAtom - The property atom, unique within the spool root.
      message - The notification payload, which must carry MESSAGE_ATOM and must not already have a parent. The instance is added as a child, not copied.
      target - The subscription, user or region this entry is going to.
      createdAt - When the entry was created.
      nextRetryTime - When the next attempt is due.
      ttlDeadline - When the entry expires and must be dropped rather than replayed.
      retryCount - The number of attempts made so far.
      Throws:
      PropException - If a property cannot be added, or if the payload does not carry MESSAGE_ATOM.
  • Method Details

    • clone

      public PNSpoolEntry clone()
      Creates a clone out of this property.
      Overrides:
      clone in class PropCnr
      Returns:
      The clone of the spool entry.
    • getMessage

      public PNMessageProp getMessage()
      Gets the notification payload.
      Returns:
      The payload, or null for a malformed entry that carries none.
    • getTarget

      public String getTarget()
      Gets the delivery target, i.e. the subscription, user or region this entry was going to.
      Returns:
      The target, or null when not set.
    • setTarget

      public boolean setTarget(String target)
      Sets the delivery target. Call only while building the entry, before it is added to the spool root.
      Parameters:
      target - The target.
      Returns:
      true when this property container has been changed, false otherwise.
    • getRetryCount

      public int getRetryCount()
      Gets the number of delivery attempts made so far.
      Returns:
      The retry count, or zero when not set.
    • setRetryCount

      public boolean setRetryCount(int retryCount)
      Sets the number of delivery attempts made so far. Call only while building the entry, before it is added to the spool root: incrementing this in place on a live spool child is exactly the mutation the class documentation forbids.
      Parameters:
      retryCount - The retry count.
      Returns:
      true when this property container has been changed, false otherwise.
    • getCreatedAt

      public ZonedDateTime getCreatedAt()
      Gets the creation timestamp.
      Returns:
      The creation timestamp, or null when not set or not a zoned date/time.
    • setCreatedAt

      public boolean setCreatedAt(ZonedDateTime createdAt) throws PropException
      Sets the creation timestamp. Call only while building the entry.
      Parameters:
      createdAt - The creation timestamp, carrying the originating server's own zone.
      Returns:
      true when this property container has been changed, false otherwise.
      Throws:
      PropException - If the property cannot be added.
      NullPointerException - If createdAt is null.
    • getNextRetryTime

      public ZonedDateTime getNextRetryTime()
      Gets the timestamp of the next delivery attempt.
      Returns:
      The next-attempt timestamp, or null when not set or not a zoned date/time.
    • setNextRetryTime

      public boolean setNextRetryTime(ZonedDateTime nextRetryTime) throws PropException
      Sets the timestamp of the next delivery attempt. Call only while building the entry.
      Parameters:
      nextRetryTime - The next-attempt timestamp.
      Returns:
      true when this property container has been changed, false otherwise.
      Throws:
      PropException - If the property cannot be added.
      NullPointerException - If nextRetryTime is null.
    • getTTLDeadline

      public ZonedDateTime getTTLDeadline()
      Gets the expiry timestamp, past which the entry is dropped rather than replayed.
      Returns:
      The expiry timestamp, or null when not set or not a zoned date/time.
    • setTTLDeadline

      public boolean setTTLDeadline(ZonedDateTime ttlDeadline) throws PropException
      Sets the expiry timestamp. Call only while building the entry.
      Parameters:
      ttlDeadline - The expiry timestamp.
      Returns:
      true when this property container has been changed, false otherwise.
      Throws:
      PropException - If the property cannot be added.
      NullPointerException - If ttlDeadline is null.