by Dr. Michael Juntao Yuan

On the road to simplicity

news
Feb 21, 200519 mins

JBoss 4.0 simplifies middleware development

In September 2004, the JBoss Application Server (AS) 4.0 became J2EE 1.4 certified. For JBoss’s core developers and early adopters, the coolest thing about JBoss AS 4.0 is not the J2EE certification, but the new technologies that currently reach beyond the scope of J2EE and aim to greatly simplify Java middleware development. The idea is to replace existing Enterprise JavaBeans (EJB) with simpler and more manageable plain old Java objects (POJOs). Simplified applications lead to higher developer productivity, better application performance, and less bugs. Simplicity (a.k.a. lightweight development) is the “next big thing” in the server-side Java community, and JBoss AS 4.0 is the first major J2EE server to take solid steps in that direction.

In this article, I use three example applications to show you the simplicity of the POJO middleware frameworks in JBoss AS 4.0 and how they relate to the current and future J2EE specifications. If you are a JBoss user or a general J2EE developer, this article teaches you portable skills that you can use in both today’s JBoss AS 4.0 server and the future JBoss 5.0 or J2EE 1.5 servers.

Let’s start with the problems inherent in the current EJB 2.1 middleware frameworks to show the need for a simpler POJO-based framework.

Open source and J2EE certification
The official J2EE certification for JBoss is a milestone for both the Java and open source communities. Not too long ago, it was believed that without charity from Sun, no open source J2EE project would pass certification due to the high cost and extensive quality assurance efforts required. JBoss achieved J2EE certification by itself and hence proved the validity of the open source development model in delivering enterprise-ready Java solutions.

What’s wrong with EJB 2.1?

Since its inception, J2EE has achieved great success in the marketplace for developing scalable and distributed server-side applications. However, EJB, a core component for middleware in the J2EE stack, has gained the reputation of being too complex and difficult to use, especially for small to midsize business applications. The overhead of EJB infrastructure code and deployment descriptors drains both server resources and, more importantly, developer productivity. Developers often end up writing and maintaining more infrastructure code than business logic code.

To illustrate the above point and provide a comparison for alternative simpler solutions enabled by JBoss AS 4.0, let’s look at an EJB 2.1-based example application. The Mortgage Calculator Web application calculates the monthly mortgage payment for a loan and stores the results in a relational database. After each calculation, the application searches the database for past results that have lower monthly payments. Those results are displayed at the bottom of the page. Figure 1 shows how the application works. When you first access the application, you are prompted to enter a username and password. Use the username and password combo “user1″/”pass1” to log in. If you want to see the error message for unauthorized users, try the “user3/pass3” combo.

The application’s source code is available from the ejb2 folder in the sample source code bundle. Just execute the command build.bat (for Windows) or build.sh (for Linux, Unix, or Mac OS X) in the ejb2 directory to build the application. Copy the resultant ejb2/build/jar/MortgageCalculatorEJB2.ear file to your JBoss server’s server/default/deploy/ directory to deploy it. The URL to the application is http://localhost:8080/MortgageCalculatorEJB2/servlet/Calculator.

Why use EJB?
Why use EJB for such a simple application? Well, the EJB container offers some valuable services that could immediately add enterprise features to our little application without requiring us to write code. For instance, the EJB container checks the user credentials and monitors database transactions for all EJB method invocations according to the configuration files. It also manages the database table and database connections without requiring us to write any SQL or JDBC (Java Database Connectivity) code.

Under the hood, the application has a servlet that takes in user input and generates the HTML page. The servlet delegates the payment calculation and database-related work to an EJB module.

This application has two EJB components. The Calculator bean is a stateless session bean. It contains transactional methods to calculate the payment, save results to the database, and search the database. Those methods are exposed to the servlet. The Calculator bean uses the History entity bean to access the database. In the EJB configuration files, we define how the History entity bean’s data fields are mapped to database columns and how to search the database for a list of History objects. Since the History bean is an EJB component, it cannot be used outside the EJB container. So we create a HistoryList value object to hold the result of any search operation, which is passed back to the servlet.

Figure 2 illustrates the EJB module’s structure. It shows all the required EJB component interfaces and key elements of the deployment descriptors.

Figure 2. Key components in the EJB module. Click on thumbnail to view full-sized image.

As you can see, the structure shown in Figure 2 is complex, with multiple tightly coupled Java interfaces, classes, and XML artifacts. Wouldn’t it be nice if a framework allowed developers to focus on what they do best—writing Java code—instead of distracting them with a flurry of component interfaces and XML artifacts? Well, the good news is that the POJO middleware framework in JBoss AS 4.0 enables developers to take advantage of EJB container services without all the EJB 2.1 baggage.

Innovate for simplicity

Learning from the perils of EJB, we can see that a successful alternative framework should have the following key characteristics:

  • The framework should not impose an arbitrary component model to developers, as such models break the object-oriented design structure. In other words, the framework should support POJOs that developers can extend and reuse inside and outside the application container.
  • The framework should eliminate the need to manually write verbose and often excessive EJB deployment descriptors. A POJO should be able to simply declare what container services it needs.
  • The framework should support local access to the POJOs by reference. Java object serialization is slow and should be avoided when possible, especially for most small to midsized applications.

Rather than waiting for the Java Community Process (JCP) to invent and standardize a POJO-based lightweight middleware framework, the Java open source community has experimented with numerous different approaches over the past several years. Examples of such open source projects include XDoclet, Hibernate, Spring, and various aspect-oriented programming (AOP) projects. The new POJO-based middleware framework in JBoss AS 4.0 leverages those past research and development efforts.

Two JBoss-sponsored open source projects play key roles in the POJO middleware framework:

  • The JBoss AOP project supports service delivery to POJOs via Java annotations
  • The Hibernate project is the de facto standard framework for object-relational mapping and POJO persistence

Both projects integrate and leverage the proven and J2EE-certified container services in JBoss AS to support enterprise-class applications. Now let’s refactor the Mortgage Calculator EJB components to POJOs and see how this powerful new development model works.

The power of enterprise POJO

The POJO Mortgage Calculator application is also included in the source code bundle. As I will soon explain, the application source code and build scripts differ slightly for J2SE 1.4 and J2SE 5.0 environments. I prepared both versions in the pojo-jdk14 and pojo-jdk50 directories, respectively. The building and deployment instructions are the same as the ejb2 example application. Once deployed, the URL to the POJO sample application is http://localhost:8080/MortgageCalculatorPOJO/servlet/Calculator.

As I discussed, our goal is to use POJO to replace the session and entity beans. Meanwhile, the framework should be able to preserve a key benefit of EJB servers—declaratively apply container services to applications. This way, when we need to change the services later, we need only modify the declaration and not refactor large amounts of Java code. But how can a POJO tell the JBoss container what services it needs without using deployment descriptors? Well, that is where AOP and Java annotation come into play.

Annotation to the rescue

Annotation was introduced as part of the official Java language syntax in J2SE 5.0. JBoss AS 4.0 defines a set of annotation tags as APIs for POJOs to access J2EE container services. You can annotate any POJO or POJO method with those tags and declare how the container services should be applied to them. Under the hood, JBoss AS uses the JBoss AOP framework to dynamically alter the behavior of the annotated objects and methods. You can find more information about JBoss AOP and how it works with annotations from the JBoss AOP user’s guide and reference documentation (see Resources).

The following listing shows the skeleton of the Calculator POJO class, which replaces the Calculator session bean:

 

@SecurityDomain ("other") public class Calculator {

@Unchecked public Calculator () { }

@Permissions ({"AuthorizedUser"}) @Tx (TxType.REQUIRED) public double getPayment (int principal, double rate, int term) throws Exception { // Calculate and save to database. // Code omitted for clarity. }

@Permissions ({"AuthorizedUser"}) @Tx (TxType.REQUIRED) public List getHistory (double payment) throws Exception {

// Search the database. // Code omitted for clarity. } }

The @SecurityDomain annotation declares that the POJO’s security domain is other, which tells JBoss to find the password and user role lists in the classpath’s users.properties and roles.properties files. The @Permissions annotation specifies that only the user with username AuthorizedUser is permitted to access the getPayment() and getHistory() methods. JBoss AS performs the permission check at runtime. The @Tx annotation starts the JBoss transaction manager for the getPayment() and getHistory() methods to ensure any change they make to the database is committed only when the complete method successfully executes and returns.

But what about J2EE 1.4 users? Can they still take advantage of the simple annotated POJOs as EJB alternatives? The answer is a resounding “Yes!” You can embed annotations as Javadoc-style comments in J2EE 1.4 source code. The JBoss AOP framework provides an annotation compiler that post-processes those Java comments and adds annotations into the bytecode. This compiler’s functionality resembles that of XDoclet. The following listing shows the J2EE 1.4 version of the Calculator POJO class in the pojo-jdk14 directory:

 

/**

* @@org.jboss.aspects.security.SecurityDomain ("other") */ public class Calculator {

/** * @@org.jboss.aspects.security.Unchecked */ public Calculator () { }

/** * @@org.jboss.aspects.security.Permissions ({"AuthorizedUser"}) * @@org.jboss.aspects.tx.Tx (org.jboss.aspects.tx.TxType.REQUIRED) */ public double getPayment (int principal, double rate, int term) throws Exception { // Calculate and save to database. // Code omitted for clarity. }

/** * @@org.jboss.aspects.security.Permissions ({"AuthorizedUser"}) * @@org.jboss.aspects.tx.Tx (org.jboss.aspects.tx.TxType.REQUIRED) */ public List getHistory (double payment) throws Exception {

// Search the database. // Code omitted for clarity. } }

To use the JBoss AOP annotation compiler, you just need to add one more task in the Ant build script. The following listing shows the relevant parts of the pojo-jdk14/build.xml script:

 

<target name="prepare"> ... ...

<taskdef name="annotationc" classname="org.jboss.aop.ant.AnnotationC" classpathref="build.classpath" /> </target> ... ... <target name="annotate" depends="compile"> <annotationc compilerclasspathref="build.classpath" classpath="${build.dir}/classes" bytecode="true"> <src path="${src.dir}"/> </annotationc> </target>

With Java annotations, we replace the session bean and its related components with a simple POJO. But what about the entity bean?

Lightweight POJO persistence

The primary function of the container-managed persistence (CMP) entity bean is to model the application data and persist it transparently to the backend database. In the JBoss AS 4.0 lightweight middleware framework, Hibernate completes the data modeling and persistence.

The History class is a simple POJO that models data in a calculation transaction. It only contains the data fields and JavaBean-style accessor methods:

 

public class History {

private int id; private int principal; private double rate; private int term; private double payment;

public History () { }

public History (int principal, double rate, int term, double payment) { this.principal = principal; this.rate = rate; this.term = term; this.payment = payment; }

public int getId () { return id; }

public void setId (int id) { this.id = id; }

public int getPrincipal () { return principal; }

public void setPrincipal (int principal) { this.principal = principal; }

public double getRate () { return rate; }

public void setRate (double rate) { this.rate = rate; }

public int getTerm () { return term; }

public void setTerm (int term) { this.term = term; }

public double getPayment () { return payment; }

public void setPayment (double payment) { this.payment = payment; } }

Now we need a Hibernate mapping file, History.hbm.xml, to map the History class to a database table, and the data fields to columns in the database table:

 

<hibernate-mapping> <class name="com.jboss.MortgageCalculator.pojo.History" table="history"> <id name="id" type="int" column="id"> <generator class="increment" /> </id>

<property name="principal" type="int" column="principal"/>

<property name="rate" type="double" column="rate"/>

<property name="term" type="int" column="term"/>

<property name="payment" type="double" column="payment"/> </class> </hibernate-mapping>

But we have not yet specified which backend database to use and how the database integrates with the JBoss server container. We specify those settings in the hibernate-service.xml file:

 <server>
    <mbean code="org.jboss.hibernate.jmx.Hibernate" 
           name="jboss.har:service=Hibernate">
        <attribute name="DatasourceName">
          java:/DefaultDS
        </attribute>
        <attribute name="Dialect">
          net.sf.hibernate.dialect.HSQLDialect
        </attribute>
        <attribute name="SessionFactoryName">
          java:/hibernate/SessionFactory
        </attribute>
        <attribute name="CacheProviderClass">
          net.sf.hibernate.cache.HashtableCacheProvider
        </attribute>
        <attribute name="Hbm2ddlAuto">
          create-drop
        </attribute>
    </mbean>
</server>

To deploy the Hibernate module in JBoss AS, we must package it in a jar file with the filename suffix .har. The hibernate-service.xml file must be placed in the META-INF directory inside the .har archive file. The following listing shows the relevant task in the Ant build script to package the .har file:

 

<target name="package-har" depends="annotate"> <jar jarfile="${build.dir}/jar/calculator-pojo.har"> <metainf dir="dd/har" includes="**/*.xml" /> <fileset dir="${build.dir}/classes"> <include name="com/jboss/MortgageCalculator/pojo/**"/> <include name="*.properties"/>

</fileset> </jar> </target>

The JBoss-Hibernate integration magic

The Hibernate module models the business data and transparently maps Java data objects to and from relational database tables. But how do we use the Hibernate POJO in the Calculator object?

In a regular Hibernate application, you must obtain a SessionFactory, create a Session, start a Transaction, and then do your work inside the transaction. After the work is done, you must commit (or rollback) the transaction and close the session.

Inside the JBoss AS, however, you need none of the framework code. JBoss transparently handles all the Hibernate services.

For the Mortgage Calculator POJO application, we only need to obtain a Hibernate Session object via the HibernateContext factory class using the JNDI (Java Naming and Directory Interface) name configured in the hibernate-service.xml file in the Hibernate .har module. The Hibernate transaction is automatically tied to the transaction service in the container, which is started via the @Tx annotation tag, as we have seen. You don’t need to close the transaction or the session. JBoss figures out whether to commit the changes and clean up the session.

The following listing shows the Hibernate-related code in the Calculator POJO class. The hsess.save() statement saves a Hibernate POJO to the database and the hsess.find() statement queries the database to obtain a list of History POJOs.

 

public class Calculator {

/** * @@org.jboss.aspects.security.Unchecked */ public Calculator () { }

/** * @@org.jboss.aspects.security.Permissions ({"AuthorizedUser"}) * @@org.jboss.aspects.tx.Tx (org.jboss.aspects.tx.TxType.REQUIRED) */ public double getPayment (int principal, double rate, int term) throws Exception { rate = rate / 100.; rate = rate / 12.; double tmp = Math.pow(1.+rate, term); tmp = (principal * tmp * rate) / (tmp - 1.);

// Save this calculation into the database. // Notice that it is automatically associated // with the AOP transaction. We do not even need // to close the session! try { History history = new History (principal, rate * 1200., term, tmp); Session hsess = HibernateContext.getSession( "java:/hibernate/SessionFactory"); hsess.save (history);

} catch (Exception e) { e.printStackTrace (); // The new exception triggers the tx rollback. throw new Exception ("Saving failed!"); }

return tmp; }

/** * @@org.jboss.aspects.security.Permissions ({"AuthorizedUser"}) * @@org.jboss.aspects.tx.Tx (org.jboss.aspects.tx.TxType.REQUIRED) */ public List getHistory (double payment) throws Exception {

List result = null; try { Session hsess = HibernateContext.getSession( "java:/hibernate/SessionFactory"); result = hsess.find ( "from History as h where h.payment < ?", new Double(payment), Hibernate.DOUBLE);

} catch (Exception e) { e.printStackTrace (); // The new exception triggers the tx rollback. throw new Exception ("Finding failed!"); } return result; } }

In summary, Figure 3 illustrates the structure of the Mortgage Calculator POJO application. Contrast it with Figure 2 to see how much simpler the POJO model is when compared with the EJB 2.1 model, even for a small application like ours.

Looking ahead to EJB 3.0

The idea of POJO-based middleware started from grassroots open source projects. Now it is making its way into the J2EE standardization process. The upcoming EJB 3.0 specification developed by the JSR (Java Specification Request) 220 expert group will use POJOs as EJB components and eliminate the need for deployment descriptors in many cases. Being a major open source player, a promoter of the simplified POJO model, and a JCP executive committee member, JBoss is one of the driving forces behind EJB 3.0.

The EJB 3.0 programming model and API resemble JBoss AS 4.0’s POJO model. Thus, migrating JBoss AS 4.0 POJO applications and related developer skills to future EJB 3.0 servers will prove easy.

If you want to play with tomorrow’s EJB 3.0 technology today, you can download and install the EJB 3.0 Preview module for JBoss AS 4.0 from JBoss. The JBoss EJB 3.0 Preview implementation is based on JBoss AOP and Hibernate. The preview software is based on a draft version of EJB 3.0 and is subject to change in the future.

Double check your version of JBoss
Make sure you have the correct version of JBoss AS installed for the EJB 3.0 module. For instance, the JBoss EJB 3.0 Preview 2 module only works with the all configuration in JBoss AS 4.0.1 RC1. This is subject to change as new EJB 3.0 Preview versions are released. You can find updated information in the EJB 3.0 module’s installation documentation.

To show how EJB 3.0 works, I re-implemented the Mortgage Calculator application in the EJB 3.0 API. You can build it from the source code’s ejb3 directory. Once deployed, the URL to the EJB 3.0 application is http://localhost:8080/MortgageCalculatorEJB3/servlet/Calculator. J2SE 5.0 is required for building the EJB 3.0 application.

As shown in the code below, the Calculator interface declares the POJO-based session bean. The @Local annotation tag specifies that the EJB 3.0 session bean is deployed in the same JVM as its client (i.e., the servlet module). Hence, the servlet accesses the session bean via its Java reference and eliminates the need for the slow object serialization operation. If the session bean runs on a separate server, you can tag the interface with the @Remote annotation.

 

@Local public interface Calculator {

public double getPayment (int principal, double rate, int term);

// Get previous queries that has payment lower than // the current one public List getHistory (double payment);

}

The CalculatorBean class is the session bean implementation. We use the @Stateless annotation to declare it as a stateless session bean:

 

@Stateless @SecurityDomain("other")

public class CalculatorBean implements Calculator {

@Inject private EntityManager manager;

public CalculatorBean () { }

@MethodPermissions({"AuthorizedUser"}) @Tx(TxType.REQUIRESNEW) public double getPayment (int principal, double rate, int term) { rate = rate / 100.; rate = rate / 12.; double tmp = Math.pow(1.+rate, term); tmp = (principal * tmp * rate) / (tmp - 1.);

HistoryBean history = new HistoryBean (principal, rate * 1200., term, tmp); manager.create (history);

return tmp; }

@MethodPermissions({"AuthorizedUser"}) @Tx(TxType.REQUIRESNEW) public List getHistory (double payment) {

return manager.createQuery( "from HistoryBean h where h.payment < :payment") .setParameter("payment", new Double(payment)) .listResults(); } }

The HistoryBean class is the data model to be persisted to the database. Check out how we use annotation to eliminate the need for the object-database table mapping file. In addition, the EntityManager replaces the HibernateContext object from the POJO example. At runtime, an annotation injects the EntityManager object into the HistoryBean object, so we do not need to mess with the JNDI settings and names:

 

@Entity @Table(name = "history") public class HistoryBean {

private int id; private int principal; private double rate; private int term; private double payment;

public HistoryBean () { }

public HistoryBean (int principal, double rate, int term, double payment) { this.principal = principal; this.rate = rate; this.term = term; this.payment = payment; }

@Id(generate = GeneratorType.AUTO) public int getId () { return id; }

public void setId (int id) { this.id = id; }

public int getPrincipal () { return principal; }

public void setPrincipal (int principal) { this.principal = principal; }

public double getRate () { return rate; }

public void setRate (double rate) { this.rate = rate; }

public int getTerm () { return term; }

public void setTerm (int term) { this.term = term; }

public double getPayment () { return payment; }

public void setPayment (double payment) { this.payment = payment; } }

Overall, applications written in EJB 3.0 resemble POJO applications in JBoss AS 4.0, except EJB 3.0’s entity bean model is even simpler than the plain Hibernate-based POJO model. Figure 4 shows the structure of the EJB 3.0-based Mortgage Calculator application.

Figure 4. Key components in the EJB 3.0 module. Click on thumbnail to view full-sized image.

Getting involved

In the server-side Java community, the development of POJO-based middleware is still pretty much a work in progress. As an open source project, JBoss makes all of its source code, including the code branch currently under development, available to the general public through its public CVS server. Everyone is welcome to contribute. You can help shape the next generation Java middleware framework through the open source collaborative development process. Are you ready for the ride?

Dr. Michael Yuan is a consultant with JBoss, specializing in end-to-end enterprise solutions. He is the author of two books, Enterprise J2ME and Nokia Series: Developing Scalable Series 40 Applications. Michael has a Ph.D. from the University of Texas at Austin.