Under the Framework's Hood
As you can see in the class diagram (see
Figure 1), the framework consists of four classes and one interface. The framework exposes only the Info interface and the
LWMFramework class to the client code that invokes the framework. This way, the client is transparent to the internal implementation of the framework.
All the components you need to monitor must implement the Info interface. The
isOk() method checks the status and the
process() method handles corrective action:
public interface Info {
//checks the status
//returns true if ok
//throws Exception if it could not be monitored
public boolean isOk() throws Exception;
//handles corrective action
public boolean process();
}
The framework uses two background threads:
- StatusMonitor for checking the status of each component
- StatusManager for executing corrective action and status reporting
The StatusMonitor (see Listing 1) polls every second (or configurable interval) to see if the counter of any monitorable object expired. If so, it calls the monitor() method on the monitorable object.
Listing 1. StatusMonitor Class
public class StatusMonitor extends Thread {
private final int monInterval; //in milliseconds
private final Monitorable[] monArray;
public void run() {
while(true) {
for(int cnt = 0; cnt < Integer.MAX_VALUE;
cnt++) {
//iterate through the array
for(int i = 0; i < monArray.length; i++) {
//check if counter expired
if((cnt % monArray[i].getCounter())
== 0) monArray[i].monitor();
}
}
try {
sleep(monInterval);
} catch(InterruptedException ie) {}
}
}
}
Each monitorable object (see Listing 2) is associated with a StatusManager object and an Info object. Also, the attributes such as counter (the frequency of monitoring) and criticality are part of the monitorable object.
Listing 2. Monitorable Class
public class Monitorable {
private int counter;
private byte criticality;
private byte prevStatus;
private StatusManager manager;
private Info info;
public void monitor() {
byte currentStatus = NOT_OK;
try {
if(info.isOk()) {
currentStatus = Monitorable.OK;
}
} catch(Exception ex) {
currentStatus =
Monitorable.COULD_NOT_BE_MONITORED;
}
//call setchanged if there is change in status
//or if status is not ok or cud not be monitored
if((currentStatus != prevStatus) ||
currentStatus != StatusManager.OK) {
synchronized(this) {
prevStatus = currentStatus;
}
manager.setChanged(this);
}
}
//return the last monitored status
public synchronized byte getStatus() {
return prevStatus;
}
//recovery action
public void execute() {
info.process();
}
}
The monitorable object keeps track of the status changes in the Info object and notifies the StatusManager thread. The communication between StatusMonitor and StatusManager is via the monitorable object.
StatusManager (see Listing 3) receives notification when the monitorable object calls the setChanged method. It then fetches the monitorable object from the queue. If the status of the monitorable object is Not Ok, it invokes the execute() method on the monitorable object. The monitorable object in turn calls the associated Info object's process() method to perform correction action.
Listing 3. StatusManager Class
public class StatusManager extends Thread {
//add to the queue and
//notify that the status of monitorable has changed
public synchronized void setChanged
(Monitorable monitorable)
monQueue.add(monitorable);
notifyAll();
}
//get the Monitorable object from the queue
public synchronized Monitorable getMonitorable() {
// return the object from the queue
while(monQueue.isEmpty()) {
try {
wait();
} catch(Exception ex) {}
}
return (Monitorable) monQueue.remove(0);
}
//report or execute corrective action
private void processChange() {
Monitorable monitorable = getMonitorable();
//take corrective action
if(monitorable.getStatus() == NOT_OK) {
monitorable.execute();
}
//log or report the status
}
public void run() {
while(true) processChange();
}
}
Currently, the recovery task executes in the context of the StatusManager thread. StatusManager can be enhanced to maintain a pool of threads for executing recovery tasks in a separate thread.
The startup class of the framework, LWMFramework, is used for configuring and initializing the framework. The sequence diagram in Figure 2 illustrates the interaction between various framework objects. The client code starts the framework after adding all the Info objects. During startup, the StatusManager and StatusMonitor threads are launched.
Synchronization
StatusManager maintains a thread-safe queue of monitorable objects. The StatusManager thread fetches the monitorable object from the queue and calls the
getStatus() method on it. The access to the
status variable of the monitorable object is also synchronized.