 | Level: Intermediate J. Jeffrey Hanson (jeff@jeffhanson.com), Chief Architect, eReinsure.com, Inc.
11 Jul 2006 Developing software without regard to the application program interface (API) constraints enforced by libraries and frameworks is an appealing proposition. This lure has led many to accept the paradigm of Plain Old Java™ Object (POJO) programming -- the idea that you should be able to develop software on the Java platform without being required to use superfluous interfaces or third-party APIs. The Apache Geronimo framework provides a solid infrastructure for POJO development to build sophisticated applications and services. Discover some of the components and techniques of the Geronimo framework that you can use to realize successful service-oriented development using POJO tactics.
Service-oriented architecture (SOA) and service-oriented programming are terms that refer to a style of software engineering that
encapsulates business logic as modular services. These services target dynamic runtime environments
in which associations between service providers and service consumers are loosely coupled. Services that are
loosely coupled typically have no compile-time associations, so you can link them dynamically at run time,
allowing deployment personnel the flexibility to make deployment decisions as needs arise. In addition to loose couplings, the following concepts are common in a service-oriented environment:
- Coarse granularity: Granularity with services refers to the extent of functionality that a service
presents publicly. Fine-grained services present public interfaces defining a specialized degree of
functionality. Coarse-grained services present a more general degree of functionality, typically to suit
a given business domain.
- Location transparency: Location transparency refers to the idea that clients can access services
without regard to location on a network.
- Protocol independence: Protocol independence refers to the idea that clients can access services
without regard to the communication/network protocol.
Building services with these concepts in mind can be a daunting task. POJO programming seeks to simplify this task.
Introducing POJOs
POJOs are Java classes that are not required to adhere to specific external interfaces or third-party APIs. This ability
in itself essentially decouples code from external associations. One of the primary benefits of this decoupling
is the freedom it gives software developers from ancillary tasks, such as persistence, transaction support, and
remoting. Many techniques eliminate component/class decoupling and facilitate POJO programming, including:
- Annotations are metadata that development tools consume and use to generate code that "decorates" a
class or a portion of a class to support a given type of functionality or feature, such as remoting,
persistence, and framework support.
- Dependency injection is a technique for building pluggable components whereby object creation and
association is removed from the components and carried out by containers or assembler components.
- Reflection is the runtime discovery of information, such as methods, fields, and constructors, about a
given class or interface.
Each decoupling technique has its advantages and disadvantages. This article proposes a simple SOA framework through POJO programming that uses reflection and Geronimo's GBean dependency injection to enable component decoupling.
JMX and Geronimo
Geronimo is built on a general-purpose kernel that uses Java Management Extensions (JMX) and a dependency injection
framework of managed components called GBeans. Virtually everything in Geronimo (adapters, applications,
containers, and so on) is either a GBean or is based on a GBean. GBeans share many similarities and underlying
infrastructure with JMX and JMX Managed Beans (MBeans).
 |
JMX
The JMX specification has emerged as the Java standard for systems management, applications management, and resource
management. JMX defines a standard for dynamically augmenting Java classes, interfaces, and runtime objects with
attributes and operations to be used for management purposes. This augmentation technique is known as instrumentation.
JMX can manage any resource (such as an application, device, or service) that you can abstract using the Java programming
language. Each managed resource is referred to as an MBean. JMX defines four types of MBeans:
- Standard MBeans use Java interfaces to define their management attributes and operations.
- Dynamic MBeans use runtime discovery to define their management attributes and operations.
- Model MBeans act as proxies for objects wanting to expose
manageable operations and attributes.
- Open MBeans use a predefined metadata vocabulary to expose the manageable attributes and
operations of classes and objects.
The primary interface to interacting with MBeans is the javax.management.MBeanServer. The
MBeanServer acts as a central repository of MBeans and facilitates communication with the
MBeans from MBean clients.
MBeans are uniquely identified by an ObjectName object. An ObjectName
instance consists of:
- A domain, which is an arbitrary name for a given domain; conventions recommend using reverse domain name
system (DNS) naming for domains in the same manner as Java package naming.
- A key property list, which is an arbitrary, unordered set of keys and associated values.
The following code demonstrates constructing a typical ObjectName object:
String domain = "com.jeffhanson.test";
String keyPropertyList =
"domain:Name=TestBean,Type=GenericService";
ObjectName objName =
new ObjectName(domain + ":" + keyPropertyList);
ObjectName objects are used as parameters to many of the
MBeanServer methods to retrieve attributes and invoke operations on an MBean.
|
|
Geronimo's GBean framework
GBeans are managed components in Geronimo that share many similarities and relationships with JMX MBeans, such as
exposing attributes and operations according to a class named GBeanInfo, which is
strikingly similar to the JMX equivalent MBeanInfo class. Geronimo uses the
MX4J library (see Resources at the end of this article for a link) as its implementation of JMX.
GBeans maintain states and association dependencies, and they handle life cycle events. A GBean can register as
an interested party in the status of other GBeans. After the GBean of interest is started, the interested GBean
will receive a reference to the GBean of interest through dependency injection. A GBean can be in one of these seven life cycle states at any given time:
- Loaded
- Unloaded
- Starting
- Running
- Stopping
- Stopped
- Failed
Listing 1 illustrates a simple GBean containing one attribute (message).
Listing 1. A typical GBean
public class TestGBean
implements GBeanLifecycle
{
private static GBeanInfo GBEAN_INFO = null;
static
{
GBeanInfoBuilder infoFactory =
GBeanInfoBuilder.createStatic(TestGBean.class);
infoFactory.addAttribute("message", String.class, true);
infoFactory.addOperation("getMessage");
GBEAN_INFO = infoFactory.getBeanInfo();
}
private String message;
public String getMessage()
{
return message;
}
...
}
|
You can start (activate) and stop the GBean with the code illustrated in Listing 2.
Listing 2. Starting a typical GBean
ObjectName testGBeanOName =
ObjectName.newInstance("jeffhanson.test:ID=test");
GBeanData gBeanData =
new GBeanData(testGBeanOName, TestBean.GBEAN_INFO);
gBeanData.setAttribute("message", "Hello world");
geronimoKernel.loadGBean(gBeanData,
Thread.currentThread().
getContextClassLoader());
geronimoKernel.startGBean(testGBeanOName);
...
geronimoKernel.stopGBean(testGBeanOName);
geronimoKernel.unloadGBean(testGBeanOName);
|
You can use GBeans extensively throughout the Geronimo kernel.
A Geronimo kernel
A Geronimo kernel is a framework of GBeans. Using this framework, you can model and build most any sophisticated
system as a set of GBean containers and GBean components to manage state, relationships, and event handling.
Creating a Geronimo kernel programmatically is a simple process using the KernelFactory classes.
Listing 3 demonstrates how to create a new Geronimo kernel named TestGeronimo,
booting the kernel, logging the boot time, and loading and starting a servlet GBean.
Listing 3. Creating a simple Geronimo kernel
try
{
Kernel geronimoKernel =
BasicKernelFactory.newInstance().
createKernel("TestGeronimo");
geronimoKernel.boot();
log.debug("Geronimo BootTime: "
+ geronimoKernel.getBootTime());
// add the servlet GBean
ObjectName servletObjName =
new ObjectName("jeffhanson.test:ID=MyGBean");
GBeanData servletGBeanData = new GBeanData(servletObjName,
GBEAN_INFO);
ClassLoader classLoader = getClass().getClassLoader();
geronimoKernel.loadGBean(servletGBeanData, classLoader);
geronimoKernel.startGBean(servletObjName);
}
catch (Exception e)
{
log.error(e);
}
|
After you create and run the kernel, invoking a method on a POJO service simply becomes an exercise in using
the Geronimo kernel server and its reflection capabilities, as shown in Listing 4.
Listing 4. Invoking a call on a service registered with the kernel
private static
Object invokePOJOService(ObjectName serviceObjName,
String operationName,
String[] params)
throws Exception
{
String[] paramTypes = null;
if (params != null && params.length > 0)
{
paramTypes = new String[params.length];
for (int i = 0; i < params.length; i++)
{
paramTypes[i] = params[i].getClass().getName();
}
}
Kernel geronimoKernel =
KernelManager.getInstance().getGeronimoKernel();
Object retVal =
geronimoKernel.invoke(serviceObjName,
operationName,
(Object[])params,
paramTypes);
return retVal;
}
|
An adaptable framework of service-oriented POJOs in Geronimo
The POJO-for-SOA framework referred to in this article uses an instance of the Geronimo kernel to register POJOs as GBeans
whereby interested clients can query and invoke them without the need for additional interfaces or APIs. The framework
resides in the business tier of a multi-tiered enterprise application environment. A service-locator class is
responsible for interacting with the kernel to find and, if needed, register POJOs to be used as services. The
service-locator class then returns the POJOs to a business-delegate component where it invokes them. Figure 1 illustrates the relationships of components in the framework.
Figure 1. The POJO-for-SOA framework
The framework is designed to receive HTTP requests from a client, and then pass the
request(s) through to a dispatcher component that massages and dispatches the request(s) to the business-delegate component.
The business-delegate component then makes use of the service locator to find the service for a particular request. The
business delegate component invokes the service and wraps any return value as a model object. The appropriate view
component processes the model object and returns it as a formatted response to the client. The sequence diagram in Figure 2 illustrates these steps.
Figure 2. Round-trip sequences of a typical HTTP request and service invocation
The class diagram in Figure 3 illustrates the relationships between the framework's classes.
Figure 3. Relationships between framework classes
Deploying and running the framework
The framework resides in the business tier of an enterprise application system. The framework exposes one servlet
that receives HTTP requests and dispatches the content to the framework for processing. The next section explains
the simple deployment process.
Deploy the framework
You can package the classes for the framework and the enterprise application in a .war file and place it within
the geronimo_home/deploy directory. If this directory doesn't already exist, create it.
Geronimo deploys the .war file automatically on startup. Applications placed in the deploy directory are hot-loaded,
enabling Geronimo to reload the application at run time when you make changes. This makes it convenient for
debugging the applications.
Test the framework
You start the Geronimo application server using the startup script (startup.bat or startup.sh) found in the
geronimo_home/bin directory. When you invoke the Geronimo startup script, the Geronimo console window
becomes visible. After you deploy the framework and the application, Geronimo's console window on startup contains lines similar to
those shown in Listing 5, confirming that the Web application has started successfully.
Listing 5. Confirmation the Web application has started successfully
0 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel -
Starting boot
422 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState
- GBeanInstanceState for: :role=Kernel State changed from stopped to
starting
422 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState
- GBeanInstanceState for: :role=Kernel State changed from starting to
running
422 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel -
Booted
640 [main] DEBUG com.jeffhanson.apptier.FrontController - Geronimo
BootTime: Sat May 20 18:51:08 MDT 2006
656 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState
- GBeanInstanceState for: jeffhanson.test:ID=FrontController State
changed from stopped to starting
656 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState
- GBeanInstanceState for: jeffhanson.test:ID=FrontController State
changed from starting to running
|
Now type the following URL into a Web browser window to instigate the setMessage
operation on the HelloWorld service:
http://<host>:<port>/<context>?Action=
HelloWorld&Operation=setMessage&Params=Hello+everybody!
When the framework processes the request, your console output should be similar to that shown in Listing 6.
Listing 6. Output of setMessage operation processing
719 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator -
Adding service [HelloWorld] to kernel...
719 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator -
Loading GBean: jeffhanson.test:Name=HelloWorld,Type=GenericService
734 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState
- GBeanInstanceState for:
jeffhanson.test:Name=HelloWorld,Type=GenericService State changed
from stopped to starting
734 [main] DEBUG org.apache.geronimo.gbean.runtime.GBeanInstanceState
- GBeanInstanceState for:
jeffhanson.test:Name=HelloWorld,Type=GenericService State changed
from starting to running
|
Type the following URL into a Web browser window to instigate the sayHello operation
on the HelloWorld service:
http://<host>:<port>/<context>?
Action=HelloWorld&Operation=sayHello
When the framework processes the request, your console output should be similar to that shown in Listing 7.
Listing 7. Output of sayHello operation processing
750 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator -
serviceObjName: jeffhanson.test:Name=HelloWorld,Type=GenericService
750 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator -
Service [HelloWorld] already in kernel
1156 [main] DEBUG com.jeffhanson.businesstier.ServiceLocator -
serviceObjName: jeffhanson.test:Name=HelloWorld,Type=GenericService
1156 [main] INFO com.jeffhanson.businesstier.services.HelloWorld -
Hello everybody!
|
When the servlet engine shuts down the servlet and calls the destroy method on the
servlet, the servlet shuts down the Geronimo kernel. When the servlet engine shuts down the servlet, your
console output should be similar to that shown in Listing 8.
Listing 8. Output following servlet shutdown
1156 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel -
Starting kernel shutdown
1156 [main] DEBUG org.apache.geronimo.kernel.basic.BasicKernel -
Kernel shutdown complete
|
The HelloWorld class is a simple POJO with a setMessage method,
a getMessage method, and a sayHelloWorld message. After you
register an instance of this class with the Geronimo kernel, you can invoke the instance dynamically and associate
it with other services and components at run time using dependency injection. The code in Listing 9 illustrates the simple HelloWorld POJO class.
Listing 9. A simple HelloWorld service
package com.jeffhanson.businesstier.services;
import org.apache.log4j.Logger;
public class HelloWorld
{
private static Logger log = Logger.getLogger(HelloWorld.class);
private String message = "Hello world";
public void setMessage(String message)
{
if (message == null || message.length() <= 0)
{
throw new RuntimeException("HelloWorld.setMessage "
+ "param is not set");
}
this.message = message;
}
public String getMessage()
{
return message;
}
public void sayHello()
{
log.info(message);
}
}
|
Summary
Designing a nimble and effective SOA that can respond to business domain changes and events in a timely manner is a complex
task. However, an SOA built around a properly designed POJO layer can help simplify this task. The Geronimo platform provides frameworks and tools that you can use to build a flexible, scalable, and maintainable
SOA using POJOs.
Download | Description | Name | Size | Download method |
|---|
| SOA with POJOs framework | GeronimoSOAwithPOJOs.zip | 14KB | HTTP |
|---|
Resources Learn
Get products and technologies
Discuss
About the author  | 
|  | Jeff Hanson has more than 20 years of experience in the software industry, including work as senior engineer for the Windows OpenDoc project and lead architect for the Route 66 framework at Novell. Jeff is currently the chief architect
for eReinsure.com, Inc. and builds Web service frameworks and platforms for Java EE-based reinsurance systems. Jeff is
the author of numerous articles and books, including .NET versus J2EE Web Services: A Comparison of Approaches, Pro JMX: Java Management Extensions, and Web Services Business Strategies and Architectures. |
Rate this page
|  |