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

net.sf.eBus.client.ESubject 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 (C) 2010, 2011, 2013-2016. Charles W. Rapp.
// All Rights Reserved.
//

package net.sf.eBus.client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import net.sf.eBus.client.sysmessages.AdMessage;
import net.sf.eBus.client.sysmessages.AdMessage.AdStatus;
import net.sf.eBus.messages.EMessageHeader;
import net.sf.eBus.messages.EMessageKey;
import net.sf.eBus.util.TernarySearchTree;
import net.sf.eBus.util.logging.StatusReporter;
import net.sf.eBus.util.regex.Pattern;

/**
 * The eBus subject performs the actual work of connecting
 * application instances together via their respective
 * {@link EFeed feeds}. {@code ESubject} instances are
 * responsible for connecting {@link EPublisher publishers} to
 * {@link ESubscriber subscribers} and {@link EReplier repliers}
 * with {@link ERequestor requestors}. There is one subject
 * instance for each unique {@link EMessageKey message key}.
 * 

* This class is responsible for maintaining a static ternary * search tree mapping a * {@link EMessageKey#keyString() key string} to the eBus subject * instance for that message key. *

*

* The eBus API is intended to be extended with new feed and * subject classes. These extensions would provide more * sophisticated notification and request/reply types. One * example is a notification feed that combines historical and * live updates or just historical, depending on what the * subscriber requests. This allows a subscriber to join the feed * at any time, not missing any previously posted notifications. *

* * @see ENotifySubject * @see ERequestSubject * @see EFeed * * @author Charles Rapp */ @SuppressWarnings ("unchecked") /* package */ abstract class ESubject implements Comparable { //--------------------------------------------------------------- // Member methods. // //----------------------------------------------------------- // Constructors. // /** * Creates a new subject instance for the given message key. * @param key the unique subject message key. */ protected ESubject(final EMessageKey key) { mKey = key; } // end of ESubject(EMessageKey) // // end of Constructors. //----------------------------------------------------------- //----------------------------------------------------------- // Abstract Method Declarations. // /** * Returns a non-{@code null} {@link AdMessage} if this * subject currently has a local feed which supports a remote * client. Note that the feed state does not need to be * {@link EFeedState#UP}, only advertised. If no such feed * exists, then returns {@code null}. * @param adStatus either add or remote this advertisement. * @return either an {@code AdMessage} or {@code null}. */ /* package */ abstract EMessageHeader localAd(final AdStatus adStatus); // // end of Abstract Method Declarations. //----------------------------------------------------------- //----------------------------------------------------------- // Comparable Interface Implementation. // /** * Returns an integer value <, equal to or > zero * depending on whether {@code this ESubject} * instance is <, equal to or > {@code subject}. * The comparison is based on * {@link net.sf.eBus.messages.EMessageKey}. * @param subject comparison instance. * @return an integer value <, equal to or > zero * depending on whether {@code this ESubject} * instance is <, equal to or > {@code subject}. */ @Override public int compareTo(final ESubject subject) { return (mKey.compareTo(subject.mKey)); } // end of compareTo(ESubject) // // end of Comparable Interface Implementation. //----------------------------------------------------------- //----------------------------------------------------------- // Object Method Overrides. // /** * Returns {@code true} if {@code o} is a * non-{@code null ESubject} whose message key equals * {@code this} subject; otherwise returns {@code false}. * @param o comparison object. * @return {@code true} if {@code o} message keys equals * {@code this} message key. */ @Override public boolean equals(final Object o) { boolean retcode = (this == o); if (!retcode && o instanceof ESubject) { retcode = mKey.equals(((ESubject) o).mKey); } return (retcode); } // end of equals(Object) /** * Returns the message key hash code. * @return the hash code for this subject instance. */ @Override public int hashCode() { return (mKey.hashCode()); } // end of hashCode() /** * Returns the message key text representation. * @return the message key text representation. */ @Override public String toString() { return (mKey.toString()); } // end of toString() // // end of Object Method Overrides. //----------------------------------------------------------- //----------------------------------------------------------- // Get Methods. // /** * Returns this subject message key. * @return this subject message key. */ public final EMessageKey key() { return (mKey); } // end of key() /** * Returns a non-{@code null}, possibly empty, message key * list taken from the current message key dictionary * entries. * @return list message key dictionary entries. */ /* package */ static List findKeys() { final Collection subjects; final List retval = new LinkedList<>(); synchronized (sSubjects) { subjects = sSubjects.values(); } // Put the subject message keys into the returned list. subjects.forEach(subject -> retval.add(subject.key())); return (retval); } // end of findKeys() /** * Returns a non-{@code null}, possibly empty, list of * message keys from the message key dictionary matching the * given regular express query. * @param query match against this pattern. * @return list of message key dictionary entries matching * {@code query}. */ /* package */ static List findKeys(final Pattern query) { final Collection subjects; final List retval = new LinkedList<>(); synchronized (sSubjects) { subjects = sSubjects.values(query); } // Put the subject message keys into the returned list. subjects.forEach(subject -> retval.add(subject.key())); return (retval); } // end of findKeys(Pattern) /** * Returns a list of local advertisements which * support a remote client. If subject returns a * non-{@code null} message and {@code adStatus} is * {@link AdStatus#ADD}, then put the subject's feed state * message on to the list as well. * @param adStatus either add or remove this advertisement. * @return advertise and feed state message list. */ /* package */ static List localAds(final AdStatus adStatus) { EMessageHeader msg; final List retval = new LinkedList<>(); // For each subject, get its ad message. for (ESubject subject : sSubjects.values()) { msg = subject.localAd(adStatus); if (msg != null) { retval.add(msg); } } return (retval); } // end of localAds(AdStatus) // // end of Get Methods. //----------------------------------------------------------- //----------------------------------------------------------- // Set Methods. // /** * Adds the given message key to the subject tree if not * already in the tree. *

* Argument validated prior to this call. *

* @param key add this message key to subject tree. */ /* package */ static void addSubject(final EMessageKey key) { final String keyString = key.keyString(); synchronized (sSubjects) { if (!sSubjects.containsKey(keyString)) { // Is this a notification message? if (key.isNotification()) { // Yes. Then create a notify subject. sSubjects.put( keyString, new ENotifySubject(key)); } // Otherwise this a request message. The caller // has already determined that key is either a // notification or request. else { // Yes. Then create a request subject. sSubjects.put( keyString, new ERequestSubject(key)); } } } return; } // end of addSubject(EMessageKey) /** * Adds all the given message keys to the message key tree * but only if the key is not already in the tree. *

* Argument validated prior to this call. *

* @param keys add these message keys to subject tree. */ /* package */ static void addAllSubjects(final Collection keys) { synchronized (sSubjects) { keys.forEach( key -> { final String keyString = key.keyString(); if (!sSubjects.containsKey(keyString)) { // Is this a notification message? if (key.isNotification()) { // Yes. Then create a notify subject. sSubjects.put( keyString, new ENotifySubject(key)); } // Otherwise this a request message. The // caller has already determined that key // is either a notification or request. else { // Yes. Then create a request subject. sSubjects.put( keyString, new ERequestSubject(key)); } } }); } return; } // end of addAllSubjects(Collection<>) // // end of Set Methods. //----------------------------------------------------------- /** * Writes the entire message key dictionary to the given * object output stream. The caller is responsible for * opening and closing the object output stream. *

* Only message keys are written to the object output stream. * The associated eBus subjects and their related feeds are * not stored. When the message key is re-loaded from the * object stream at application start, the eBus subjects are * recreated but not the feeds. Feeds must be re-opened by * the application upon start. *

* @param oos load message keys to this object output stream. * @throws IOException * if an error occurs writing message keys to {@code oos}. * * @see #storeKeys(Pattern, ObjectOutputStream) * @see #loadKeys(ObjectInputStream) */ /* package */ static void storeKeys(final ObjectOutputStream oos) throws IOException { final Collection keys; synchronized (sSubjects) { keys = sSubjects.values(); } storeKeys(keys, oos); return; } // end of storeKeys(ObjectOutputStream) /** * Writes those message keys matching the given query to * the object output stream. * @param query message key regular expression pattern. * @param oos object output stream. * @throws IOException * if an error occurs writing message keys to {@code oos}. */ /* package */static void storeKeys(final Pattern query, final ObjectOutputStream oos) throws IOException { final Collection keys; synchronized (sSubjects) { keys = sSubjects.values(query); } storeKeys(keys, oos); return; } // end of storeKeys(Pattern, ObjectOutputStream) /** * Loads the message keys extracted from the object input * stream back into the eBus message key dictionary. Note: * existing message keys are not overwritten or replaced by * the keys found in {@code ois}. * @param ois read in message keys from this object input * stream. * @throws IOException * if an error occurs reading in message keys. */ /* package */ static void loadKeys(final ObjectInputStream ois) throws IOException { final int numKeys = ois.readInt(); int i; EMessageKey key; String keyString; synchronized (sSubjects) { for (i = 0; i < numKeys; ++i) { try { key = (EMessageKey) ois.readObject(); keyString = key.keyString(); // Make sure the message key is not already // defined. if (!sSubjects.containsKey(keyString)) { // Keys are either notification or // request. if (key.isNotification()) { sSubjects.put( keyString, new ENotifySubject(key)); } else { sSubjects.put( keyString, new ERequestSubject(key)); } } } catch (ClassNotFoundException classex) { throw ( new IOException( "ois contains a non-EMessageKey object", classex)); } } } return; } // end of loadKeys(ObjectInputStream) /** * Performs the actual work of writing message keys to the * object output stream. * @param subjects write subject keys to {@code oos}. * @param oos output message keys to this object output * stream. * @throws IOException * if an error occurs writing the message keys to the object * output stream. */ private static void storeKeys(final Collection subjects, final ObjectOutputStream oos) throws IOException { // Store the number of message key entries first. oos.writeInt(subjects.size()); for (ESubject subject : subjects) { // Now write out each message key. oos.writeObject(subject.key()); } return; } // end of storeKeys(Collection<>) //--------------------------------------------------------------- // Member data. // /** * The subject message key. */ protected final EMessageKey mKey; //----------------------------------------------------------- // Statics. // /** * Maps the {@link EMessageKey} to its eBus subject. * {@link EMessageKey#keyString} is used for the mapping. */ protected static final TernarySearchTree sSubjects = new TernarySearchTree<>(); //--------------------------------------------------------------- // Inner classes. // /** * Adds the subject information to the periodic status * report for all extant subjects. */ private static final class SubjectStatusReporter implements StatusReporter { //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // /** * Creates the singleton eBus subject status reporter. */ public SubjectStatusReporter() {} // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // StatusReporter Interface Implementation. // /** * Appends the client and subject count to the periodic * status report. * @param report periodic status report. */ @Override public void reportStatus(final PrintWriter report) { final int clientCount = EClient.clientCount(); final int subjectCount = sSubjects.size(); report.println(); report.println("EClient:"); report.format(" clients: %,d%n", clientCount); report.println(); report.println("eBus Subject:"); report.format(" subjects: %,d%n", subjectCount); return; } // end of reportStatus(PrintWriter) // // end of StatusReporter Interface Implementation. //------------------------------------------------------- //----------------------------------------------------------- // Member data. // } // end of class SubjectStatusReporter } // end of class ESubject




© 2015 - 2025 Weber Informatics LLC | Privacy Policy