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

advertisement

AddThis Social Bookmark Button

Java Component Development: A Conceptual Framework
Pages: 1, 2

Only One Instance of the Component Should Run in the Enterprise

A component should have just one and only one instance running at a time, so the Singleton design pattern, which ensures having only instance in a JVM, is an appropriate choice. But while this works in a single-JVM scenario, it's a problem when multiple JVMs are used. If this component needs in multiple nodes of a cluster, each node will have its own instance. But it is still acceptable if the configuration information, loaded at component startup, doesn't need to change and it deals with completely static information.

If we assume that the component will be running in a single JVM, then ComponentControllerFactory will look like Figure 3:

Figure 3
Figure 3. Component controller factory in a single JVM

The methods provided by the singleton controller factory are:

  • getXXXService(): This method returns the service provider implementation class defined by the XML file.
  • getXXXAdapter(): This method returns the adapter implementation class defined by the XML file.

Changes in the Configuration Should Be Dynamic

If a component is immutable, each cluster node will have an identical copy of the singleton instance; but if it is not immutable and the configuration information needs to be changed dynamically, we need something different.

There are two probable scenarios when dynamic configuration change may happen:

  • A single-JVM case
  • A multiple-JVM case
A Single-JVM Case

If application will run in a single JVM, things are simpler. As we know, SingletonControllerFactory will always have a single instance in the JVM, so whenever any changes are made to the configuration file, the factory object will need to be reloaded, based on some notification mechanism, which will load the Java serializable configuration object in turn.

Here is the ConfigManager class. It is based on the Observer-Observable pattern and performs two activities:

  • It reads and processes the configuration XML file with the help of XMLizer (a separate component) and loads the configuration Java object.
  • It monitors the XML configuration file for possible changes.

Figure 4 shows the methods of the ConfigManager.

Figure 4
Figure 4. ConfigManager

The ConfigManager class acts as an Observer; when it's notified by the Observable, its update method will be called. The update() method will call the reload() method of SingletonControllerFactory so that the newly created Java object will reload its configuration information.

ConfigurationChangeNotifier acts as an Observable and starts a thread, which notifies the ConfigManager if any change occurs in the timestamp of the configuration XML file, which may indicate a change in its contents. Figure 5 shows this relationship.

Figure 5
Figure 5. ConfigurationChangeNotifier

A Multiple-JVM Case

In a multiple JVM scenario, things are not so simple. We have to have:

  • A strategy for reloading the changed configuration XML file dynamically on the fly without bringing down the whole enterprise application.
  • A strategy to have just one single instance running at a time throughout the cluster.

Using JNDI combined with RMI is one option to ensure having one and only one instance running in a particular node (JVM) of multiple nodes of a clustered environment. A RMI service needs to be written and a RMI stub would be generated out of this RMI service. This generated RMI stub needs to be bound in the JNDI tree of the application server. This object will sit on one container, which will make the object available throughout the cluster.

To deal with this situation, we need to introduce ConfigManager, which will do the following tasks:

  • Create a configuration XML file that needs to be changed dynamically.
  • Create a Java Serializable object from the XML file. Serialization and de-serialization can be done by different components.
  • Create a RMI service, register the RMI stub generated from the RMI service, and load the serializable configuration object within the RMI service.
  • Bind this RMI stub object to the JNDI tree of any node of the clustered environment.
  • Create a notification system that will rebind the RMI service and reload the object in the tree whenever that configuration XML file appears to have changed in the file system.

Figure 6 shows the RMI service interface and its implementation.

Figure 6
Figure 6. RMI service

In the multiple-JVM scenario, the ConfigManager will look like Figure 7:

Figure 7
Figure 7. ConfigManager in a multiple-JVM scenario

The ConfigManagerMultipleJVM class acts as a Observer. When it is notified by the Observable, its update method will be called. Within the update() method, the rebindRMIService() method will be called so that the newly created object (with the latest configuration information) will be reloaded.

SingletonControllerFactory will act as a wrapper for the RMI service, returning the appropriate, configured object.

The problem with this approach is that since there will be just one instance, it has a single point of failure. The ConfigManager component needs to be more robust to handle the failover.

But there is also another approach that will synchronize a cached configuration object in a different node of a cluster, with the help of MDB and JMS. In this case, RMI service is not required. Here are the steps to implement this approach:

  • The SingletonControllerFactory initializes and starts up the component with the configuration object.
  • ConfigManager's Observer-Observable model tracks any change in the configuration of the XML file with its notification mechanism. If a change is found, it publishes the message to the JMS topic.
  • An MDB running in each cluster of clustered environment triggers its own onMessage() and loads the changed configuration Java object.

A Component Should Have a Proper Third-Party Integration Mechanism

If the component depends on third-party integration to get a service, the third-party API should not be directly used in the implementation class. The best strategy would be to develop an adapter and isolate third-party calls to the adapter implementations.

Figure 8 shows an example adapter used by the logger component, which shows how it can adapt to different third-party APIs easily.

Figure 8
Figure 8. Application logger interface

The advantage of using this adapter pattern is to incorporate different third-party APIs easily. Furthermore, if those APIs change, the adapter implementation will need to be changed, but the service that uses the adapter interface will not need to change.

Choosing from several adapters is facilitated with a configuration XML file, as described in the "A Component Should Be Configurable" section above.

A Component Should Have a Proper Error-Handling Mechanism

Each component will have its own exception-handling class, which will help to catch the proper exceptions. It's assumed that we will have a separate component, specific to the business at hand, to handle exceptions. This component-specific exception class (Underwriter exception) will take the required service out of the exception-handling component.

Figure 9
Figure 9. Component exception handling

This exception class is very specific to the Underwriter service and it extends the enterprise base exception class. Its job is to wrap the exception that occurred in this service class and re-throw it.

Related Reading

Head First EJB
Passing the Sun Certified Business Component Developer Exam
By Kathy Sierra, Bert Bates

Conclusion

To summarize, here are the basic steps to put it together:

  • As part of the application startup process, ConfigManager will read the different configuration XML files for different components with the help of XMLizer (a separate component used for XML-to-Java object conversion) and bind the configuration Java object with the JNDI tree of an application server node.
  • As part of the component startup process, the configuration object will be read and accordingly, the relevant provider/adapter/service classes will be instantiated.
  • If changes happen in configuration, ConfigManager will read the changed XML and rebind the configuration object.
  • The component will reload the configuration object and reinitialize with its latest changes.

Coming back to where we started: a component framework can be effective in adapting to changes in business and technology when you are planning to develop a robust system. The best part of this conceptual framework is that it completely isolates the component management/life cycle process from the business logic and different third-party APIs, by introducing the concept of different plug-and-play service providers. Even when changes are made, you do not have to worry about the rest of the code, other than changing/replacing the service provider. This in turn makes the application more maintainable, adaptable, and robust.

Palash Ghosh is a BEA Certified Enterprise Architect, IBM Cerified OOAD & SOA Soln Designer and has more than 11 years of software architecting, designing, management experience working with the global fortune 100 companies focusing on providing business and technology solutions across diverse range of technologies.


Return to ONJava.com.