Class EHistoricPublishFeed

  • All Implemented Interfaces:
    EObject, EPublisher, EReplier

    public final class EHistoricPublishFeed
    extends EAbstractHistoricFeed<IEHistoricPublisher>
    implements EPublisher, EReplier
    The historic publisher feed extends EPublishFeed's ability to publish notification messages to subscribers with the ability to:

    Please note that ESubscribers are able to seamlessly access notification messages posted to an historic publish feed. There is no distinction between live-only and historic feeds. Of course an ESubscriber cannot access previously published notification messages.

    Note: an IEMessageStore instance may be shared between multiple EHistoricPublishFeeds but the implementation is responsible for maintaining thread safety.

    EHistoricPublishFeed is similar to using an EPublishFeed:

    Step 1: Implement IEHistoricPublisher interface.

    Step 2: Open an historic publish feed using an EHistoricPublishFeed.Builder instance (obtained using builder(EMessageKey, IEHistoricPublisher)) for a given IEHistoricPublisher instance, type+topic message key, feed scope, and IEMessageStore instance.

    Note: historic notification feeds are dependent on setting a unique publisher identifier. This allows historic feed subscribers to differentiate between publishers.

    Note that when opening an historic publish feed, the configured message store must be open and ready to persist and retrieve historic notification messages.

    Step 3: Start up historic publish feed. Call this method directly rather than using EFeed.register(net.sf.eBus.client.EObject) and EFeed.startup(net.sf.eBus.client.EObject) because EHistoricPublishFeed is an eBus hybrid object and runs in the IEHistoricPublisher's dispatcher. The underlying historic notification reply feed is opened, advertised, and feed state set to up at this time. This means the historic publish feed is able to process historic notification requests even when the publish feed is unadvertised.

    Step 4: Advertise this publisher to eBus. This allows eBus to match publishers with subscribers.

    Step 5: Set feed state to up. This step must be done prior to publishing any notifications. Put another way, notifications cannot be published when the feed state is not up.

    Step 6: Start publishing notifications. Note that this differs from EPublishFeed where the publisher must wait for subscribers prior to posting a notification. This is because notifications need to be persisted despite the lack of subscribers. This allows an IEHistoricSubscriber to access previously published messages when opening its historic subscribe feed.

    It is highly recommended that publishers set ENotificationMessage.position values especially if more than one notification is published per millisecond.

    Step 7: When shutting down the publisher retract notification advertisement and close the historic feed.

    Example use of EHistoricPublishFeed

    import net.sf.eBus.client.EFeed.FeedScope;
    import net.sf.eBus.client.EFeedState;
    import net.sf.eBus.feed.historic.IEHistoricPublisher;
    import net.sf.eBus.feed.historic.EHistoriicPublishFeed;
    import net.sf.eBus.messages.EMessageKey;
    import net.sf.eBus.messages.ENotificationMessage;
    
    // Step 1: Implement IEHistoricPublisher interface.
    public class CatalogPublisher implements IEHistoricPublisher {
        // Roll over notification position when this value is reached.
        private static final int MAX_POSITION = 1_000;
    
        // HistoricReply messages contain up to 10 notifications per reply message.
        private static final int MAX_NOTIFICATIONS_PER_REPLY = 10;
    
        // Publishes this notification message class/subject key.
        private final EMessageKey mKey;
    
        // Unique publisher identifier.
        private final long mPublisherId;
    
        // Catalog publisher name used for logging purposes.
        private final String mName;
    
        // Published messages remain within this scope.
        private final FeedScope mScope;
    
        // Message store for persisting and retrieving notification messages.
        private final IEMessageStore mStore;
    
        // Advertise and publish on this feed.
        private EHistoricPublishFeed mFeed;
    
        // Latest message position. Incremented on each notification publish.
        // Value is reset to zero when MAX_POSITION is reached.
        private int mPosition;
    
        public CatalogPublisher(final String subject,
                                final long pubId,
                                final FeedScope scope,
                                final IEMessageStore store) {
            mKey = new EMessageKey(CatalogUpdate.class, subject);
            mPublisherId = pubId;
            mName = (this.getClass()).getSimpleName + "-" + pubId;
            mScope = scope;
            mStore = store;
            mFeed = null;
            mPosition = 0;
        }
    
        @Override
        public void startup() {
            try {
                // Step 2: Open publish feed. Place IEHistoricPublisher interface method overrides here.
                // This publisher overrides IEHistoricPublisher interface method.
                final EHistoricPublishFeed.Builder builder = EHistoricPublishFeed.builder(this, mKey);
    
                mFeed = builder.name(mName)
                               .scope(mScope)
                               .messageStore(mStore)
                               .notificationsPerReply(MAX_NOTIFICATIONS_PER_REPLY)
                               .build();
    
                // Step 3: Start up this publisher.
                mFeed.startup();
    
                // Step 4: Advertise this publisher to eBus.
                mFeed.advertise();
    
                // Step 5: Inform the world that this publisher's feed state is up.
                mFeed.updateFeedState(EFeedState.UP);
            } catch (IllegalArgumentException argex) {
                // Advertisement failed. Place recovery code here.
            }
        }
    
        // Note: this method is never called by EHistoricPublishFeed since the publishStatus is always up once
        // historic publish feed is opened, advertised, and publish feed state is set to up.
        @Override
        public void publishStatus(final EFeedState feedState, final EPublishFeed feed) {
            ...
        }
    
        // Step 6: Start publishing notifications.
        public void updateProduct(final String productName, final Money price, final int stockQty) {
            if (mFeed != null && mFeed.inPlace()) {
                mFeed.publish((CatalogUpdate.builder()).subject(mKey.subject)
                                                       .timestamp(Instant.now())
                                                       .publisherId(mPublisherId)
                                                       .position(mPosition)
                                                       .productName(productName)
                                                       .price(price)
                                                       .inStockQuantity(stockQty)
                                                       .build());
    
                ++mPosition;
    
                // Reset position?
                if (mPosition == MAX_POSITION) {
                    mPosition = 0;
                }
            }
        }
    
        // Retract the notification feed.
        @Override
        public void shutdown() {
            Step 7: On shutdown either unadvertise or close publish feed.
            if (mFeed != null) {
                // unadvertise() unnecessary since close() retracts an in-place advertisement.
                mFeed.close();
                mFeed = null;
            }
        }
    }

    Historic Publisher and Message Exhaust

    eBus release 6.2.0 introduced message exhaust interface which allows all messages (notification, request, reply) flowing through eBus to be exhausted to persistent store. The difference between this and an historic publisher is that the historic publisher persists a notification message even if it is not published to eBus which message exhaust persists only those messages successfully published to eBus. The reason for historic feeds is to allow subscribers to retrieve historic and live notification messages in a seamless manner. The reason for message exhaust is to track what messages flowed through eBus. This allows the user to recreate what happened when a system error occurred.
    Author:
    Charles W. Rapp
    See Also:
    EPublishFeed, IEHistoricPublisher, EHistoricSubscribeFeed, IEHistoricSubscriber
    • Field Detail

      • DEFAULT_NOTIFICATIONS_PER_REPLY

        public static final int DEFAULT_NOTIFICATIONS_PER_REPLY
        Default maximum number of notifications per HistoricReply is 5.
        See Also:
        Constant Field Values
      • DEFAULT_PUBLISH_FEED_NAME

        public static final String DEFAULT_PUBLISH_FEED_NAME
        If historic publish feed name is not set, then defaults to "EHistoricPublishFeed-" appended with feed index.
        See Also:
        Constant Field Values
    • Method Detail

      • name

        public String name()
        Returns historic publish feed's eBus object name. Used for logging purposes only.
        Specified by:
        name in interface EObject
        Returns:
        eBus object name.
      • shutdown

        public void shutdown()
        Closes all extand publish and reply feeds.
        Specified by:
        shutdown in interface EObject
      • publisherId

        public long publisherId()
        Returns historic publisher identifier.
        Returns:
        historic publisher identifier.
      • notificationsPerReply

        public int notificationsPerReply()
        Returns maximum number of historic notifications placed in a historic message reply.
        Returns:
        maximum number of historic messages per reply.
      • isFeedUp

        public boolean isFeedUp()
        Returns true if this historic publish feed is 1) open, 2) advertised, and 3) feed state is up; otherwise returns true
        Returns:
        true if historic publisher is clear to publish notification messages.
      • advertise

        public void advertise()
        Advertises publisher feed and historic reply feed associated with message key. If this historic feed is currently advertised, then does nothing. Historic feed publisher may publish notifications only after both advertising the feed and setting the publish status to up. Note: once the publisher has advertised and marked this historic feed as up, the publisher is free to publish notifications even if there are no subscribers to this feed since all notification messages are persisted.
        Throws:
        IllegalStateException - if this feed is closed.
        See Also:
        unadvertise(), updateFeedState(EFeedState), shutdown()
      • unadvertise

        public void unadvertise()
        Retracts both the notification publish and historic reply feeds. Does nothing if this historic feed is not currently advertised.
        See Also:
        advertise(), shutdown()
      • updateFeedState

        public void updateFeedState​(EFeedState update)
        Updates publish feed state to the given value. If update equals the current state, nothing is done. Otherwise this new publish feed state is forwarded to the underlying EPublishFeed and persisted to this historic message store.
        Parameters:
        update - new publish feed state.
        Throws:
        NullPointerException - if update is null.
        IllegalStateException - if this feed is not advertised.
      • publish

        public void publish​(ENotificationMessage msg)
        First persists this notification to message store and then, if the notification feed is up. Historic publish feeds differ from EPublishFeed in that notifications may be published even when the feed is down.

        If any exception is thrown, this means the message was not persisted.

        Parameters:
        msg - post this notification message to subscribers.
        Throws:
        NullPointerException - if msg is null.
        IllegalArgumentException - if msg message key or publisher identifier does not match the feed's values.
        IllegalStateException - if this feed is not advertised or the publisher has not declared the feed to be up.
      • builder

        public static EHistoricPublishFeed.Builder builder​(EMessageKey key,
                                                           IEHistoricPublisher publisher)
        Returns a new historic publish feed builder for the specified notification message key and publisher identifier. It is recommended that a new builder be obtained for each historic publish feed rather than re-using an existing builder.
        Parameters:
        key - published notification message key.
        publisher - historic publisher associated with this feed.
        Returns:
        new historic publish feed builder.
        Throws:
        NullPointerException - if key or publisher is null.
        IllegalArgumentException - if key is not a notification message.