Simplify enterprise Java development with EJB 3.0, Part 1

Use annotations to develop POJO services

Java Enterprise Edition, or Java EE (previously called J2EE), is a powerful but notoriously complex platform for developing server-side applications. Since its early days, complexity has long been cited as one of the major factors holding back Java EE's adoption. In a previous JavaWorld article "On the Road to Simplicity," I discussed some of those issues that complicate Java EE application development, specifically those related to the current Enterprise JavaBeans (EJB) 2.1 specification.

Over the past three years, the Java open source community, the Java Community Process (JCP), and major Java EE vendors have endeavored to make Java EE simpler. For example, new design paradigms, such as POJO (plain-old Java object) services, service interceptors, and dependency injection, are practiced in real-world applications to simplify Java EE development. And new tools and frameworks, such as Hibernate, AOP (aspect-oriented programming), Struts, XDoclet, and Spring, have been widely adopted for the same purpose.

Simplification is not feature reduction
Simplifying a programming model does not reduce its functionality. Simplification merely hides the complex logic behind framework code or reusable components. In essence, it shifts complexity from one part of the system that needs explicit management by application developers to another part invisible to most developers.

Those patterns and tools have made life easier for beginning developers while improving the productivity for experienced Java developers and are currently being incorporated into the next generation of Java EE standards (i.e., EJB 3.0) by the JCP. A recent study by Java developer Raghu Kodali has shown that porting Sun's Java EE tutorial application RosterApp from EJB 2.1 to EJB 3.0 resulted in more than a 50-percent reduction in code.

Java annotations are the key behind EJB 3.0, which ties POJO services, POJO persistence, and dependency injection altogether into a complete enterprise middleware solution. In this article, I use a sample application, the JBoss EJB 3.0 TrailBlazer, to illustrate how to develop lightweight EJB 3.0 POJO applications with annotations. The TrailBlazer application implements an investment calculator multiple times using different tools and APIs available in EJB 3.0. The sample application runs out of the box in JBoss Application Server 4.0.3 and is fully compliant to the latest EJB 3.0 specification (public review) at the time of this writing.

Let's begin by examining the benefits of an annotation-driven programming model.

EJB 3.0's annotation-driven programming model

From the developer's point of view, EJB 3.0 extensively uses Java annotations. Annotations have two key advantages: they replace excessive XML-based configuration files and eliminate the need for the rigid component models.

Annotations versus XML

XML-based deployment descriptors and annotations can both be used to configure service-related attributes in Java EE applications. The two differ in that XML files are processed separately from the code, often at runtime, while annotations are compiled with the code and checked by the compiler. That has some important implications for developers, as I list below:

  • Verbosity: XML configuration files are known for their verbosity. To configure the code, XML files must duplicate a lot of information, such as class names and method names, from the code. Java annotations, on the other hand, are part of the code and specify configuration information without external reference to the code.
  • Robustness: The duplicated code information in the XML configuration files introduces multiple points of potential failures. For example, if you misspell a method name in the XML file, the application would fail at runtime. In other words, XML configuration files are less robust than annotations, which are checked by the compiler and processed with the rest of the code.
  • Flexibility: Since the XML files are processed separately from the code, the XML-based configuration data is not "hard-coded" and can be changed later. The deploy time flexibility is a great feature for system administrators.

Annotations are easy to use and prove sufficient for most application needs. XML files are more complex and can be used to address more advanced issues. EJB 3.0 allows you to configure most application settings via annotations. EJB 3.0 also supports XML files for overriding default annotation values and configuring external resources such as database connections.

In addition to replacing and simplifying XML descriptors, annotations also allow us to do away with the rigid component model that plagued EJB 1.x and 2.x.

POJO versus rigid components

EJB components are container-managed objects. The container manipulates the behavior and internal state of the bean instances at runtime. For this behavior to occur, the EJB 2.1 specification defines a rigid component model the beans must conform to. Each EJB class must inherit from a certain abstract class that provides callback hooks into the container. Since Java supports only single inheritance, the rigid component model limits a developer's ability to build complex object structure using EJB components. That is especially a problem for mapping complex application data in entity beans, as you will see in Part 2 of this series.

In EJB 3.0, all container services can be configured and delivered to any POJO in the application via annotations. In most cases, special component classes are not needed.

Let's check out how annotations are used in EJB 3.0 POJO applications via the JBoss EJB 3.0 TrailBlazer.

Develop loosely coupled service objects

One of the most important benefits of enterprise middleware, such as Java EE, is that it allows developers to build applications with loosely coupled business components. The components are only coupled via their published business interfaces. Hence, the component implementation classes can be changed without affecting the rest of the application. That makes the application more robust, easier to test, and more portable. EJB 3.0 makes it easy to build loosely coupled business components in POJO.

Session beans

In EJB 3.0 applications, loosely coupled service components are typically implemented as session beans. A session bean must have an interface (i.e., the business interface), through which other application components can access its services. The code that follows provides the business interface for our example investment calculator service. It has only one method to calculate total investment return given the start and end age of the investor, the fund growth rate, and the monthly saving amount.

 

public interface Calculator {

public double calculate (int start, int end, double growthrate, double saving);

}

The session bean class simply implements the business interface. You must tell the EJB 3.0 container that this POJO class is a session bean by annotating it with the Stateless or Stateful annotations. A stateful session bean maintains the client state across several different service requests. On the contrary, stateless session bean requests are served by a random bean instance each time. Their behaviors match those of the old EJB 2.1 stateful and stateless session beans. The EJB 3.0 container figures out when to instantiate the bean object and makes it available via the business interface. Below is the code for the session bean implementation class:

 

@Stateless public class CalculatorBean implements Calculator {

public double calculate (int start, int end, double growthrate, double saving) { double tmp = Math.pow(1. + growthrate / 12., 12. * (end - start) + 1); return saving * 12. * (tmp - 1) / growthrate; }

}

You can also specify multiple interfaces for a session bean—one for local clients and one for remote clients. Just use the @Local and @Remote annotations to differentiate the interfaces. The following snippet shows that the CalculatorBean session bean implements both a local and a remote interface. If you do not have the @Local and @Remote annotations, the session bean interface defaults to a local interface.

 

@Stateless @Local ({Calculator.class}) @Remote ({RemoteCalculator.class}) public class CalculatorBean implements Calculator, RemoteCalculator {

public double calculate (int start, int end, double growthrate, double saving) { double tmp = Math.pow(1. + growthrate / 12., 12. * (end - start) + 1); return saving * 12. * (tmp - 1) / growthrate; }

public String getServerInfo () { return "This is the JBoss EJB 3.0 TrailBlazer"; } }

The session bean client obtains a stub object of the bean via JNDI (Java Naming and Directory Interface). Provided by the container, the stub object implements the session bean's business interface. All calls made to the stub object are routed to the container and invoked against the managed bean instances. For stateless session beans, you can obtain a new stub every time you make a call. For stateful session beans, you must cache the stub on the client side so the container knows to provide you with the same bean instance for each subsequent call. The following snippet shows how to make a call to the session bean. Later in the article, you will learn a simpler way to obtain a bean stub object.

 

InitialContext ctx = new InitialContext(); cal = (Calculator) ctx.lookup(Calculator.class.getName());

double res = cal.calculate(start, end, growthrate, saving);

Session bean lifecycle management

To achieve loose coupling, the application defers the creation, pooling, and destruction of the session bean instance to the EJB 3.0 container (i.e., the Inversion of Control design pattern). And the application works only with the business interfaces.

But what if the application needs finer control over the session object? For instance, the application might need to perform database initialization when the container creates the session bean, or close external connections when the bean is destroyed. You can do these by implementing lifecycle callback methods in the bean class. These methods are called by the container at various stages of the bean's lifecycle (e.g., bean creation and destruction). In EJB 3.0, you can specify any bean method as a callback by annotating it with the following annotations. Unlike EJB 2.1, where all callback methods must be implemented even if they are empty, EJB 3.0 beans can have any number of callback methods with any method name.

  • @PostConstruct: The container immediately calls the annotated method after a bean instance is instantiated. This annotation applies to both stateless and stateful session beans.
  • @PreDestroy: The annotated method is called before the container destroys an unused or expired bean instance from its object pool. This annotation is applicable to both stateless and stateful session beans.
  • @PrePassivate: If a stateful session bean instance is idle for too long, the container might passivate it and store its state to a cache. The method tagged by this annotation is called before the container passivates the bean instance. This annotation applies to stateful session beans.
  • @PostActivate: When the client uses the passivated stateful session bean again, a new instance is created and the bean state is restored. The method tagged by this annotation is called when the activated bean instance is ready. This annotation is only applicable to stateful session beans.
  • @Init: This annotation designates initialization methods for a stateful session bean. It differs from the @PostConstruct annotation in that multiple methods can be tagged with @Init in a stateful session bean. However, each bean instance can have only one @Init method invoked. The EJB 3.0 container determines which @Init method to invoke depending on how the bean is created (see the EJB 3.0 specification for details). The @PostConstruct method is called after the @Init method.

Another useful annotation for a lifecycle method, especially for stateful session beans, is @Remove. When the application calls the @Remove tagged method through the stub, the container knows to remove the bean instance from the object pool after the method executes. Here is an example of those lifecycle method annotations in CalculatorBean:

 

@Stateful public class CalculatorBean implements Calculator, Serializable {

// ... ... @PostConstruct public void initialize () { // Initializes the history records and load // necessary data from database etc. } @PreDestroy public void exit () { // Save history records into database if necessary. } @Remove public void stopSession () { // Call to this method signals the container // to remove this bean instance and terminates // the session. The method body can be empty. } // ... ... }

Message-driven beans

The session bean services are provided over synchronous method invocations. Another important type of loosely coupled service is an asynchronous service triggered by incoming messages (e.g., email or Java Message Service messages). The EJB 3.0 message-driven beans (MDBs) are components designed to handle message-based service requests.

1 2 Page 1
Page 1 of 2