Here we have our common code. Which, of course, means we have a complete decoupling from our presentation layer. Unfortunately, we will also have to worry about our framework, but we will do that later.
We have an enum for our statuses: ObservedStatus.java
package com.sample.observe;
public enum ObservedStatus {
OK, WARNING, ERROR;
}
Typically I'd embed a status class like this in the class it relates to, but since we don't want any knowledge of the implementation, even this is pulled out.
The next class is a singleton. One of the better ways of implementing a singleton is to make it an enum: ObservableSingleton.java:
package com.sample.observe;
import java.util.HashSet;
import java.util.Set;
public enum ObservableSingleton implements ObservableInterface {
MONITOR_PROCESS_ONE;
public static final String NOT_RUNNING_MESSAGE = "Not Running";
private ObservedStatus status = ObservedStatus.OK;
private String message = NOT_RUNNING_MESSAGE;
private Set<ObserverInterface> listeners = new HashSet<ObserverInterface>();
private Thread thread = null;
public void setMessage(ObservedStatus status, String message) {
this.status = status;
this.message = message;
notifyListeners();
}
public String getMessage() {
return message;
}
public ObservedStatus getStatus() {
return status;
}
// The setters for the two getters are replaced with the above setMessage
// public void setMessage(String message) ;
// public void setStatus(ObservedStatus status) ;
public synchronized void registerListener(ObserverInterface listener) {
listeners.add(listener);
}
public synchronized void removeListener(ObserverInterface listener) {
listeners.remove(listener);
}
public synchronized void notifyListeners() {
for (ObserverInterface listener : listeners) {
listener.updateListener(this);
}
}
private boolean done = false;
public synchronized void startProcess() {
done = false;
if (thread == null || !thread.isAlive()) {
thread = new Thread() {
@Override
public void run() {
int size = ObservedStatus.values().length;
int status = 0;
for (int i = 0; i < 100 && !done;) {
try {
Thread.sleep(50);
setMessage(ObservedStatus.values()[status], Integer.toString(i));
} catch (Exception e) {
}
if(i == 99) status = ++status % size;
i = (++i) % 100;
}
}
};
thread.start();
}
}
public synchronized void stopProcess() {
done = true;
if (thread.isAlive()) {
thread.interrupt();
}
thread = null;
message = NOT_RUNNING_MESSAGE;
}
}
Why do we need a singleton? What if we are monitoring multiple processes? The singleton is per process to be monitored. So if we had a process to monitor the health of the database, a process to monitor the network status, and a process to monitor some input from the user, that would be 3 instances of an Observable, but, there would only be one instance for each of them. In the example shown, that would be accomplised by adding values to our enum. i.e. MONITOR_PROCESS_ONE, MONITOR_DB_HEALTH, MONITOR_NETWORK, MONITOR_INPUT. Each would have its own thread, listeners, etc.
So, as we can see, the business tier is completely decoupled from the presentation tier. We have no knowledge of, nor care, how it has been implemented. We have synchronized our methods. Each method is a small fast running block of code. The one area we need to be careful of is synchronizing our notifyListeners. This method is calling another method beyond this classes control. What if the method listener.updateListener is a long running process itself? Or, it waits on another process? If we don't synchronize it, then the listener hash set could be modified while we are in the middle of processing. A solution would be to synchronize the cloning of the listeners set, then start a thread to iterate through and notify them. This would guarantee the observable wasn't stopped by poorly written code in the observer. But, of course, leaving it here will improve performance and expose poorly written code, such that we'll leave it this way.
Note: to change whatever we are monitoring is a simple replacement of the start and stop process methods.
Thus completes the first iteration of our project. Next we will do the same thing in JSF. Then we will compare the two and transition from Swing to JSF.
No comments:
Post a Comment