Server-side Java: Patterns for flexible initialization, Part 2

Customizing command targets in your system initialization

After some pain and agony, your application system is now recoded to use the Setup pattern described in "Server-side patterns for flexible initialization, Part 1." Although it certainly took some effort to dispose of the fairly specific setup structure you had implemented, all of it is now working smoothly. However, you are still two steps away from being able to launch the system in a general manner:

  1. You must still know the name of the method to call in the dynamically loaded object to start up the system. It may be properly configured, but you still must invoke the launch method in a general enough way. Thus, you need to choose a general approach and a pattern for invoking that method.
  2. There is currently no general runtime control over the various system modules; the system is presently -- per definition -- running in a normal operating state when you launch the startup method as described above. Most application systems need fine-grained runtime control and several operation states to function properly. You need, therefore, to implement a pattern that would provide a general system-state control.

When selecting any approach for achieving general runtime control in a large application system, you must modify existing code as little as possible. If it is done correctly, you should need to modify only one class per application system module to enable general centralized system-state control. The two patterns that most closely match the restraints imposed by the two conditions listed above are slightly modified versions of the Builder, Facade, and Bridge patterns. If you add these to the modified Abstract Factory pattern (discussed in Part 1), your setup system will reach the level of maturity needed for general application systems. The process flow should therefore be extended as shown in Figure 1:

Figure 1. Augmented setup process flow

The three major process steps are illustrated with the green arrows in Figure 1. You must implement classes corresponding to all these steps to instantiate the startup object and launch the system from the parameters in the configuration file. The two first components of the flow, ConfigFileParser and ObjectFactory (shown in Figure 2), were examined in some detail in Part 1. In this article, I will discuss the needs of the last setup process step, State, as well as the runtime management of application system state. The component handling the invocation and centralized management of system state is known as the system controller instance. This instance is described by a Java interface: SystemController, as shown in Figure 2:

Figure 2. Augmented setup process flow and components

The main reason for using interfaces to describe all process step handlers is their flexibility. Any process flow component may be reimplemented in any manner without breaking the initial system design. You have already seen that the ConfigFileParser and ObjectFactory steps are declared as Java interfaces, implemented by specific subclasses. Preserving the pattern, the SystemController is declared and implemented in the same manner. Since a runtime system controller is a fairly complex entity to implement, its substructure is compound enough to accommodate different levels of automation and system integration.

Figure 3. Setup system partial class diagram

I covered the leftmost hierarchy of the startup system of Figure 3 in Part 1. I will cover the rightmost SystemController interface and its associated classes in this segment. The ObjectFactory creates a single instance of a SystemController subclass (a concrete subclass of the AbstractSystemController) and invokes a method to create all system subcomponents. After all these subcomponents are created, you can use the SystemController interface to monitor and control the application system's operation status. Most larger application systems enable their operation status to turn services on and off in a correct order when starting up or shutting down the system (or some parts of the system). For instance, the services of any Unix operating system are started in a controlled fashion when the operating system changes operation state (run level).

I recommend designing your system with the Interface pattern as much as possible, since any implementation may dynamically be switched for another without affecting other system modules. The overall design could, of course, be improved and boosted to accommodate even more powerful features -- but for this article, the important steps are the complete process and module management as a whole and the collaboration between the interface implementations.

In Part 1, the collaboration diagram showing interaction between the Startup class and its ObjectFactory and ConfigFileParser utility classes consisted of two method calls.

The doSetup sequence diagram, shown in Figure 4, is now modified with an extra method call, which loads all system modules (subsystems) and puts the application system in normal state:

Figure 4. The doSetup() sequence diagram
1 2 3 4 Page 1
Page 1 of 4