Package net.sf.eBusx.monitor

This package provides the ability to instrument eBus objects and to monitor those objects' on-going state and transient events.

An on-going state is one that persists over time. An example is a disconnected connection. This state continues until either re-established or explicitly closed by the application. A transient event occurs in an instant, with no on-going impact to the application. An example of a transient event would be the application catching exception from which it immediately recovers. This exception is reported as a transient event so administrators are aware of the problem.

Both on-going status and transient events are reported with a given action level. This action level informs administrators about the event severity. The need to report ActionLevel.POSSIBLE_ACTION, ActionLevel.ACTION_REQUIRED and ActionLevel.FATAL_ERROR events is obvious: there is a problem occurring which must be corrected. But there is debate about reporting ActionLevel.NO_ACTION, information-only events. One view is that an application may be considered to be operating correctly unless stated otherwise. The opposing view is that, "if it goes without saying, then it goes even better with saying it." By reporting "no action" events, it is an explicit statement of correct operation.

Instrumenting your Application

The starting point is to open a monitor with your host, application name, version, copyright, description and optional name/value attributes. This method should be called once at system start. See Monitor.openMonitor for a detailed explanation for each of the parameters.

The next step in application instrumentation is to register your eBus objects with the Monitor instance. You obtain the Monitor instance either from Monitor.openMonitor or Monitor.getMonitor() ( note: Monitor.getMonitor() returns null if monitor was not opened). EObject.name() is used to uniquely identify this object. A newly registered eBus object's initial on-going status is set to:

  • Action level: NO_ACTION
  • Action name: Registered
  • Action message: "Registered with monitor subsystem"

Once registered, an object may update its on-going (persistent) state and report transient events via the Monitor instance. This is demonstrated in the following code:

import net.sf.eBusx.monitor.ActionLevel;
import net.sf.eBusx.monitor.Monitor

// Note Monitor.getMonitor() returns null if Monitor was not opened.
final Monitor monitor = Monitor.getMonitor();

// This example assumes this eBus object is registered with Monitor.
// Note: this state is on-going until corrected.
monitor.update(ActionLevel.ACTION_REQUIRED,
               "Market data feed",
               "Ticker plant market data feed is DOWN.",
               this);

// This is an example of a transient status report - not an on-going issue.
monitor.transientStatus(ActionLevel.ACTION_REQUIRED,
                        "Market data feed",
                        "Ticker plant market data feed down for " + downTimeInterval,
                        this);

// When the problem is resolved, then be sure to update the on-going status.
monitor.update(ActionLevel.NO_ACTION,
               "Market data feed",
               "Ticker plant market data feed is up.",
               this);

If a registered object is to be discarded prior to application termination, it should be deregistered. Monitor reports this de-registration by setting the final on-going status to:

  • Action level: NO_ACTION
  • Action name: Deregistered
  • Action message: "Deregistered from monitor subsystem"

Please note that Monitor maintains a weak reference to the registered eBus object. If a registered eBus object is garbage collected, then Monitor will automatically de-register this eBus object.

Monitor reports object registration, on-going updates, transient updates, and de-registration using MonitorUpdate notification message which contains: MonitorId and MonitorUpdate.UpdateType.

  • host name (String),
  • application name (String),
  • monitor instance (MonitorId),
  • update type (UpdateType) which specifies if eBus object is registered, on-going status changed, transient status, and de-registered,
  • action level (ActionLevel)
  • action name (String), and
  • action message (String).

These MonitorUpdate messages are published to the subject "/eBus/monitor/update/<host name>/<application name>" where <host name> and <application name> are those used to open the application's Monitor instance. Monitor also replies to ApplicationInfoRequest and MonitoredObjectRequest messages. ApplicationInfoRequest sends a ApplicationInfoReply containing the application host name, application name, etc. MonitoredObjectRequest sends a MonitoredObjectReply containing the latest PersistentStatusMessage for all currently registered eBus objects.

This leads to the next section.

Monitoring eBus applications

1. Connect to Remote eBus Applications

Establish a remote connection to all eBus applications which will be monitored.

2. Subscribe to Monitor Updates

As mentioned above, Monitor publishes ApplicationInfo and MonitorUpdate messages on a patterned subject MONITOR_UPDATE_FORMAT. If ESubscribeFeed is used receive monitor updates, then the subscriber must know all host name, application name pairs. That is, the monitored hosts and application is fixed and known a prior to subscribing. It also means that there must be a way to add or remove host, application name pairs if monitoring is dynamic.

eBus provides a way to subscribe to all monitor update subjects rather than each specific host, application name pair: EMultiSubscribeFeed using an eBus pattern as the "subject". The following code demonstrates how to use an EMultiSubscribeFeed to receive monitor updates:

import static net.sf.eBus.client.EFeed.FeedScope;
import net.sf.eBus.client.EMultiSubscribeFeed;
import net.sf.eBus.client.IESubscribeFeed;
import net.sf.eBus.util.regex.Pattern;
import net.sf.eBusx.monitor.ApplicationInfo;
import net.sf.eBusx.monitor.Monitor;
import net.sf.eBusx.monitor.MonitorUpdate;

private EMultiSubscribeFeed mAppInfoFeed;
private EMultiSubscribeFeed mUpdateFeed;

@Override public void startup() {
    final Pattern multiUpdateSubject =
        Pattern.compile(
            // Put ".+" in host and application name subject portions to accept
            // updates from all hosts and applications.
            String.format(Monitor.MONITOR_UPDATE_FORMAT, ".+", ".+"));

    mAppInfoFeed =
        (EMultiSubscribeFeed.builder()).target(this)
                                       .messageClass(ApplicationInfo.class)
                                       .scope(FeedScope.LOCAL_AND_REMOTE)
                                       .query(multiUpdateSubject)
                                       .statusCallback(this::onAppInfoFeedStatus)
                                       .notifyCallback(this::onAppInfoUpdate)
                                       .build();
    mUpdateFeed =
        (EMultiSubscribeFeed.builder()).target(this)
                                       .messageClass(MonitorUpdate.class)
                                       .scope(FeedScope.LOCAL_AND_REMOTE)
                                       .query(multiUpdateSubject)
                                       .statusCallback(this::onUpdateFeedStatus)
                                       .notifyCallback(this::onMonitorUpdate)
                                       .build();
}

private void onAppInfoFeedStatus(final EFeedState state, final IESubscribeFeed feed) {
    ...
}

private void onAppInfoUpdate(final ApplicationInfo info, final IESubscribeFeed feed) {
    ...
}

private void onUpdateFeedStatus(final EFeedState state, final IESubscribeFeed feed) {
    ...
}

private void onMonitorUpdate(final MonitorUpdate update, final IESubscribeFeed ffed) {
   ...
}

It is recommended that subscriptions be put into place before requesting latest monitor updates which is the next step.

3. Request Latest Monitor Updates

Unlike monitor notification subject, the monitor request subjects are fixed: APP_INFO_REQUEST_SUBJECT and ONGOING_REQUEST_SUBJECT. It is recommended that these request subjects be put into place on start-up but the requests themselves not be made until the repliers are known to be up. The following code demonstrates how to do this:

import static net.sf.eBus.client.EFeed.FeedScope;
import net.sf.eBus.client.ERequestFeed;
import net.sf.eBus.messages.EMessageKey;
import net.sf.eBusx.monitor.ApplicationInfoRequest;
import net.sf.eBusx.monitor.Monitor;
import net.sf.eBusx.monitor.MonitorObjectRequest;

private ERequestFeed mAppInfoRequestFeed;
private ERequestFeed.ERequest mAppInfoRequest;
private ERequestFeed mMonitorRequestFeed;
private ERequestFeed.ERequest mMonitorRequest;

@Override public void startup() {
    final EMessageKey appInfoRequestKey =
        new EMessageKey(
            ApplicationInfoRequest.class, Monitor.APP_INFO_REQUEST_SUBJECT);
    final EMessageKey ogRequestKey =
        new EMessageKey(
            MonitoredObjectRequest.class, Monitor.ONGOING_REQUEST_SUBJECT);

    mAppInfoRequestFeed =
        (ERequestFeed.builder()).target(this)
                                .messageKey(appInfoRequestKey)
                                .scope(FeedScope.LOCAL_AND_REMOTE)
                                .statusCallback(this::onAppInfoRequestFeedStatus)
                                .replyCallback(this::onAppInfoReply)
                                .build();
    mAppInfoRequestFeed.subscribe();

    mMonitorRequestFeed =
        (ERequestFeed.builder()).target(this)
                                .messageKey(ogRequestKey)
                                .scope(FeedScope.LOCAL_AND_REMOTE)
                                .statusCallback(this::onMonitorFeedStatus)
                                .replyCallback(this::onMonitorStatusReply)
                                .build();
    mMonitorRequestFeed.subscribe();
}

private void onAppInfoRequestFeedStatus(final EFeedState state, final IESubscribeFeed feed) {
    if (state == EFeedState.UP) {
        mAppInfoRequest  =
            mAppInfoRequestFeed.request(
                (ApplicationInfoRequest.builder()).subject(Monitor.APP_INFO_REQUEST_SUBJECT)
                                                  .build());
    }
}

private void onAppInfoReply(final int remaining, final EReplyMessage msg, final ERequestFeed.Request request) {
    // Process application information reply.
}

private void onMonitorFeedStatus(final EFeedState state, final IESubscribeFeed feed) {
    if (state == EFeedState.UP) {
        mMonitorRequest =
            mMonitorRequestFeed.request(
                (MonitoredObjectRequest.builder()).subject(Monitor.ONGOING_REQUEST_SUBJECT)
                                                  .build());
    }
}

private void onMonitorStatusReply(final int remaining, final EReplyMessage msg, final ERequestFeed.ERequest request) {
    // Process monitored object status reply.
}