Java Component Development: A Conceptual Framework
by Palash Ghosh
03/23/2005
Let's introduce a few business scenarios that need attention while architecting
and designing a solution to accommodate continuous changes in business:
- A company wants to move its document repository from Documentum to FileNet.
- A company wants to change its security provider.
- An insurance company has to change its policy issuance process to a large
extent because there is a tremendous change in economic situation.
One thing is clear: requirements change very fast as business and technology
change. But with every change, big or small, do we need to throw away the complete
system and start over? Not necessarily--a little thought, a good strategy,
and best practices during architecting and designing a new solution could adapt
the existing architecture to those changes without much hassle.
In object-oriented programming and distributed object technology, a component is
a set of classes and interfaces to fulfill requirements (functional and non-functional)
with a reusable external API. Components should be able to run in a distributed network
environment to form a network application. Component-based design and development
is not a new topic at all to professionals who are following Object-oriented
analysis and design (OOAD) methodology.
The goal of this article is to arrive at a common conceptual framework to
develop a Java component step by step, following Java best design practices, and
starting from scratch. The expected audience of this article needs
to have prior knowledge of Java and UML, and Java/J2EE design patterns. The key
areas to be addressed in the this article are:
- The basic properties of a component.
- How to achieve those basic properties in Java component design, using
Java design best practices (design patterns), and arrive at a conceptual basic
component development framework, which can be used in any component development.
Basic Properties of a Component
To meet the definition, a component must satisfy the following requirements:
- A component must have a service interface (API) so that other components can
interact with it.
- A component should have a proper life cycle mechanism (start, stop, initialize, etc.).
- A component should be configurable.
- Only one instance of the component should run in the enterprise.
- Changes in the configuration should be dynamic (on the fly).
- A component should have a proper third-party integration mechanism.
- A component should have a proper error-handling mechanism.
How to Achieve Basic Component Properties
A Component Must Have a Service Interface (API)
Whenever we write 100 to 1000 lines of code in a single class or number of
classes, ultimately the work product (the class or the combination of classes)
provides some basic high-level services. Thinking backwards, we can identify
those basic high-level services we are trying to achieve, even before implementing
them.
Let's give an example from the insurance domain, where an underwriter does
the following tasks in his day-to-day activities:
- Checking an insurance proposal.
- Assessing and gathering background information.
- Calculate the premium using the company's existing rule set.
- Gathering information and various types of reports (medical, etc.) from
other departments.
- Writing and preparing a policy.
So now if we try to write an Underwriter business component, we will have
to have a service interface and its implementation, as seen in Figure 1:
Figure 1. Underwriter service interface
When another component requests a service from the Underwriter component,
it doesn't need to worry about what is happening inside of the component. Encapsulating
its business logic within itself makes the component more maintainable and
extensible.
The Service component will have one main implementation service class (an
implementation of the Service interface) and this class may make use of helper
classes that are part of this component, and perhaps use other components, too.
In product development, we may have many components providing different types
of services. For example, in the insurance field, we could have a "claim-processing
component," a "policy-holder service component," and more. So we must have
a strategy to register those service components in the enterprise solution,
in order to be able to enable or disable those services according to the specific needs of the
enterprise.
Here is an example XML structure, which can handle this service registration
process automatically.
<Services>
<Service>
<Serviceid>S001</Serviceid>
<ServiceName>UnderwriterService</ServiceName>
<ServiceImplClass>
com.org.service.UnderWriterServiceImpl
</ServiceImplClass>
</Service>
<Service>
<ServiceId>S002</ServiceId>
<Servicename>PolicyHolderService</ServiceName>
<ServiceImplClass>
com.org.service.PolicyHolderServiceImpl
</ServiceImplClass>
</Service>
</Services>
A Component Should Have a Proper Life Cycle Mechanism
The component also needs a built-in, visible, independent mechanism in its
life cycle so it can be started and stopped as needed. This ComponentControllerFactory is
a singleton, since only one instance is needed. This factory is responsible
for creating instances of a class for different providers based on the configuration
information. ComponentControllerFactory plays a dual role: first it manages
the component life cycle with its init(), reload(), etc.
methods (which is why it's a "controller"), and secondly it instantiates the
class based on the parameter passed to it (which is why it's a "factory").
Figure 2 shows its methods.
Figure 2. Component controller factory
The component life cycles methods are:
-
doStart(): Starts the component
-
doInitialize(): Helps to read from the Configuration object
created from XML configuration file and is responsible of creating an instance
of the appropriate class.
-
doStop(): Stops the component
-
reload(): If changes occur in configuration XML file when
the component has been started already, this method will read the configuration
XML file once again and restart the component.
-
getInstance(): Returns the instance of the ComponentControllerFactory
class.
A Component Should Be Configurable
Generally, every component will have its own configurable parameters, which
are not required to change often. For example, suppose we need to write a Cache component,
which needs to be refreshed every hour to load some semi-static data from a
database. The value of the refresh time should come from a configuration
file, so that the value of that the parameter can be changed without touching the
source code.
Here is one example of a configuration XML file for a logger component, which
takes care of logging throughout all the layers of an enterprise.
<LoggingServiceProvider>
<Provider>
<ProviderName>Apache</ProviderName>
<AdapterImpl>com.org.integration.adapter.Log4jAdapter
</AdapterImpl>
<Enable>true</Enable>
</Provider>
<Provider>
<ProviderName>WebLogic</ProviderName>
<AdapterImpl>com.org.integration.adapter.WebLogicAdapter
</AdapterImpl>
<Enable>false</Enable>
</Provider>
</LoggingServiceProvider>