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

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

The newest version!
//
// Copyright 2021 Charles W. Rapp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package net.sf.eBus.client;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.eBus.client.EClient.ClientLocation;
import net.sf.eBus.client.EFeed.FeedScope;
import net.sf.eBus.client.sysmessages.McastKeyMessage;
import net.sf.eBus.client.sysmessages.McastSubscribeMessage;
import net.sf.eBus.client.sysmessages.SystemMessageType;
import net.sf.eBus.config.EConfigure.MulticastConnection;
import net.sf.eBus.config.EConfigure.MultifeedType;
import net.sf.eBus.messages.EMessage;
import net.sf.eBus.messages.EMessageHeader;
import net.sf.eBus.messages.EMessageKey;
import net.sf.eBus.messages.ENotificationMessage;
import net.sf.eBus.messages.type.DataType;
import net.sf.eBus.messages.type.MessageType;
import net.sf.eBus.util.MultiKey2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * This class is responsible for subscribing to notification
 * messages from its multicast connection. This means it
 * maintains a {@link EMultiPublishFeed multi-feed publisher} to
 * eBus. The notification messages received from the multicast
 * group are then posted to the multi-feed if the
 * inbound message
 * 

* The multi-feed message keys are either fixed or dynamic. A * fixed feed consists of either a subject list or a non-dynamic * pattern. If a list, then only those subjects in that list are * published to eBus from the multicast group. If a non-dynamic * pattern, then only those subjects which match the query upon * opening the feed are used. The problem here is that if there * are no matching feeds on open then no messages will be * forwarded from the multicast group to eBus. *

*

* A dynamic feed uses a subject pattern. Only those * notifications published on the feed which match that pattern * are forwarded to eBus. If new subjects are added to eBus * after opening the multi-feed which match the pattern, those * new subjects will be forwarded by the multicast subscriber. *

* * @see EMulticastConnection * @see EMulticastPublisher * * @author Charles W. Rapp */ public final class EMulticastSubscriber extends EMulticastConnection implements EPublisher { //--------------------------------------------------------------- // Member data. // //----------------------------------------------------------- // Statics. // /** * Logging subsystem interface. */ private static final Logger sLogger = LoggerFactory.getLogger(EMulticastSubscriber.class); //----------------------------------------------------------- // Locals. // /** * Maps notification class to its multi-feed publisher. */ private final Map mFeeds; //--------------------------------------------------------------- // Member methods. // //----------------------------------------------------------- // Constructors. // /** * Creates a new instance of EMulticastSubscriber. */ /* package */ EMulticastSubscriber(final MulticastConnection config) { super (config); mFeeds = new ConcurrentHashMap<>(); } // end of EMulticastSubscriber(MulticastConnection) // // end of Constructors. //----------------------------------------------------------- //----------------------------------------------------------- // Abstract Method Overrides. // /** * Opens a multi-feed subscriber for each of the multicast * message keys. Multi-feed subscriber type will be either * list-based or query-based depending on the multicast * message key configuration. */ @Override protected void onOpen() { Class mc; String mcName; EMultiPublishFeed.Builder builder; EMultiPublishFeed feed; sLogger.debug("{}: open EMultiPublishFeeds.", mName); for (MulticastKey key : mMulticastKeys.values()) { mc = key.messageClass(); mcName = mc.getCanonicalName(); mIsDynamic = (mIsDynamic || key.isDynamic()); builder = EMultiPublishFeed.builder(); sLogger.trace("{}: opening multi-feed {}.", mName, mcName); // Fixed subject list or dynamic subject query? if (key.feedType() == MultifeedType.LIST) { // Fixed subject list. feed = builder.target(this) .location(ClientLocation.REMOTE) .messageClass(mc) .subjects(key.subjectList()) .scope(FeedScope.LOCAL_ONLY) .build(); } // Dynamic subject query. Set the initial subject // list to an empty list because the query is used else { feed = builder.target(this) .location(ClientLocation.REMOTE) .messageClass(mc) .query(key.subjectQuery()) .scope(FeedScope.LOCAL_ONLY) .build(); } mFeeds.put(mcName, feed); } } // end of onOpen() /** * When the multicast group is successfully joined, the * configured notification feeds are advertised to eBus and * a {@link McastSubscribeMessage} is multicast to the * group publishers. */ @Override protected void onConnect() { final EMessageHeader header = new EMessageHeader( (SystemMessageType.MCAST_SUBSCRIBE_KEY).keyId(), ERemoteApp.NO_ID, ERemoteApp.NO_ID, (McastSubscribeMessage.builder()).build()); sLogger.debug( "{}: advertising EMultiPublishFeeds.", mName); mFeeds.values().forEach(this::advertiseFeed); // Multcast the McastSubscribeMessage to retrieve the // current multicast publisher feed state. send(header, mTarget); } // end of onConnect() /** * Marks all published multi-feeds as down as a result of the * multicast group disconnect. */ @Override protected void onDisconnect() { final Collection feeds = new ArrayList<>(mFeeds.values()); feeds.forEach(this::feedStateDown); } // end of onDisconnect /** * Closes all multi-feed publishers and then discards them. */ @Override protected void onClose() { final Collection feeds = new ArrayList<>(mFeeds.values()); sLogger.debug("{}: closing EMultiPublishFeeds.", mName); mFeeds.clear(); feeds.forEach(EMultiPublishFeed::close); } // end of onClose() /** * Handles an inbound multicast message. If the message is a * notification supported by this multicast subscriber and * that feed's state is up, then the notification message is * published on eBus. If the message is an * {@link McastKeyMessage} which references a supported feed * then the feed's state is updated as per the message * contents. All other messages are ignored. * @param msg inbound multicast message. * @param source message source address. */ @Override protected void onMessage(final EMessage msg, final InetSocketAddress source) { final Class msgClass = msg.getClass(); if (sLogger.isTraceEnabled()) { sLogger.trace( "{}: received {} {} message:\n{}", mName, msg.key(), msg.messageType(), msg); } else { sLogger.debug("{}: received {} {} message.", mName, msg.key(), msg.messageType()); } // Is this a notification? if ((ENotificationMessage.class).isAssignableFrom(msgClass)) { final EMultiPublishFeed feed = mFeeds.get((msg.getClass()).getCanonicalName()); // Yes. Is this a supported message class? // Is this a supported message subject? if (feed != null && feed.isSubject(msg.subject)) { // Yes in both casess. feed.publish((ENotificationMessage) msg); } } // Is this a publisher status update? else if (McastKeyMessage.class.equals(msgClass)) { final McastKeyMessage keyMsg = (McastKeyMessage) msg; final EMultiPublishFeed feed = mFeeds.get(keyMsg.messageClass); sLogger.trace( "{}: checking if {} is a supported feed.", mName, keyMsg.messageClass); // Yes. Is this a supported message class? if (feed != null) { // Yes. Update the message key feed state. onKeyUpdate(keyMsg, source); } } } // end of onMessage(EMessage, InetSocketAddress) // // end of Abstract Method Overrides. //----------------------------------------------------------- //----------------------------------------------------------- // EPublisher Interface Implementation. // /** * Logs the change in a feed's publish state. * @param feedState latest feed publish state. * @param feed update applies to this feed. */ @Override public void publishStatus(final EFeedState feedState, final IEPublishFeed feed) { sLogger.debug("{}: setting feed {} to {}.", mName, feed.key(), feedState); } // end of publishStatus(EFeedState, IEPublishFeed) // // end of EPublisher Interface Implementation. //----------------------------------------------------------- /** * Advertises the published multi-feed to eBus and sets the * publish feed state to up. * @param feed advertise this multi-feed. */ private void advertiseFeed(final EMultiPublishFeed feed) { sLogger.trace("{}: advertising {}, feed is up.", mName, feed.key()); feed.advertise(); feed.updateFeedState(EFeedState.UP); } // end of advertiseFeed(EMultiPublishFeed) /** * Sets given multi-feed publish state to down. * @param feed this multi-feed publish state is down. */ private void feedStateDown(final EMultiPublishFeed feed) { sLogger.trace("{}: {} feed is down.", mName, feed.key()); if (feed.isAdvertised()) { feed.updateFeedState(EFeedState.DOWN); } } // end of feedStateDown() /** * Updates the message feed state as per the given * multicast key message. If the message key does not * appear in the supported key maps but is supported by this * multicast subscriber, then a new message key is created * and its supporting information is placed into the maps. * Finally if the message keys's new multicast feed state * has changed then corresponding multi-feed state is also * updated. * @param msg contains message key feed's latest state. * @param source message source address. */ @SuppressWarnings({"java:S3776"}) private void onKeyUpdate(final McastKeyMessage msg, final InetSocketAddress source) { final EFeedState feedState = msg.feedState; final MultiKey2 mapKey = new MultiKey2<>(msg.multicastId, msg.keyId); final EMessageKey msgKey; final KeyInfo keyInfo; sLogger.trace( "{}: updating map key {} message key {} to {}.", mName, mapKey, msg.messageKey(), feedState); // Is this a new key? if (mKeys.containsKey(mapKey)) { // No. Use the existing key information. msgKey = mKeys.get(mapKey); keyInfo = mKeyIds.get(msgKey); } // This key is not known. Create a new key if-and-only-if // this subject is supported by this subscriber. else { keyInfo = createKeyInfo(msg, source); // Was a new multicast key created? if (keyInfo != null) { // Yes. Add the message key to the maps. msgKey = keyInfo.key(); sLogger.trace( "{}: adding new keys {} and {} to maps.", mName, keyInfo, msgKey); mKeys.put(mapKey, msgKey); mKeyIds.put(msgKey, keyInfo); } // No and that means the message key is not supported // by this multicast subscriber. } // Is there a key to update? // Did the key's feed state change? if (keyInfo == null) { // No. sLogger.trace( "{}: {} {} is an unsupported message key.", mName, mapKey, msg.messageKey()); } else if (keyInfo.update(feedState, source)) { // Yes and yes. Update the matching multi-feed state. final EMultiPublishFeed feed = mFeeds.get(msg.messageClass); final String subject = msg.messageSubject; sLogger.trace("{}: {} {} is {}.", mName, source, msg.messageKey(), feedState); // If this subject is *not* a part of the multi-feed // publisher, then add it in first. This will happen // if this multicast subscriber has a dynamic query // matching subject. if (!feed.isSubject(subject)) { feed.addFeed(subject); } feed.updateFeedState(subject, keyInfo.state()); } else if (sLogger.isTraceEnabled()) { sLogger.trace("{}: {} {} {} feed state unchanged.", mName, source, msg.messageKey(), feedState); } } // end of onKeyUpdate(McastKeyMessage, InetSocketAddress) /** * Returns a new message key information object based on the * given multicast key update message. Returns {@code null} * if the updated message key is not supported by this * multicast subscriber. This is the case if * {@link McastKeyMessage#messageSubject} is not in a * {@code EMultiPublishFeed}'s subject list or does not * match a dynamic subject pattern. * @param msg multicast message key update message. * @param source message source. * @return new multicast key information or {@code null}. */ private KeyInfo createKeyInfo(final McastKeyMessage msg, final InetSocketAddress source) { final String subject = msg.messageSubject; final MulticastKey mcastKey = mMulticastKeys.get(msg.messageClass); final EMultiPublishFeed feed = mFeeds.get(msg.messageClass); KeyInfo retval = null; // Is this feed dynamic and does this subject match a // dynamic pattern? // Or is this one of the fixed subjects? if ((mcastKey != null && mcastKey.matches(subject)) || (feed != null && feed.isSubject(subject))) { // Yes this is a supported subject. final EMessageKey msgKey = msg.messageKey(); final MessageType mt = (MessageType) DataType.findType( msgKey.messageClass()); retval = new KeyInfo(msgKey, source, msg.multicastId, msg.keyId, EFeedState.UNKNOWN, new MessageReader( msg.keyId, subject, mt)); } return (retval); } // end of createKeyInfo(McastKeyMessage, InetSocketAddress) } // end of class EMulticastSubscriber




© 2015 - 2024 Weber Informatics LLC | Privacy Policy