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

net.sf.eBusx.monitor.Monitor 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 2011, 2012, 2015, 2016. Charles W. Rapp
// All Rights Reserved.
//

package net.sf.eBusx.monitor;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.eBus.client.EFeed;
import net.sf.eBus.client.EFeed.FeedScope;
import net.sf.eBus.client.EFeedState;
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.type.DataType;
import net.sf.eBus.util.IndexPool;

/**
 * This class provides static methods, interfacing with the eBus
 * monitoring subsystem. {@link Monitorable} objects must
 * {@link #register(net.sf.eBusx.monitor.Monitorable) register}
 * prior to calling
 * {@link #update(ActionLevel, String, String, Monitorable) updating}
 * its current state or reporting
 * {@link #transientStatus(ActionLevel, String, String, Monitorable) transient events}.
 * When monitorable objects
 * {@link #deregister(Monitorable) deregisters}, the monitor
 * status is retracted.
 * 

* This class publishes the optional * {@link #applicationInfo(String, String, String, String, EField) application information}. * This information is generally set once upon application * start and not changed although that is allowed. This class * also publishes the * {@link MonitorUpdate} notification which reports to * subscribers newly registered or deregistered monitorable * objects. * Finally, this class sends * {@link MonitoredObjectReply} replies to * {@link MonitoredObjectRequest} messages. These replies contain * the list of currently register monitorable objects. * * @author Charles Rapp */ public final class Monitor { //--------------------------------------------------------------- // Member methods. // //----------------------------------------------------------- // Constructors. // /** * A private default constructor is defined to prevent * instantiation. */ private Monitor() {} // // end of Constructors. //----------------------------------------------------------- /** * Publishes the given application information. * @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. * @throws IllegalArgumentException * if either {@code name} or {@code version} is {@code null} * or empty. */ public static void applicationInfo(final String name, final String version, final String copyright, final String description, final EField attributes) throws IllegalArgumentException { _publisher.publish(new ApplicationInfo(name, version, copyright, name, attributes)); return; } // end of applicationInfo(String, String, String, String) /** * Registers a monitorable object. The object's initial * persistent status is set to {@link ActionLevel#NO_ACTION}, * "Registered". A {@link Monitorable} object must * successfully register prior to * {@link #update(ActionLevel, String, String, Monitorable) updating} * is status or posting a * {@link #transientStatus(ActionLevel, String, String, Monitorable) transient} event. * @param obj register this monitorable object. * @exception IllegalArgumentException * if {@code obj} is {@code null} or * {@link Monitorable#instanceName} returns a {@code null} * or empty string. * @see #update(ActionLevel, String, String, Monitorable) * @see #transientStatus(ActionLevel, String, String, Monitorable) * @see #deregister(Monitorable) */ public static void register(final Monitorable obj) throws IllegalArgumentException { final String instanceName; if (obj == null) { throw (new IllegalArgumentException("null obj")); } else if ((instanceName = obj.instanceName()) == null || instanceName.length() == 0) { throw ( new IllegalArgumentException( "null or empty instance name")); } else { final String typeName = (obj.getClass()).getName(); final MonitorId monitorId = new MonitorId(typeName, instanceName, _monitorIdPool.nextIndex()); final MonitorEntry entry = new MonitorEntry(obj, monitorId); // Is this object already registered? if (_monitoredMap.putIfAbsent(obj, entry) == null) { // No. Continue with the registration. if (_logger.isLoggable(Level.FINE) == true) { _logger.fine( String.format( "Registering %s.%s for monitoring.", typeName, instanceName)); } _monitoredMap.put(obj, entry); // Have the entry open the persistent and // transient status feeds. (entry._lock).lock(); try { EFeed.register(entry); EFeed.startup(entry); // Wait here for the entry to start up to // complete. try { (entry._startSignal).await(); } catch (InterruptedException interrupt) {} } finally { (entry._lock).unlock(); } // Tell the world about this new monitored object. _publisher.publish( new MonitorUpdate( Monitor.MONITOR_UPDATE_SUBJECT, monitorId, true)); } // Yes, ignore this duplicate registration. } return; } // end of register(Monitorable) /** * 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. * @param obj the registered monitorable object. * @throws IllegalArgumentException * if {@code obj} is {@code null} or {@code actionName} is * either {@code null} or an empty string. * @throws IllegalStateException * if {@code obj} is not registered. * @see #transientStatus(ActionLevel, String, String, Monitorable) * @see #register(Monitorable) */ public static void update(final ActionLevel actionLevel, final String actionName, final String actionMsg, final Monitorable obj) throws IllegalArgumentException, IllegalStateException { final MonitorEntry entry; if (obj == null) { throw (new IllegalArgumentException("null obj")); } else if (actionName == null || actionName.length() == 0) { throw ( new IllegalArgumentException( "null or empty actionName")); } // The actionMsg may be null or empty. // But the monitored object must be registered. else if ((entry = _monitoredMap.get(obj)) == null) { throw ( new IllegalStateException("obj not registered")); } else { entry.updatePersistent(actionLevel, actionName, actionMsg); } return; } // end of updatePersistent(ActionLevel, String, String, Monitorable) /** * 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. * @param obj the registered monitorable object. * @throws IllegalArgumentException * if {@code obj} is {@code null} or {@code actionName} is * either {@code null} or an empty string. * @throws IllegalStateException * if {@code obj} is not registered. * @see #register(Monitorable) * @see #update(ActionLevel, String, String, Monitorable) */ public static void transientStatus(final ActionLevel actionLevel, final String actionName, final String actionMsg, final Monitorable obj) throws IllegalArgumentException, IllegalStateException { final MonitorEntry entry; if (obj == null) { throw (new IllegalArgumentException("null obj")); } else if (actionName == null || actionName.length() == 0) { throw ( new IllegalArgumentException( "null or empty actionName")); } // The actionMsg may be null or empty. // But the monitored object must be registered. else if ((entry = _monitoredMap.get(obj)) == null) { throw ( new IllegalStateException("obj not registered")); } else { entry.updateTransient(actionLevel, actionName, actionMsg); } return; } // end of transientStatus(...) /** * Removes a registered {@link Monitorable} object from the * monitor subsystem, retracting its published status and * transient event feeds. * @param obj remove this monitorable instance. * @throws IllegalArgumentException * if {@code obj} is {@code null}. */ public static void deregister(final Monitorable obj) throws IllegalArgumentException { final MonitorEntry entry; // Is this monitored object currently registered? if (obj == null) { throw (new IllegalArgumentException("obj is null")); } else if ((entry = _monitoredMap.remove(obj)) != null) { // Yes. Continue with the deregistration. final MonitorId monitorId = entry.monitorId(); if (_logger.isLoggable(Level.FINE) == true) { _logger.fine( String.format( "Deregistering %s from monitoring.", monitorId)); } // Publish and close the monitor entry. entry.updatePersistent( ActionLevel.NO_ACTION, "Deregistered", "Deregistered from monitor subsystem"); (entry._lock).lock(); try { EFeed.shutdown(entry); // Wait here for the entry to shutdown to // complete. try { (entry._stopSignal).await(); } catch (InterruptedException interrupt) {} } finally { (entry._lock).unlock(); } // Tell the world about this change. _publisher.publish( new MonitorUpdate(Monitor.MONITOR_UPDATE_SUBJECT, monitorId, false)); } return; } // end of deregister(Monitorable) //--------------------------------------------------------------- // Member data. // //----------------------------------------------------------- // Statics. // /** * Responsible for publishing monitor notification messages. */ private static final MonitorPublisher _publisher; /** * Used to synchronize access to static data. */ private static final Lock _monitorLock = new ReentrantLock(true); /** * Used to assign a unique identifier to a monitored object. */ private static final IndexPool _monitorIdPool = new IndexPool(); /** * 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 static final MonitorReplier _replier; /** * Maps the monitored object to its entry. */ private static final ConcurrentMap _monitoredMap = new ConcurrentHashMap<>(); /** * Logging subsystem interface. */ private static final Logger _logger = Logger.getLogger(Monitor.class.getName()); //----------------------------------------------------------- // Constants. // /** * {@link #MONITOR_UPDATE_SUBJECT} is advertised with the * subject "net.sf.eBusx.monitor" to form this notification * key. */ public static final String MONITOR_UPDATE_SUBJECT = "net.sf.eBusx.monitor"; /** * {@link #MONITOR_REQUEST} is advertised with the subject * "net.sf.eBusx.monitor" to form this request key. */ public static final String MONITOR_REQUEST = "net.sf.eBusx.monitor.MonitoredObjectRequest"; // 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); // Instantiate the monitor object publisher and advertise // its notification feed. final CountDownLatch signal = new CountDownLatch(2); _publisher = new MonitorPublisher(signal); EFeed.register(_publisher); // Instantiate the monitored object request handler and // advertise it. _replier = new MonitorReplier(signal); EFeed.register(_replier); EFeed.startup(_publisher); EFeed.startup(_replier); // Wait here for the publisher and replier start-up to // complete. try { signal.await(); } catch (InterruptedException interrupt) {} } // end of static. //--------------------------------------------------------------- // Inner classes. // /** * This passive, immutable class is used to encapsulate a * registered {@link Monitorable} object, its {@link MonitorId} * and latest PersistentStatus message. */ private static class MonitorEntry implements EPublisher { //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // public MonitorEntry(final Monitorable object, final MonitorId id) { _object = object; _id = id; _lock = new ReentrantLock(); _startSignal = _lock.newCondition(); _stopSignal = _lock.newCondition(); _persistLevel = ActionLevel.NO_ACTION; _persistName = "Registered"; _persistMessage = "Registered with monitor subsystem"; _persistFeed = null; _transientFeed = null; } // end of MonitorEntry(Monitorable, MonitorId) // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // Get methods. // public Monitorable monitoredObject() { return (_object); } // end of monitoredObject() public MonitorId monitorId() { return (_id); } // end of monitorId() // // end of Get methods. //------------------------------------------------------- //------------------------------------------------------- // EObject Interface Implementation. // /** * Opens, advertises, and sets the feed state to up for * both the persistent and transient notification feeds. * @see #shutdown() */ @Override public void startup() { _lock.lock(); try { final String subject = _id.toString(); final EMessageKey persistKey = new EMessageKey( PersistentStatusMessage.class, subject); final EMessageKey transientKey = new EMessageKey( TransientStatusMessage.class, subject); _persistFeed = EPublishFeed.open(this, persistKey, FeedScope.LOCAL_AND_REMOTE); _transientFeed = EPublishFeed.open(this, transientKey, FeedScope.LOCAL_AND_REMOTE); _persistFeed.advertise(); _transientFeed.advertise(); _persistFeed.updateFeedState(EFeedState.UP); _transientFeed.updateFeedState(EFeedState.UP); _startSignal.signal(); } finally { _lock.unlock(); } return; } // end of startup() /** * Closes both the persistent and transient status feeds * if open. * @see #startup() */ @Override public void shutdown() { _lock.lock(); try { if (_persistFeed != null) { _persistFeed.close(); _persistFeed = null; } if (_transientFeed != null) { _transientFeed.close(); _transientFeed = null; } _stopSignal.signal(); } finally { _lock.unlock(); } return; } // end of shutdown() // // end of EObject Interface Implementation. //------------------------------------------------------- //------------------------------------------------------- // EPublisher Interface Implementation. // /** * Informs this publisher that the persistent status feed * is either up or down. If {@code feedState} is * {@link EFeedState#UP up}, then sends the current * persistent state. * @param feedState the notification feed is either up or * down. * @param feed {@code feedState} applies to this * persistent state feed. */ @Override public void publishStatus(final EFeedState feedState, final IEPublishFeed feed) { // Is the feed up or down? if (feed == _persistFeed && feedState == EFeedState.UP) { // Up. Post the current persistent state. _persistFeed.publish( new PersistentStatusMessage(_id.toString(), _id.id, _persistLevel, _persistName, _persistMessage)); } return; } // end of persistStatus(EFeedState, IEPublishFeed} // // end of EPublisher Interface Implementation. //------------------------------------------------------- /** * 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) { _persistLevel = level; _persistName = actionName; _persistMessage = actionMsg; if (_persistFeed.isFeedUp() == true) { _persistFeed.publish( new PersistentStatusMessage(_id.toString(), _id.id, _persistLevel, _persistName, _persistMessage)); } return; } // 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) { if (_transientFeed.isFeedUp() == true) { _transientFeed.publish( new TransientStatusMessage(_id.toString(), _id.id, level, actionName, actionMsg)); } return; } // end of updateTransient(ActionLevel, String, String) //----------------------------------------------------------- // Member data. // /** * The monitored object. Hold the reference to prevent * its disappearing. */ private final Monitorable _object; /** * The monitored object's identifier. */ private final MonitorId _id; /** * This lock is acquired when starting and shutting down * a monitor. */ private final Lock _lock; /** * Set this signal when {@link #startup()} completes. */ private final Condition _startSignal; /** * Set this signal when {@link #shutdown()} completes. */ private final Condition _stopSignal; /** * The current persistent status level. Initialized to * {@link ActionLevel#NO_ACTION}. */ private ActionLevel _persistLevel; /** * The current persistent action name. Initialized to * "Registered". */ private String _persistName; /** * The current persistent action message. Initialized to * "". */ private String _persistMessage; /** * Publish persistent status messages on this feed. */ private EPublishFeed _persistFeed; /** * Publish transient status messages on this feed. */ private EPublishFeed _transientFeed; } // end of class MonitorEntry /** * The singleton application-level publisher for the JVM. * Publishes the application information and monitor update * messages. */ private static final class MonitorPublisher implements EPublisher { //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // /** * Creates a new monitor notification publisher. * @param signal decrement this signal when start-up * completes. */ private MonitorPublisher(final CountDownLatch signal) { _signal = signal; } // end of MonitorPublisher(CountDownLatch) // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // EObject Interface Implementation. // /** * Opens and advertises the publisher feeds. Once open, * these feeds are not closed. */ @Override public void startup() { final EMessageKey appInfoKey = new EMessageKey(ApplicationInfo.class, MONITOR_UPDATE_SUBJECT); final EMessageKey updateKey = new EMessageKey( MonitorUpdate.class, MONITOR_UPDATE_SUBJECT); // Put the publisher feeds in place. _appInfoFeed = EPublishFeed.open( this, appInfoKey, FeedScope.LOCAL_AND_REMOTE); _updateFeed = EPublishFeed.open( this, updateKey, FeedScope.LOCAL_AND_REMOTE); _appInfoFeed.advertise(); _updateFeed.advertise(); _appInfoFeed.updateFeedState(EFeedState.UP); _updateFeed.updateFeedState(EFeedState.UP); _signal.countDown(); _signal = null; return; } // 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. */ @Override public void publishStatus(final EFeedState feedState, final IEPublishFeed feed) {} // // 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 (_appInfoFeed.isFeedUp() == true) { _appInfoFeed.publish(msg); } return; } // 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 (_updateFeed.isFeedUp() == true) { _updateFeed.publish(msg); } return; } // end of publish(MonitorUpdate) //----------------------------------------------------------- // Member data. // /** * Publish application information on this feed. */ private EPublishFeed _appInfoFeed; /** * Publish monitored state updates on this feed. */ private EPublishFeed _updateFeed; /** * Decrement this signal when start-up is complete and * then drop it. */ private CountDownLatch _signal; } // end of class MonitorPublisher /** * This class replies to {@link MonitoredObjectRequest} * messages. */ private static final class MonitorReplier implements EReplier { //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // /** * Creates a monitor request handler instance. * @param signal decrement this signal when start-up * completes. */ private MonitorReplier(final CountDownLatch signal) { _signal = signal; } // end of MonitorReplier(CountDownLatch) // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // EObject EPublisher Interface Implementation. // /** * Opens and advertises the monitored object request * reply feed. Once open, the reply feed remains open. */ @Override public void startup() { final EMessageKey requestKey = new EMessageKey(MonitoredObjectRequest.class, MONITOR_REQUEST); final EReplyFeed replyFeed = EReplyFeed.open(this, requestKey, FeedScope.LOCAL_AND_REMOTE, null); replyFeed.advertise(); replyFeed.updateFeedState(EFeedState.UP); _signal.countDown(); _signal = null; return; } // end of startup() // Since this replier is never shutdown, the shutdown() // method is not overridden. // // end of EObject Interface Implementation. //------------------------------------------------------- //------------------------------------------------------- // EReplier Interface Implementation. // /** * Generate a monitored object reply to this * request. * @param request post the reply to this request. */ @Override public void request(final ERequest request) { final MonitorId[] ids; _monitorLock.lock(); try { int i = 0; ids = new MonitorId[_monitoredMap.size()]; for (MonitorEntry entry : _monitoredMap.values()) { ids[i] = entry.monitorId(); ++i; } } finally { _monitorLock.unlock(); } request.reply( new MonitoredObjectReply( (request.key()).subject(), ids)); return; } // end of request(ERequestb) /** * 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. */ @Override public void cancelRequest(final ERequest request) {} // end of cancelRequest(ERequest) // // end of EReplier Interface Implementation. //------------------------------------------------------- //----------------------------------------------------------- // Member data. // /** * Decrement this signal when start-up is complete and * then drop it. */ private CountDownLatch _signal; } // end of class MonitorReplier } // end of class Monitor





© 2015 - 2025 Weber Informatics LLC | Privacy Policy