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

net.sf.eBusx.monitor.Monitor Maven / Gradle / Ivy

//
// Copyright 2011, 2012, 2015, 2016, 2019, 2024 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.eBusx.monitor;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
import net.sf.eBus.client.EClient;
import net.sf.eBus.client.EClient.ClientLocation;
import net.sf.eBus.client.EFeed;
import net.sf.eBus.client.EFeed.FeedScope;
import net.sf.eBus.client.EFeedState;
import net.sf.eBus.client.EObject;
import net.sf.eBus.client.EPublishFeed;
import net.sf.eBus.client.EPublisher;
import net.sf.eBus.client.EReplier;
import net.sf.eBus.client.EReplyFeed;
import net.sf.eBus.client.EReplyFeed.ERequest;
import net.sf.eBus.client.IEPublishFeed;
import net.sf.eBus.messages.EField;
import net.sf.eBus.messages.EMessageKey;
import net.sf.eBus.messages.EReplyMessage.ReplyStatus;
import net.sf.eBus.messages.type.DataType;
import net.sf.eBusx.monitor.MonitorUpdate.UpdateType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class provides the ability to instrument
 * {@link EObject eBus objects} and monitor those instrumented
 * object's status. The first step is to
 * {@link #openMonitor(String, String, String, String, String, EField) open a Monitor}
 * instance. Note that only one {@code Monitor} may be opened
 * and it stays open as long as the JVM is running. The next
 * step is
 * {@link #register(EObject) registering eBus objects}. Once
 * registered, these objects may
 * {@link #update(ActionLevel, String, String, EObject) report on-going status}
 * and
 * {@link #transientStatus(ActionLevel, String, String, EObject) transient events}.
 * When an eBus object is no longer operational, it should be
 * {@link #deregister(EObject) de-registered} from
 * {@code Monitor}.
 * 

* Note: {@code Monitor} does not * maintain a strong reference to registered eBus objects. This * means that an eBus object can be finalized while still * registered with {@code Montior}. If this happens, * {@code Monitor} will automatically de-register the finalized * eBus object. *

*

* {@code Monitor} publishes: *

*
    *
  • * {@link ApplicationInfo} reporting the monitor's opening, *
  • *
  • * {@link PersistentStatusMessage} reporting an update to * an eBus object's on-going status, and *
  • *
  • * {@link TransientStatusMessage} reporting an eBus object's * transient event. *
  • *
*

* {@code Monitor} also handles the following requests: *

*
    *
  • * {@link ApplicationInfoRequest}: request for monitor's * application information. {@link ApplicationInfoReply} is * sent in reply. *
  • *
  • * {@link MonitoredObjectRequest}: request for current * on-going registered eBus objects. * {@link MonitoredObjectReply} is sent in reply containing * {@link PersistentStatusMessage}s. *
  • *
*

* Note: there is no way to request previously post transient * events. These can only be received when actively subscribed * to transient status events. *

*

* See {@link net.sf.eBusx.monitor package documentation} for a * detailed explanation on how to use {@code Monitor}. The * package documentation provides detailed explanation on how * to instrument an eBus object and monitor updates. *

* * @see #openMonitor(String, String, String, String, String, EField) * @see #getMonitor() * * @author Charles Rapp */ @SuppressWarnings({"java:S1450"}) public final class Monitor implements EObject { //--------------------------------------------------------------- // Member data. // //----------------------------------------------------------- // Constants. // /** * Monitor publisher identifier is {@value}. */ public static final long MONITOR_PUBLISHER_ID = -2001L; /** * eBus object name is "{@value}". */ public static final String MONITOR_NAME = "eBus monitor"; /** * {@link MonitorUpdate} action name is {@value}. */ public static final String REGISTER_ACTION_NAME = "registered"; /** * {@link MonitorUpdate} action name is {@value}. */ public static final String DEREGISTER_ACTION_NAME = "deregistered"; /** * {@code MONITOR_UPDATE_FORMAT} is advertised with the * subject {@value} with host and application name to form * this notification key. */ public static final String MONITOR_UPDATE_FORMAT = "/eBus/monitor/update/%s/%s"; /** * {@link ApplicationInfoRequest} is advertised with the * subject {@value} to form request key. */ public static final String APP_INFO_REQUEST_SUBJECT = "/eBus/monitor/request/app-info"; /** * {@link MonitoredObjectRequest} is advertised with the * subject {@value} to form request key. */ public static final String ONGOING_REQUEST_SUBJECT = "/eBus/monitor/request/on-going"; /** * Monitor entry publisher name. */ private static final String MONITOR_PUBLISHER = "MonitorPublisher"; /** * Monitor entry replier name. */ private static final String MONITOR_REPLIER = "MonitorReplier"; //----------------------------------------------------------- // Statics. // /** * Logging subsystem interface. */ private static final Logger sLogger = LoggerFactory.getLogger(Monitor.class); /** * Singleton eBus monitor instance. Instantiated using * {@link #getMonitor()} method. */ private static Monitor sMonitor; /** * Used to protect access to monitor singleton instance. */ private static final Lock sInstanceLock = new ReentrantLock(); /** * Finalized {@link MonitorEntry} objects are placed into * this queue. This queue is processed by the * {@link #sGcThread clean up thread}. */ private static final ReferenceQueue sGcQueue; /** * This thread uses the blocking * {@link ReferenceQueue#remove()} method to take the next * defunct {@code MonitorEntry} from {@link #sGcQueue} and * automatically de-registering the finalized monitor * object. */ private static final Thread sGcThread; // Static initialization block. static { // "Compile" the message types on start. DataType.findType(ApplicationInfo.class); DataType.findType(MonitorUpdate.class); DataType.findType(MonitoredObjectReply.class); DataType.findType(MonitoredObjectRequest.class); DataType.findType(PersistentStatusMessage.class); DataType.findType(TransientStatusMessage.class); sGcQueue = new ReferenceQueue<>(); sGcThread = new Thread("eBux:monitorCleanupThread") { @Override public void run() { MonitorEntry entry; while (true) { try { entry = (MonitorEntry) sGcQueue.remove(); sLogger.debug( "Monitor: de-registering eBus object {}.", entry.monitorId()); entry.cleanUp(); } catch (InterruptedException interrupt) {} } } }; sGcThread.start(); } // end of static. //----------------------------------------------------------- // Locals. // /** * Monitor is running on this "host" name. Not required to be * a network host name. */ private final String mHostName; /** * Application name. */ private final String mAppName; /** * Application version. */ private final String mAppVersion; /** * Application copyright. */ @Nullable private final String mAppCopyright; /** * Application description. */ @Nullable private final String mAppDescription; /** * Application-specific attributes. */ @Nullable private final EField mAppAttributes; /** * Post application info, on-going status, and transient * status notifications on this subject. */ private final String mUpdateSubject; /** * Used to synchronize access to static data. */ private final Lock mMonitorLock; /** * Maps the monitored object identifier to its entry. */ private final ConcurrentMap mMonitoredMap; /** * Responsible for publishing monitor notification messages. */ private MonitorPublisher mPublisher; /** * Since eBus only interacts with objects, create an instance * of the static {@link MonitorReplier} class to handle * {@code net.sf.eBusx.monitor.MonitoredObjectRequest} * messages. */ private MonitorReplier mReplier; /** * Tracks latest application information update message. */ private int mAppInfoPosition; /** * Tracks latest monitor update message. */ private int mUpdatePosition; //--------------------------------------------------------------- // Member methods. // //----------------------------------------------------------- // Constructors. // /** * Default constructor is private since Monitor may only be * instantiated by {@link #getMonitor()}. * @param host monitor running on this "host". Does not * necessary need to be a network host name. * @param name the application name. May not be {@code null} * or empty. * @param version the application version. May not be * {@code null} or empty. * @param copyright the application copyright. May be * {@code null}. * @param description the application description. May be * {@code null}. * @param attributes the application-specific attributes. * May be {@code null}. */ private Monitor(final String host, final String name, final String version, final @Nullable String copyright, final @Nullable String description, final @Nullable EField attributes) { mHostName = host; mAppName = name; mAppVersion = version; mAppCopyright = copyright; mAppDescription = description; mAppAttributes = attributes; mUpdateSubject = String.format(MONITOR_UPDATE_FORMAT, host, name); mMonitorLock = new ReentrantLock(true); mMonitoredMap = new ConcurrentHashMap<>(); mAppInfoPosition = 0; mUpdatePosition = 0; } // end of Monitor() // // end of Constructors. //----------------------------------------------------------- //----------------------------------------------------------- // EObject Interface Implementation. // /** * Returns {@link #MONITOR_NAME}. * @return returns eBus monitor name. */ @Override public String name() { return (MONITOR_NAME); } // end of name() /** * Creates, registers, and starts monitor publisher and * replier eBus objects. */ @Override public void startup() { sLogger.debug("{}: starting up.", this.name()); mPublisher = new MonitorPublisher(); EFeed.register(mPublisher); // Instantiate the monitored object request handler and // advertise it. mReplier = new MonitorReplier(); EFeed.register(mReplier); EFeed.startup(ImmutableList.of(mPublisher, mReplier)); } // end of startup() // Note: eBus monitor singleton instance may not be shut // down once started. // // end of EObject Interface Implementation. //----------------------------------------------------------- //----------------------------------------------------------- // Get Methods. // /** * Returns configured host name. Please note that this name * is not necessarily a network host name. * @return host name. */ public String hostName() { return (mHostName); } // end of hostName() /** * Returns configured application name. * @return application name. */ public String applicationName() { return (mAppName); } // end of applicationName() /** * Returns configured application version. * @return application version. */ public String version() { return (mAppVersion); } // end of version() /** * Returns application copyright. May return a {@code null} * value. * @return application copyright. */ @Nullable public String copyright() { return (mAppCopyright); } // end of copyright() /** * Returns application description. May return a {@code null} * value. * @return application description. */ @Nullable public String description() { return (mAppDescription); } // end of description() /** * Returns application attributes. May return a {@code null} * value. * @return application attributes. */ @Nullable public EField attributes() { return (mAppAttributes); } // end of attributes() /** * Returns singleton monitor instance. Returns {@code null} * if monitor was not previously * {@link #openMonitor(String, String, String, String, String, EField) opened}. * @return singleton monitor instance. */ public static @Nullable Monitor getMonitor() { return (sMonitor); } // end of getMonitor() // // end of Get Methods. //----------------------------------------------------------- /** * Registers a monitorable object and returns unique monitor * identifier associated with registered object. The object's * initial persistent status is set to * {@link ActionLevel#NO_ACTION}, "Registered". * An eBus object must successfully register prior to * {@link #update(ActionLevel, String, String, EObject) updating} * its status or posting a * {@link #transientStatus(ActionLevel, String, String, EObject) transient} * event. *

* Note: monitor does not maintain * a strong references to {@code obj}. If this eBus object is * finalized while still registered with {@code Monitor}, * then monitor is informed of this finalization and * automatically de-registers the finalized eBus object. *

* @param obj register this monitorable object. * @throws NullPointerException * if {@code obj} is {@code null}. * @throws IllegalArgumentException * if {@code obj} is {@code null} or * {@link EObject#name} returns a {@code null} or empty * string. * * @see #update(ActionLevel, String, String, EObject) * @see #transientStatus(ActionLevel, String, String, EObject) * @see #deregister(EObject) */ public void register(final EObject obj) { final String instanceName = (Objects.requireNonNull(obj, "obj is null")).name(); if (Strings.isNullOrEmpty(instanceName)) { throw ( new IllegalArgumentException( "instance name is either null or empty")); } MonitorEntry entry = findEntry(obj); // Is this eBus object already registered? if (entry == null) { final String typeName = (obj.getClass()).getName(); final MonitorId monitorId = MonitorId.builder() .typeName(typeName) .instanceName(instanceName) .id(nextMonitorId(obj)) .build(); // No. Continue with the registration. entry = new MonitorEntry(obj, monitorId); // Is this object already registered? sLogger.debug( "Registering {} for monitoring.", monitorId); mMonitoredMap.put(monitorId, entry); // Tell the world about this new monitored object. mPublisher.publish( MonitorUpdate.builder() .subject(mUpdateSubject) .publisherId(MONITOR_PUBLISHER_ID) .position(mUpdatePosition) .hostName(mHostName) .appName(mAppName) .instance(monitorId) .updateType(UpdateType.REGISTER) .actionLevel(ActionLevel.NO_ACTION) .actionName(REGISTER_ACTION_NAME) .build()); ++mUpdatePosition; } // Yes, ignore this duplicate registration. } // end of register(EObject) /** * Updates the monitorable object on-going status with the * given parameters. * @param actionLevel the action level. * @param actionName the action name. * @param actionMsg the human-readable action message. May be * {@code null} or an empty string. * @param obj the registered monitorable object. * @throws NullPointerException * if {@code obj} is {@code null}. * @throws IllegalArgumentException * if {@code actionName} is either {@code null} or an empty * string. * @throws IllegalStateException * if {@code obj} is not registered. * * @see #transientStatus(ActionLevel, String, String, EObject) * @see #register(EObject) * @see #deregister(EObject) */ public void update(final ActionLevel actionLevel, final String actionName, final @Nullable String actionMsg, final EObject obj) { Objects.requireNonNull(obj, "obj is null"); if (Strings.isNullOrEmpty(actionName)) { throw ( new IllegalArgumentException( "null or empty actionName")); } final MonitorEntry entry = findEntry(obj); // The actionMsg may be null or empty. // But the monitored object must be registered. if (entry == null) { throw ( new IllegalStateException("obj not registered")); } sLogger.debug( "Monitor: {} update action level={}, name={}, message={}", obj.name(), actionLevel, actionName, actionMsg); entry.updatePersistent(actionLevel, actionName, actionMsg); mPublisher.publish( MonitorUpdate.builder() .subject(mUpdateSubject) .publisherId(MONITOR_PUBLISHER_ID) .position(mUpdatePosition) .hostName(mHostName) .appName(mAppName) .instance(entry.monitorId()) .updateType(UpdateType.ON_GOING_UPDATE) .actionLevel(actionLevel) .actionName(actionName) .actionMessage(actionMsg) .build()); ++mUpdatePosition; } // end of updatePersistent(ActionLevel, String, String, EObject) /** * Posts a one-time transient event for the given monitorable * object. * @param actionLevel the action level. * @param actionName the action name. * @param actionMsg the human-readable action message. May be * {@code null} or an empty string. * @param obj the registered monitorable object. * @throws NullPointerException * if {@code obj} is {@code null}. * @throws IllegalArgumentException * if {@code actionName} is either {@code null} or an empty * string. * @throws IllegalStateException * if {@code obj} is not registered. * * @see #register(EObject) * @see #update(ActionLevel, String, String, EObject) * @see #deregister(EObject) */ public void transientStatus(final ActionLevel actionLevel, final String actionName, final @Nullable String actionMsg, final EObject obj) { Objects.requireNonNull(obj, "null obj"); if (Strings.isNullOrEmpty(actionName)) { throw ( new IllegalArgumentException( "null or empty actionName")); } final MonitorEntry entry = findEntry(obj); // The actionMsg may be null or empty. // But the monitored object must be registered. if (entry == null) { throw ( new IllegalStateException("obj not registered")); } sLogger.debug( "Monitor: {} transient action level={}, name={}, message={}", obj.name(), actionLevel, actionName, actionMsg); entry.updateTransient(actionLevel, actionName, actionMsg); mPublisher.publish( MonitorUpdate.builder() .subject(mUpdateSubject) .publisherId(MONITOR_PUBLISHER_ID) .position(mUpdatePosition) .hostName(mHostName) .appName(mAppName) .instance(entry.monitorId()) .updateType(UpdateType.TRANSIENT_UPDATE) .actionLevel(actionLevel) .actionName(actionName) .actionMessage(actionMsg) .build()); ++mUpdatePosition; } // end of transientStatus(...) /** * Removes a registered eBus object from the monitor * subsystem, retracting its published status and transient * event feeds. * @param obj remove this monitorable instance. * @throws NullPointerException * if {@code obj} is {@code null}. * * @see #update(ActionLevel, String, String, EObject) * @see #transientStatus(ActionLevel, String, String, EObject) * @see #register(EObject) */ public void deregister(final EObject obj) { Objects.requireNonNull(obj, "null obj"); final MonitorEntry entry = findEntry(obj); // Is this monitored object currently registered? if (entry != null) { // Yes. Continue with the deregistration. deregister(entry); } // Monitored object is not registered. Ignore this // request. } // end of deregister(EObject) /** * Creates, starts, and returns singleton monitor instance. * Please note that when calling this method for the first * time, the method blocks until {@code Monitor} instance * is started. Parameter values are left up to the user to * decide what constitutes a valid setting. Note that these * values should be meaningful to both the application and * to those monitoring the application. *

* For example, consider {@code host}. A natural reaction is * to use the name configured by the network support team. * But these names are often arcane and meaningful only to * network support. The application team may refer to this * host as "uat-ny-1" - meaning the first user acceptance * testing host (uat) site in New York. Since * {@code Monitor} operates at the application level, this * would be a better host name. This also applies to the * remaining parameters. *

*

* The {@code attributes} parameter allows an application * to add any number of additional settings by extending an * eBus {@link EField}. A partial example is: *

*
import net.sf.eBus.messages.EField;

public final class MyAppAttributes
    extends EField {
    // Report when this application was released to production.
    public final ZonedDateTime releaseDate;

    // Text providing production release authorization.
    public final String releaseSignOff;

    // Application is running on this Java version.
    public final String javaVersion;

    // JVM implementation vendor.
    public final String javaVendor;

    // ... and any other attributes deemed necessary for
    // application support.
}
*

* If monitor is already open, then returns the monitor * singleton instance. *

* @param host monitor running on this "host". Does not * necessary need to be a network host name. * @param appName the application name. May not be {@code null} * or empty. * @param version the application version. May not be * {@code null} or empty. * @param copyright the application copyright. May be * {@code null}. * @param description the application description. May be * {@code null}. * @param attributes the application-specific attributes. * May be {@code null}. * @return singleton monitor instance. * @throws IllegalArgumentException * if either {@code host}, {@code name} or {@code version} is * {@code null} or empty. */ public static Monitor openMonitor(final String host, final String appName, final String version, @Nullable final String copyright, @Nullable final String description, @Nullable final EField attributes) { if (Strings.isNullOrEmpty(host)) { throw ( new IllegalArgumentException( "host is either null or empty")); } if (Strings.isNullOrEmpty(appName)) { throw ( new IllegalArgumentException( "appName is either null or empty")); } if (Strings.isNullOrEmpty(version)) { throw ( new IllegalArgumentException( "version is either null or empty")); } sInstanceLock.lock(); try { if (sMonitor == null) { sLogger.info("Opening eBus monitor."); sMonitor = new Monitor(host, appName, version, copyright, description, attributes); EFeed.register(sMonitor); EFeed.startup(sMonitor); } } finally { sInstanceLock.unlock(); } return (sMonitor); } // end of openMonitor(...) /** * Performs the actual work of de-registering a monitored * object. * @param entry monitored eBus object's entry. */ private void deregister(final MonitorEntry entry) { final MonitorId monitorId = entry.monitorId(); sLogger.debug("Deregistering {} from monitoring.", monitorId); mMonitoredMap.remove(monitorId); // Publish and close the monitor entry. entry.updatePersistent( ActionLevel.NO_ACTION, "Deregistered", "Deregistered from monitor subsystem"); // Tell the world about this change. mPublisher.publish( MonitorUpdate.builder() .instance(monitorId) .subject(mUpdateSubject) .publisherId(MONITOR_PUBLISHER_ID) .position(mUpdatePosition) .hostName(mHostName) .appName(mAppName) .instance(monitorId) .updateType(UpdateType.DEREGISTER) .actionLevel(ActionLevel.NO_ACTION) .actionName(DEREGISTER_ACTION_NAME) .build()); ++mUpdatePosition; } // end of deregister(MonitorEntry) /** * Returns monitor entry for the given object. Returns * {@code null} if eBus object is not registered for * monitoring. * @param obj eBus object. * @return eBus object's registered entry. */ @Nullable private MonitorEntry findEntry(final EObject obj) { final Iterator mIt = (mMonitoredMap.values()).iterator(); MonitorEntry retval = null; while (retval == null && mIt.hasNext()) { retval = mIt.next(); // Is this entry for the given eBus object? // (i.e. does the entry reference obj) if (retval.get() != obj) { // No. Move on to the next entry. retval = null; } } return (retval); } // end of findEntry(EObject) /** * Returns a monitor identifier for given monitorable object. * If given object is an {@link EObject eBus object}, then * uses the eBus client identifier. Otherwise assigns next * value from the monitor identifier pool. * @param obj find identifier for this monitorable object. * @return monitor identifier. */ private static int nextMonitorId(final EObject obj) { final EClient client = EClient.findOrCreateClient( obj, ClientLocation.LOCAL); return (client.clientId()); } // end of nextMonitorId(EObject) //--------------------------------------------------------------- // Inner classes. // /** * This passive, immutable class is used to encapsulate a * registered eBus object, its {@link MonitorId} and latest * {@code PersistentStatus} message. */ private final class MonitorEntry extends WeakReference { //----------------------------------------------------------- // Member data. // //------------------------------------------------------- // Locals. // /** * The monitored object. Hold the reference to prevent * its disappearing. */ private final EObject mObject; /** * The monitored object's identifier. */ private final MonitorId mId; /** * The current persistent status level. Initialized to * {@link ActionLevel#NO_ACTION}. */ private ActionLevel mPersistLevel; /** * The current persistent action name. Initialized to * "Registered". */ private String mPersistName; /** * The current persistent action message. Initialized to * "". */ private String mPersistMessage; //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // /** * Creates an entry for the given monitorable object and * identifier. * @param object monitored object. * @param id monitor identifier. */ public MonitorEntry(final EObject object, final MonitorId id) { super (object, sGcQueue); mObject = object; mId = id; mPersistLevel = ActionLevel.NO_ACTION; mPersistName = "Registered"; mPersistMessage = "Registered with monitor subsystem"; } // end of MonitorEntry(EObject, MonitorId) // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // Get methods. // /** * Returns monitored object. * @return monitored object. */ public EObject monitoredObject() { return (mObject); } // end of monitoredObject() /** * Returns monitored object identifier. * @return monitor identifier. */ public MonitorId monitorId() { return (mId); } // end of monitorId() // // end of Get methods. //------------------------------------------------------- /** * Updates the monitored objects current persistent * status information. Posts the persistent status * message if the feed is up. * @param level current persistent action level. * @param actionName current persistent action name. * @param actionMsg current persistent action message. */ private void updatePersistent(final ActionLevel level, final String actionName, final String actionMsg) { sLogger.trace( "MonitorEntry {} update: level={}, action={}, message={}.", mId.instanceName, level, actionName, actionMsg); mPersistLevel = level; mPersistName = actionName; mPersistMessage = actionMsg; } // end of updatePersistent(ActionLevel, String, String) /** * Publishes the transient status message is the feed is * up. * @param level current transient action level. * @param actionName current transient action name. * @param actionMsg current transient action message. */ private void updateTransient(final ActionLevel level, final String actionName, final String actionMsg) { sLogger.trace( "MonitorEntry {} transient: level={}, action={}, message={}.", mId.instanceName, level, actionName, actionMsg); } // end of updateTransient(ActionLevel, String, String) /** * De-registers this finalized eBus object. */ private void cleanUp() { // Clean up means de-register this object. deregister(this); } // end of cleanUp() /** * Returns a persistent status message based on the * monitor's configuration. * @return a new persistent status message. */ private PersistentStatusMessage generatePersistentMessage() { final PersistentStatusMessage.Builder builder = PersistentStatusMessage.builder(); return (builder.subject(mId.toString()) .hostName(mHostName) .appName(mAppName) .instance(mId) .actionLevel(mPersistLevel) .actionName(mPersistName) .actionMessage(mPersistMessage) .build()); } // end of generatePersistentMessage() } // end of class MonitorEntry /** * The singleton application-level publisher for the JVM. * Publishes the application information and monitor update * messages. */ private final class MonitorPublisher implements EPublisher { //----------------------------------------------------------- // Member data. // //------------------------------------------------------- // Locals. // /** * Publish application information on this feed. */ private EPublishFeed mAppInfoFeed; /** * Publish monitored state updates on this feed. */ private EPublishFeed mUpdateFeed; //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // /** * Creates a new monitor notification publisher. */ private MonitorPublisher() {} // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // EObject Interface Implementation. // /** * Returns monitor publisher name. * @return monitor publisher name. */ @Override public String name() { return (MONITOR_PUBLISHER); } // end of name() /** * Opens and advertises the publisher feeds. Once open, * these feeds are not closed. */ @Override public void startup() { final EMessageKey appInfoKey = new EMessageKey( ApplicationInfo.class, mUpdateSubject); final EMessageKey updateKey = new EMessageKey(MonitorUpdate.class, mUpdateSubject); // Put the publisher feeds in place. mAppInfoFeed = (EPublishFeed.builder()) .target(this) .messageKey(appInfoKey) .scope(FeedScope.LOCAL_AND_REMOTE) .build(); mUpdateFeed = (EPublishFeed.builder()) .target(this) .messageKey(updateKey) .scope(FeedScope.LOCAL_AND_REMOTE) .build(); sLogger.debug("{}: advertising {} and {} feeds.", this.name(), appInfoKey, updateKey); mAppInfoFeed.advertise(); mUpdateFeed.advertise(); mAppInfoFeed.updateFeedState(EFeedState.UP); mUpdateFeed.updateFeedState(EFeedState.UP); } // end of startup() // Since this publisher is never shutdown, the shutdown() // method is not overridden. // // end of EObject Interface Implementation. //------------------------------------------------------- //------------------------------------------------------- // EPublisher Interface Implementation. // /** * Informs this publisher that the feed is either up * or down. Since the feed state is checked each time * a message is published, this method does nothing. * @param feedState the notification feed is either up or * down. * @param feed {@code feedState} applies to this * notification feed. */ // This callback does nothing and so the method body is // empty. @SuppressWarnings({"java:S1186"}) @Override public void publishStatus(final EFeedState feedState, final IEPublishFeed feed) { sLogger.trace( "MonitorPublisher: {} publish status is {}.", feed.key(), feedState); // Is this the application information feed? // Did the feed switch from down to up? if (feed == mAppInfoFeed && feedState == EFeedState.UP) { // Yes to both. Publish the application // information to the first subscriber. publish( ApplicationInfo.builder() .subject(mUpdateSubject) .publisherId(MONITOR_PUBLISHER_ID) .position(mAppInfoPosition) .hostName(mHostName) .appName(mAppName) .appVersion(mAppVersion) .copyright(mAppCopyright) .description(mAppDescription) .attributes(mAppAttributes) .build()); ++mAppInfoPosition; } } // end of publishStatus(EFeedState, IEPublishFeed) // // end of EPublisher Interface Implementation. //------------------------------------------------------- /** * Publishes the application information message if the * feed is up. * @param msg application information message. * * @see #publish(MonitorUpdate) */ private void publish(final ApplicationInfo msg) { if (mAppInfoFeed != null && mAppInfoFeed.isFeedUp()) { mAppInfoFeed.publish(msg); } } // end of publish(ApplicationInfo) /** * Publishes the monitor updatePersistent message is the * feed is up. * @param msg monitor updatePersistent message. * * @see #publish(ApplicationInfo) */ private void publish(final MonitorUpdate msg) { if (mUpdateFeed != null && mUpdateFeed.isFeedUp()) { mUpdateFeed.publish(msg); } } // end of publish(MonitorUpdate) } // end of class MonitorPublisher /** * This class replies to {@link MonitoredObjectRequest} * messages. */ private final class MonitorReplier implements EReplier { //----------------------------------------------------------- // Member data. // //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // /** * Creates a monitor request handler instance. */ private MonitorReplier() {} // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // EObject EPublisher Interface Implementation. // /** * Returns monitor replier name. * @return monitor replier name. */ @Override public String name() { return (MONITOR_REPLIER); } // end of name() /** * Opens and advertises the monitored object request * reply feed. Once open, the reply feed remains open. */ @Override public void startup() { final EMessageKey appInfoKey = new EMessageKey(ApplicationInfoRequest.class, APP_INFO_REQUEST_SUBJECT); final EMessageKey requestKey = new EMessageKey(MonitoredObjectRequest.class, ONGOING_REQUEST_SUBJECT); final EReplyFeed appInfoFeed = (EReplyFeed.builder()) .target(this) .messageKey(appInfoKey) .scope(FeedScope.LOCAL_AND_REMOTE) .requestCallback(this::onAppInfoRequest) .build(); final EReplyFeed updateFeed = (EReplyFeed.builder()) .target(this) .messageKey(requestKey) .scope(FeedScope.LOCAL_AND_REMOTE) .requestCallback(this::onPersistentRequest) .build(); sLogger.debug("{}: advertising {} feed.", this.name(), appInfoKey); appInfoFeed.advertise(); appInfoFeed.updateFeedState(EFeedState.UP); sLogger.debug("{}: advertising {} feed.", this.name(), requestKey); updateFeed.advertise(); updateFeed.updateFeedState(EFeedState.UP); } // end of startup() // Since this replier is never shutdown, the shutdown() // method is not overridden. // // end of EObject Interface Implementation. //------------------------------------------------------- //------------------------------------------------------- // EReplier Interface Implementation. // /** * Since a reply is sent as soon as a request is * received, there is no way it can be canceled. * @param request cancel this request. * @param mayRespond ignored. */ // This callback does nothing and so the method body is // empty. @SuppressWarnings({"java:S1186"}) @Override public void cancelRequest(final ERequest request, final boolean mayRespond) {} // end of cancelRequest(ERequest) // // end of EReplier Interface Implementation. //------------------------------------------------------- //------------------------------------------------------- // Request Handlers. // /** * Generates an application information reply to this * request. * @param request application information request. */ private void onAppInfoRequest(final ERequest request) { final ApplicationInfoReply.Builder builder = ApplicationInfoReply.builder(); mMonitorLock.lock(); try { builder.subject((request.key()).subject()) .replyStatus(ReplyStatus.OK_FINAL) .hostName(mHostName) .appName(mAppName) .appVersion(mAppVersion) .copyright(mAppCopyright) .description(mAppDescription) .attributes(mAppAttributes); } finally { mMonitorLock.unlock(); } request.reply(builder.build()); } // end of onAppInfoRequest(ERequest) /** * Generates a monitored object reply to this * request for all persistent statuses. * @param request post the reply to this request. */ private void onPersistentRequest(final ERequest request) { final PersistentStatusMessage[] statuses; mMonitorLock.lock(); try { int i = 0; statuses = new PersistentStatusMessage[mMonitoredMap.size()]; for (MonitorEntry entry : mMonitoredMap.values()) { statuses[i] = entry.generatePersistentMessage(); ++i; } } finally { mMonitorLock.unlock(); } request.reply( MonitoredObjectReply.builder() .subject((request.key()).subject()) .replyStatus(ReplyStatus.OK_FINAL) .hostName(mHostName) .appName(mAppName) .statuses(statuses) .build()); } // end of onPersistentRequest(ERequest) // // end of Request Handlers. //------------------------------------------------------- } // end of class MonitorReplier } // end of class Monitor




© 2015 - 2024 Weber Informatics LLC | Privacy Policy