ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Reducing Upgrade Risk with Aspect Oriented Programming

by Stephen B. Morris

Upgrades present risks to software systems, not least of which is inadvertent damage to features and data. Telecom systems, in particular, typically have stringent architectural availability and reliability quality attributes. Adding new telecom features is often fraught with difficulty--the same is true for other demanding areas such as flight simulation.

In this article, I'll use aspect oriented programming (AOP) as a means of intercepting Java methods that I want to upgrade. The intercepted code can then be added to the operational system to provide the required interim feature(s). For the next major release (following the upgrade), the upgrade code can then be moved out of the interception area into the main code base. This use of interception reduces the risk of updating the main class.

The intercepted code is set up using the following dynaop-based script file. (Please see the dynaop project on java.net for more information.) This is a script file in the sense that it annotates a class and prepares it for interception by another at runtime. It's not an executable script file.

// Apply interceptor to all getter methods in the class  
   new UpgradeInterceptor()

The NMSSubject class is a client in the Observer pattern; we want to upgrade the method getNewData(). The UpgradeInterceptor class provides the AOP instrumentation for intercepting getNewData(). Once intercepted, the method can be augmented (to call into a JNDI naming service) and the expanded data is returned transparently to the original client.

The Takeaway

I'll make use of the Observer design pattern and upgrade a subclass method via AOP. This illustrates the merit of separating code for handling data production and consumption. An additional strand introduces, in passing, some software architecture concepts.

The Observer Design Pattern

The Observer pattern (also known as publish-subscribe) is useful for designing code with a single server and multiple client classes. It implicitly encourages the separation of concerns between data producers and consumers. An example from network management is where a server application maintains a virtual picture of the topology of network of devices. It does this by a combination of polling (actively requesting) and (passively) listening for messages concerning device and link status. Figure 1 illustrates the idea with a topology server that communicates with a network of devices. A set of clients then takes a view of the topology server's data via the Observer pattern.

Figure 1
Figure 1. Topology Server and the Observer Pattern

The fidelity of the "picture" maintained by the server determines the quality of the users' view of the managed network. This is an important networking area, referred to as discovery. Many device types support their own version of discovery; e.g., IP routers work hard to maintain an up-to-date picture of the surrounding network. If a distant link fails, then the routers must try to figure out the effect this has on their internal tables. This process is called convergence and can be thought of as a specific example of the more general problem of discovery. For more on this and related subject matter, please see Network Management, MIBs & MPLS: Principles, Design & Implementation.

Returning to Figure 1, the topology picture (or designated parts of it) can be communicated to the clients and kept up to date using the Observer pattern. In other words, when the topology server detects a change in the network, it communicates this to the clients using an appropriate method in the Observer pattern.

Let's take a look at an implementation of the observer.

The Observer Subject

My data producer (or publisher) is a class called NMSSubject that implements an interface called Subject.

interface Subject {
   void addObserver(Observer observer);
   void removeObserver(Observer observer);
   void notifyObservers(); }

As we can see, Subject has three methods that allow observers to be added, removed, and notified (the notify is used for updating observer data). These methods operate on a Collection object in the NMSSubject class (the acronym "NMS" stands for network management system).

public class NMSSubject implements Subject {
   Collection observers = new ArrayList();

   public void addObserver(Observer observer) {
     observers.add(observer); }

   public void removeObserver(Observer observer) {
     this.observers.remove(observer); }

   public void notifyObservers() {
     for (Iterator i = observers.iterator(); i.hasNext();)
       ((Observer) i.next()).update(this); }

   // Have AOP intercept the next call and in turn call into JNDI
   // The legacy code might have been reading from a file or database.
   // The intercepted code could call into JNDI or some other API
   // This can be used as a temporary measure to avoid an upgrade
   public String getNewData() {
     return "Data Set: "; // Call into JNDI here via AOP }

The last method in NMSSubject is called getNewData(), which is our target upgrade code. We want to intercept calls to getNewData() via AOP and upgrade them transparently, so that the calling client will not be aware of any interception.

The Observer Client

A single interface provides an update method, into which is passed an instance of the NMSSubject class. This update method calls into the getNewData() method of the subject class.

interface Observer {
     void update(NMSSubject subject); }

public class NMSListener implements Observer {

public void update(NMSSubject subject) {
	  updateView(subject.getNewData()); }

public void updateView(String data) {
	  System.out.println("Updated subject data is " + data); }

The getNewData() method is intercepted for upgrade by a class called UpgradeInterceptor. Let's look at this class.

Pages: 1, 2

Next Pagearrow