All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.eBus.client.EMultiSubscribeFeed Maven / Gradle / Ivy

//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General
// Public License along with this library; if not, write to the
//
// Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330,
// Boston, MA
// 02111-1307 USA
//
// The Initial Developer of the Original Code is Charles W. Rapp.
// Portions created by Charles W. Rapp are
// Copyright 2017. Charles W. Rapp
// All Rights Reserved.
//

package net.sf.eBus.client;

import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import net.sf.eBus.client.EClient.ClientLocation;
import net.sf.eBus.client.EFeed.FeedScope;
import static net.sf.eBus.client.EMultiFeed.sLogger;
import net.sf.eBus.messages.EMessageKey;
import net.sf.eBus.messages.ENotificationMessage;
import net.sf.eBus.util.regex.Pattern;

/**
 * This feeds acts as a proxy for handling multiple
 * {@link ESubscribeFeed}s on behalf of a {@link ESubscriber}
 * client. A subscriber opens a multi-key subscribe feed for a
 * specified notification message class and zero or more message
 * subjects. There subjects may be specified as a list or a
 * regular expression query If a query is used, then the message
 * class and subject query are used to to search the message key
 * dictionary for all matching subjects. The matching subjects
 * are used to create the initial subordinate
 * {@code ESubjectFeed}s.
 * 

* The multi-key subscribe feed coordinates the subordinate * subscribe feeds to they are given the same configuration and * are in the same state (open, subscribed, un-subscribed, * closed). *

*

* While the multi-key feed is open, new subordinate subscribe * feeds may be {@link #addFeed(String) added to} or * {@link #closeFeed(String) removed from} the multi-key feed. * Newly added subordinate feeds are configured and put into the * same state as the existing subordinate feeds. *

* * @author Charles W. Rapp */ public final class EMultiSubscribeFeed extends EMultiFeed implements IESubscribeFeed { //--------------------------------------------------------------- // Member data. // //----------------------------------------------------------- // Statics. // /** * Lambda expression used to create a new subordinate * subscribe feed. */ private static final SubordinateFeedFactory sSubFactory = (cl, key, sc, cond, loc) -> ESubscribeFeed.open(cl, key, sc, cond, loc, true); /** * Lambda expression used to create a new multi-key subscribe * feed. */ private static final MultiFeedFactory sMultiFactory = (cl, mc, sc, cond, feeds) -> new EMultiSubscribeFeed(cl, mc, sc, cond, feeds); //----------------------------------------------------------- // Locals. // /** * Feed status callback. If not explicitly set by client, * then defaults to * {@link ESubscriber#feedStatus(EFeedState, ESubscribeFeed)}. * Applied to all subordinate subscribe feeds. */ private FeedStatusCallback mStatusCallback; /** * Notification message callback. If not explicity set by * client, then defaults to * {@link ESubscriber#notify(ENotificationMessage, ESubscribeFeed)}. * Applied to all subordinate subscribe feeds. */ private NotifyCallback mNotifyCallback; //--------------------------------------------------------------- // Member methods. // //----------------------------------------------------------- // Constructors. // /** * Creates a new multi-key subscribe feed instance for the * given client, scope, feeds, and condition. Note: * {@code feeds} may be dynamic meaning that new * {@code ESubscribeFeed} instances may be added to the * {@code feeds} list while this multi-key feed is active. * @param client connect this client to the subordinate * {@code ESubscribeFeed}s. * @param mc all feeds apply to this notification message * class. * @param scope subscribe feed scope. * @param condition subscribe condition applied to all * subordinate feeds. * @param feeds initial subordinate subscribe feed list. */ private EMultiSubscribeFeed(final EClient client, final Class mc, final FeedScope scope, final ECondition condition, final Map feeds) { super (client, mc, scope, condition, feeds); mStatusCallback = null; mNotifyCallback = null; } // end of EMultiSubscribeFeed(...) // // end of Constructors. //----------------------------------------------------------- //----------------------------------------------------------- // IESubscribeFeed Interface Implementations. // /** * Puts the feed status callback in place. If {@code cb} * is not {@code null}, feed status updates will be passed * to {@code cb} rather than * {@link ESubscriber#feedStatus(EFeedState, IESubscribeFeed)}. * The reverse is true if {@code cb} is {@code null}. That * is, a {@code null cb} means feed status updates are * posted to the * {@link ESubscriber#feedStatus(EFeedState, IESubscribeFeed)} * override. *

* The status callback is applied to all subordinate * {@code ESubscribeFeed}s. This means that each subordinate * feed calls back the same method. *

* @param cb the feed status update callback. May be * {@code null}. * @throws IllegalStateException * if this feed is either closed or subscribed. */ @Override public void statusCallback(final FeedStatusCallback cb) { if (!mIsActive.get()) { throw ( new IllegalStateException("feed is inactive")); } if (mInPlace) { throw ( new IllegalStateException( "subscription in place")); } mStatusCallback = cb; return; } // end of statusCallback(FeedStatusCallback<>) /** * Puts the notification message callback in place. If * {@code cb} is not {@code null}, then notification messages * will be passed to {@code cb} rather than * {@link ESubscriber#notify(ENotificationMessage, IESubscribeFeed)}. * A {@code null cb} means that notification messages will be * passed to the * {@link ESubscriber#notify(ENotificationMessage, IESubscribeFeed)} * override. *

* The notify callback is applied to all subordinate * {@code ESubscribeFeed}s. This means that each subordinate * feed passes inbound notification messages to the same * method. *

* @param cb pass notification messages back to application * via this callback. * @throws IllegalStateException * if this feed is either closed or subscribed. */ @Override public void notifyCallback(final NotifyCallback cb) { if (!mIsActive.get()) { throw ( new IllegalStateException("feed is inactive")); } if (mInPlace) { throw ( new IllegalStateException( "subscription in place")); } mNotifyCallback = cb; return; } // end of notifyCallback(NotifyCallback) /** * Subscribes each subordinate {@link ESubscribeFeed}. If * this feed is currently subscribed, then does nothing. The * subscriber client will receive a * {@link ESubscriber#feedStatus(EFeedState, IESubscribeFeed)} * callback from each subordinate subscribe feed. * @throws IllegalStateException * if this feed is closed or the client did not override * {@link ESubscriber} methods nor put the required callback * in place. * * @see #unsubscribe() * @see #close() */ @Override public void subscribe() { if (!mIsActive.get()) { throw ( new IllegalStateException("feed is inactive")); } if (!mInPlace) { if (sLogger.isLoggable(Level.FINER)) { sLogger.finer( String.format( "%s multi-key subscriber %d: subscribing (%s).", mEClient.location(), mEClient.clientId(), mScope)); } // Subscribe each subordinate feed. mFeeds.values() .stream() .map( feed -> { feed.statusCallback(mStatusCallback); feed.notifyCallback(mNotifyCallback); return (feed); }) .forEachOrdered(ESubscribeFeed::subscribe); // This feed is now advertised. mInPlace = true; } return; } // end of subscribe() /** * Retracts this multi-key subscribe feed by un-subscribing * each subordinate subscribe feed. Does nothing if this * feed is not currently subscribed. * @throws IllegalStateException * if this multi-key subscribe feed is closed. * * @see #subscribe() * @see #close() */ @Override public void unsubscribe() { if (!mIsActive.get()) { throw ( new IllegalStateException("feed is inactive")); } if (mInPlace) { if (sLogger.isLoggable(Level.FINER)) { sLogger.finer( String.format( "%s multi-key subscriber %d: unsubscribing (%s).", mEClient.location(), mEClient.clientId(), mScope)); } // Unadvertise each subordinate feed. mFeeds.values() .stream() .forEachOrdered(ESubscribeFeed::unsubscribe); // This feed is no longer subscribed. mInPlace = false; } return; } // end of unsubscribe() // // end of IESubscribeFeed Interface Implementations. //----------------------------------------------------------- //----------------------------------------------------------- // Abstract Method Implementations. // /** * Returns a newly minted subordinate subscribe feed for the * given key. * @param key create feed for this key. * @return a subordinate subscribe feed. */ @Override protected ESubscribeFeed createFeed(final EMessageKey key) { final ESubscriber subscriber = (ESubscriber) mEClient.target(); return (ESubscribeFeed.open(subscriber, key, mScope, mCondition, ClientLocation.LOCAL, true)); } // end of createFeed(EMessageKey) /** * Sets the callbacks and subscribes the {@code feed}. * @param feed subscribe this feed. */ @Override protected void putFeedInPlace(final ESubscribeFeed feed) { // Must set the callbacks (if any) before subscribing. feed.statusCallback(mStatusCallback); feed.notifyCallback(mNotifyCallback); feed.subscribe(); return; } // end of putFeedInPlace(ESubscribeFeed) // // end of Abstract Method Implementations. //----------------------------------------------------------- /** * Returns an open multi-key subscribe feed for the given * notification message class and multiple subjects. Once * opened, the caller can (optionally) set the status and * notify callbacks and subscribe the feed just like * {@link ESubscribeFeed}. *

* Note: {@code client} receives callbacks * for each subordinate {@code ESubscribeFeed} as if it * opened all those feeds directly. If {@code keys} contains * a large number of notification message keys, then * {@code client} must be prepared for feed status and notify * callbacks for each of the subordinate subscribe feeds. *

*

* {@code subjects} may be a non-{@code null}, empty list * resulting in no initial subordinate subscribe feeds opened. * This allows the subscriber to start with an empty * multi-key subscriber feed, {@link #addFeed(String) adding} * subordinate feeds later. *

* @param client the application object publishing the * notification message class and subject. * @param mc notification message class. All feeds apply to * this message class. * @param subjects list of notification message subjects. * May not contain {@code null} or empty strings. * @param scope whether the feed supports local feeds, * remote feeds, or both. * @param condition accept notification messages only if the * messages passes this condition. May be {@code null}. If * {@code null}, then the * {@link ESubscribeFeed#NO_CONDITION default condition} * which accepts all messages is used. * @return a new multiple key subscribe feed for the given * application object and notification message keys. * @throws NullPointerException * if any of the required arguments is {@code null}. * @throws IllegalArgumentException * if {@code subjects} contains an empty string. * * @see #open(ESubscriber, Class, Pattern, EFeed.FeedScope, ECondition) * @see #statusCallback(FeedStatusCallback) * @see #notifyCallback(NotifyCallback) * @see #subscribe() * @see #addFeed(String) * @see #closeFeed(String) * @see EMultiFeed#close() */ public static EMultiSubscribeFeed open(final ESubscriber client, final Class mc, final List subjects, final FeedScope scope, final ECondition condition) { return (openList(client, mc, subjects, scope, condition, sSubFactory, sMultiFactory)); } // end of open(ESubscriber,Class,List<>,FeedScope,ECondition) /** * Returns an open multi-key subscribe feed for a given * notification message class and multiple message subjects. * Once opened, the caller can (optionally) set the status * and notify callbacks and subscribe the feed just like * {@link ESubscribeFeed}. *

* The subordinate subscribe feeds are selected based on the * given notification message class and the regular * expression query. The multi-key subscribe feed is opened * whether the message key dictionary entries match the * message class and subject query or not. In either case, * the application may {@link #addFeed(String) add} more * subordinate feeds to the returned multi-key feed. *

*

* Note: {@code client} receives callbacks * from subordinate {@code ESubscribeFeed} feeds, not the * multi-key feed. If {@code mc} and {@code query} match * 1,000 notification message keys, then {@code client} will * receive feed status and notify callbacks from those 1,000 * subordinate feeds. *

* @param client application object subscribing to the * notification message class and matching subjects. * @param mc the message key query is for this notification * message class only. * @param query message key subject query. * @param scope whether the feed supports local feeds, remote * feeds, or both. * @param condition accept notification messages only if the * messages passes this condition. May be {@code null}. If * {@code null}, then the * {@link ESubscribeFeed#NO_CONDITION default condition} * which accepts all messages is used. * @return a new multiple key subscribe feed for the given * application object and notification message keys matching * the notification message class and query. * @throws NullPointerException * if any of the arguments are {@code null}. * @throws IllegalArgumentException * if any of the arguments is invalid. * * @see #open(ESubscriber, Class, List, EFeed.FeedScope, ECondition) * @see #statusCallback(FeedStatusCallback) * @see #notifyCallback(NotifyCallback) * @see #addFeed(String) * @see #closeFeed(String) * @see #subscribe() * @see #close() */ public static EMultiSubscribeFeed open(final ESubscriber client, final Class mc, final Pattern query, final FeedScope scope, final ECondition condition) { return (openQuery(client, mc, query, scope, condition, sSubFactory, sMultiFactory)); } // end of open(...) } // end of class EMultiSubscribeFeed




© 2015 - 2025 Weber Informatics LLC | Privacy Policy