ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


The Singleton as a Network Management Pattern The Singleton as a Network Management Pattern

by Stephen B. Morris
10/27/2004

Service-driven networking requires rapid automated provisioning in response to user demand. Suppose you want to increase your allocated bandwidth or access the latest broadband game from your service provider. You fill in an online form, a provisioning server updates your profile, and you then have access to the required resources. Service providers are falling over themselves to achieve this type of customer response! Speed and accuracy of order fulfillment is of the essence.

An important element of this is the provisioning server--the software that modifies the network to deliver a requested service. In this article, I'll look at how to use the Singleton pattern, among others, to implement a simple, extensible provisioning server. The latter could be used to modify the bandwidth of the service provider link for two types of users: a home office user, and a large enterprise network manager.

A good reference for this material is Craig Larman's Applying UML and Patterns. As is well described in Larman's book, the major merit of using patterns is the speed at which fairly complex and complete software can be written. The other merit is that patterns encourage architects, designers, and developers to think outside the box (i.e., outside the code) by sharing a common vocabulary. A simple example is when an architect or designer says, "Class X should be loosely coupled to class Y, because Y will change in the next release." The developers can then create the two classes so that X and Y are minimally co-dependent. This is a pattern example that helps in reducing unnecessary future code changes.

One of the best investments of our precious time is to study a good patterns book and, as you read it, try to dream up applications (in your own specific domain) for each of the different pattern variants. This is a difficult undertaking, but potentially very rewarding; later, when you're embroiled in a project and trying to solve tough problems, you can consult your patterns notes to see when/if a pattern can be employed. This is also often a good time to update your notes. Patterns help move practitioners up the value chain--crucial in an era of outsourcing.

Service with a Smile

Our goal here is to be able to quickly switch on a network service. To do this, we must interact with the network and change its configuration and state in some fashion (e.g., allocate more bandwidth from a service provider). This simple task is often incredibly difficult to achieve in telecom networks! It requires interaction with multiple back-end systems--service portals, databases, and network devices, as illustrated in Figure 1.

Figure 1
Figure 1. Service provider network layers

In spite of this complexity, as we'll see below, there is a growing demand for customer-driven network management. One reason why telecom is especially complex is because of its intrinsically three-tier nature--users, back-end systems (provisioning servers and databases), and network devices (routers and switches), as illustrated in Figure 1. Let's now take a quick look at the typical workflows that accompany the user-driven service change requests.

Related Reading

Head First Design Patterns
By Eric Freeman, Elisabeth Robson, Kathy Sierra, Bert Bates

Sample Workflow

The home office user in Figure 1 places an online order for an increase in the bandwidth of her broadband connection (Link 1 in Figure 1). Likewise, the network manager at the headquarters site also places an order to increase her bandwidth on Link 2. These two orders are typically routed through some type of workflow system (not shown in Figure 1) that verifies the change requests. The change requests are ultimately translated into a series of device-configuration changes that are applied in an orderly fashion to the routers (the hockey puck symbols) in Figure 1.

We'll simulate this workflow with a simple user-driven GUI front end that communicates with the provisioning server in Figure 1. This GUI is typically accessed using a browser. The provisioning server then applies the required changes to the back-end systems (in this case, storing the request in a relational database) and the associated network devices.

In the midst of all of this complexity, we want to provide a way to rapidly implement the required capability. Patterns come to the rescue! Let's briefly review the major requirements:

We need a basic GUI for the customers in Figure 1. This GUI facility is often called customer network management and is increasingly a feature of service provider networks. A very popular customer network management feature is called service-level assurance monitoring. This allows a customer to view the parameters of a contractual service level agreement (e.g., 2Mbps guaranteed, a 20ms round-trip delay, etc.). If the customer breaks the agreement by sending too much traffic, then the provider can drop excess traffic. If the service provider fails to meet the agreement (e.g., dropping or delaying traffic during periods of congestion), then the customer gets a financial rebate. Creating the infrastructure to do this is at the leading edge of modern network development.

Our provisioning server handles all service change requests and typically routes them for validation to another back-end system. This offloading helps reduce the load on the provisioning server itself.

The provisioning server focuses its efforts on presenting a single instance, receiving orders, and updating the complex back-end systems. In network management, it's often a key requirement that there is only one provisioning server instance. Orders are routed to this instance using intra-JVM static access (as used in this example), RMI, CORBA, etc. Traditionally, the server software is installed on a single designated machine and clients then access the services it exports. This solves the problem of avoiding multiple instances of the server code--by deploying just one. In other words, the provisioning server is a good candidate for implementation using the Singleton pattern. Let's take a look at it.

The Singleton Pattern

The Singleton pattern is often used where you want just one (and only one) instance of a class in a given JVM. This can be useful for applications that require a single point of entry for a block of functionality (e.g., order fulfillment in our example domain). In the telecom world, it's often highly important to have ordered use of devices, such as routers or legacy network elements. By ordered, I mean configuration commands are sent at a predetermined rate of arrival, so as not to overwhelm the device. In many cases, the devices implement network management facilities in code that has a lower priority than the traffic handling code. Remember, network devices have a lot of work to do besides responding to network management commands--all the more so, as network technology speeds and feeds increase.

In effect, resource consumption can be controlled using the Singleton pattern.

The Service Order GUI

The service order GUI is very simple--it allows a user to see and update the existing configuration of his or her service. For our home office user, this is 56Kbps on Link 1 in Figure 1, and for the corporate user, it's 5Mbps on Link 2 in Figure 1. In passing, please note that the units of kilo- and mega- in the telecom world relate to 1000 and 1,000,000, respectively (not 1024 and 1,048,576, as used in the storage domain). So 56Kbps is actually 56,000bps and 5Mbps is 5,000,000bps.

Let's assume our home office user wins the lottery and decides to upgrade her connection by 1Mbps. She runs the service configuration program and sees the GUI shown in Figure 2. This is the allowed view of the service for our newly rich home office user!

Figure 2
Figure 2. Service configuration view

The current allocated service level is 56Kbps, and three options are available with the portal in Figure 2:

Selecting the update option sends an order to the provisioning server to upgrade the service by 1Mbps, as illustrated in Figure 3. The order is dispatched to the server and the result is seen in Figure 3.

Figure 3
Figure 3. Service upgrade GUI

The Provisioning Server

The provisioning server is implemented as a single class using the Singleton pattern. The main element is a list collection into which GUI orders are placed. In a production system, the server might be implemented as an RMI (or CORBA) endpoint. The implementation here is much simpler but allows for extension. The key element is the use of the Singleton pattern. The server class provides a private constructor and maintains a private static instance of itself.

As we'll see below, the list collection maintained by the provisioning server is synchronized. Given that we can have just one instance of the server class, this means that the list is protected against multiple thread access. In addition, each list entry represents a complete end-user order (i.e., each entry represents an atomic action of the part of the server). Once an entry is created, the client can invoke the appropriate server action. Let's look at these actions.

The server offers four public methods to clients:

The getInstance() method returns a reference to the provisioning server Singleton class. The client can use this to call the other public methods, as described below.

The executeCommand() method dispatches an order (formed via the GUI) to the server. This order takes the form of a textual message that includes the operation count, the user ID, and the order details. Orders are appended to an operations list object. The server could store these details in the database (via JDBC) and apply the required updates to the network routers (via SNMP or some other device access technology).

The undoCommand() reverses the most recent order submitted by the associated GUI client. An important part of this is that the orders from the user types are not mixed up. If the enterprise network manager decides to reverse an order, then it's essential that this has no effect on the home office user.

The toString() method returns a string representation of the operations list for a specific user. In this program, we have two possible users: "Home Office User" and "Enterprise User", respectively.

The Java Code

Three Java classes make up the code base in this example:

RunPattern.java executes the software. It opens with a brief description of the program. Next, two GUI user instances are created (Figures 2 and 4, respectively). The associated users can then interact with their GUIs and update their service provider links as required. A production system would package this differently, perhaps as a web service.

public class RunPattern {
    public static void main(String [] arguments) {

        ServicePortal portal1 = new ServicePortal();
        portal1.createUserView("Home Office User",
        "Service - 56Kbps link");

        ServicePortal portal2 = new ServicePortal();
        portal2.createUserView("Enterprise User",
        "Service - 5Mbps link");
    }
}

ServicePortal.java

The ServicePortal class provides a simple Swing GUI that is coupled to the ProvServer class. The buttons on the GUI provide access to the appropriate methods in the latter.

	// Call into Prov Server to update portal
    public void refreshTextDisplay() {
        textDisplay.setText("Provisioning Server " +
        "Command History for " + userId +"\n" +
            ProvServer.getInstance().toString(
            userId));
    }

    public void actionPerformed(ActionEvent evt) {
        Object originator = evt.getSource();
        if (originator == updateService) {
    executeCommand(" " + userId +
    " Increase bandwidth by 1Mbps");
        }
        else if (originator == undoButton) {
    undoCommand();
        }
        else if (originator == exitButton) {
            exitApplication();
        }
    }

    private void executeCommand(String message) {
        ProvServer.getInstance().executeCommand(
        (++operationCount) + message);
refreshTextDisplay();
    }

    private void undoCommand() {
        Object result =
        ProvServer.getInstance().
        undoCommand(userId);

		if (operationCount > 0)
		operationCount--;
		refreshTextDisplay();
    }
}

ProvServer.java

The ProvServer class implements the required provisioning server. As described earlier, it has two private data members: an operations list and an instance of itself. The latter is crucial in the design, because no client can call the constructor. Assuming there is just a single provisioning server installed in the network, this avoids having more than the required one instance. Clients access the methods via the getInstance() method.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ProvServer {
    private List opList =
    	Collections.synchronizedList(
    		new ArrayList());
    private static ProvServer instance =
    	new ProvServer();

    private ProvServer() {
	}

    public static ProvServer getInstance() {
        return instance;
    }

    public void executeCommand(String command) {
		opList.add(command + " Operation --> " +
			updateBackendSystems(command));
    }

    private String updateBackendSystems(
    String command) {
		return "Succeeded";
	}

    public String toString(String userId) {
        StringBuffer result = new StringBuffer();

        for (int i = 0; i < opList.size(); i++) {
			if (((String) opList.get(i)).
			indexOf(userId) != -1) {
				result.append("  ");
				result.append(opList.get(i));
				result.append("\n");
			}
        }
        return result.toString();
    }
}

Conclusion

The Singleton pattern is an excellent candidate for a class that must not be instantiated more than once. As with patterns in general, it offers a simple and elegant solution to the case at hand in this article--a provisioning server. The ease with which the Singleton pattern can be employed helps to free the developer to get to grips with solving the complex application domain issues. This provides a powerful combination of a solid pattern-based foundation and more time than usual to focus on application value add features. It allows for better product differentiation.

I didn't look at any of the middleware code that typically forms part of our service provisioning system. Typically, this code makes use of network device technology, such as SNMP, command-line interface, etc. I'll cover some of these technologies as part of an upcoming article on SNMP (JDMK) and JMX. As usual, I'll make use of some other patterns for this--most notably the GoF (Gang of Four) Adapter pattern. The latter is employed to hide the complexities of technologies such as the ones mentioned.

Patterns provide fertile design-level ground for top-grade software development. By solving significant generic problems, such as single instance control, patterns should become an indispensable tool for all software developers. They allow for rapid solutions to difficult recurring problems.

One aspect of patterns that particularly appeals to me is that their application can cross industry segments. In other words, if a practitioner makes an honest effort to understand and apply a wide range of patterns in their application domain, then there's a good chance that this knowledge is portable to another industry. A patterns-literate developer could (with some additional domain training) move, for example, from programming telecom to finance systems.

Resources

Stephen B. Morris is an independent writer/consultant based in Ireland.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.