9.7 Generating EJB Home and Remote Interfaces

9.7.1 Problem

You need XDoclet to generate the EJB home and remote interfaces each time your bean class changes.

9.7.2 Solution

Mark up your bean implementation class with the necessary XDoclet tags and use XDoclet to generate the home and remote interfaces.

9.7.3 Discussion

Writing EJB home and remote interfaces is a cumbersome task. The remote, home, and bean code must stay in sync or the deployment of the bean fails. Depending on the server, you may or may not receive suitable error messages. Let's look at an example of what needs to be written if XDoclet is not used.

Example 9-3 shows an example of a hand-coded remote interface. When writing remote interfaces, ensure that each method throws java.rmi.RemoteException. This may not seem like a huge task but the first time you forget to add the exception to the throws clause you will wish you never wrote this interface.

Example 9-3. Hand-coded remote interface
package com.oreilly.javaxp.xdoclet.ejbdoclet.ejb;

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface PaymentProcessingBean extends EJBObject {

   public boolean makePayment(String accountNumber, double payment) 
           throws RemoteException;
}

Example 9-4 shows an example of a hand-coded home interface. The home interface provides a view into the container for creating, finding, and removing beans. You must ensure that all "create" methods throw javax.ejb.CreateException, that "finder" methods throw javax.ejb.FinderException, and all methods throw RemoteException. Once again, this may not seem like a daunting taskbut the first time you forget is the last time you will want to write this code.

Example 9-4. Hand-coded home interface
package com.oreilly.javaxp.xdoclet.ejbdoclet.ejb;

import java.rmi.RemoteException;
import javax.ejb.EJBHome;
import javax.ejb.CreateException;

public interface PaymentProcessingBeanHome extends EJBHome {
   
   public PaymentProcessingBean create(  ) 
           throws CreateException, RemoteException;
}

Finally, Example 9-5 shows the bean implementation. The bean implementation, in this example, extends javax.ejb.SessionBean and provides empty implementations of the SessionBean interface methods. Also notice the ejbCreate( ) method. This method is added because the home interface defined a create method called create( ). Failure to add this method causes runtime problems.

Example 9-5. Bean implementation
package com.oreilly.javaxp.xdoclet.ejbdoclet.ejb;

import javax.ejb.SessionBean;

public class PaymentProcessingBean implements SessionBean {
 
    public boolean makePayment(String accountNumber, double payment) {
        // perform logic to look up customer and make payment against their
        // account
        return true;
    }

    /**
     * Not part of the SessionBean interface. This method exists because the
     * home interface defined a method called create(  ).
     */
    public void ejbCreate(  ) {
    }

    public void ejbActivate(  ) throws EJBException, RemoteException {
    }

    public void ejbPassivate(  ) throws EJBException, RemoteException {
    }

    public void ejbRemove(  ) throws EJBException, RemoteException {
    }

    public void setSessionContext(SessionContext sessionContext)
            throws EJBException, RemoteException {
    }
}

The previous example is simple but helps exemplify the cumbersome tasks that take attention away from what really matterswriting the bean! Now, let us turn our attention to automatically generating the home and remote interfaces using XDoclet.

Using XDoclet to generate home and remote interfaces requires marking up the bean implementation with XDoclet tags. Use Ant to execute the XDoclet engine to generate the files. Example 9-6 shows the marked-up bean implementation.

Example 9-6. Marked-up PaymentProcessingBean
/**
 * @ejb.bean
 *     type="Stateless"
 *     name="PaymentProcessingBean"
 *     jndi-name="ejb/PaymentProcessingBean"
 *     view-type="remote"
 * @ejb.transaction
 *     type="Required"
 * @ejb.transaction-type
 *     type="Container"
 *
 * @author Brian M. Coyner 
 */
public abstract class PaymentProcessingBean implements SessionBean {

    /**
     * @ejb.interface-method view-type="remote"
     */
    public boolean makePayment(String accountNumber, double payment) {
        // perform logic to look up customer and make payment against their
        // account
        return true;
    }
}

This is the only source file you have to write. In this example, the @ejb.interface-method is the only tag specified for the makePayment( ) method, which simply tells XDoclet that this method should be included in the remote interface. Another important aspect to this example is that it is declared abstract. Thus, we do not have to directly implement any of the SessionBean methods or add the ejbCreate( ) method, but rather we rely on XDoclet to provide a subclass of the bean to provide the implementations. Example 9-7 shows the generated session bean.[4]

[4] This example has been reformatted to print nicely on the page.

Example 9-7. Generated session bean
/*
 * Generated by XDoclet - Do not edit!
 */
package com.oreilly.javaxp.xdoclet.ejbdoclet.ejb;

/**
 * Session layer for PaymentProcessingBean.
 */
public class PaymentProcessingBeanSession
        extends com.oreilly.javaxp.xdoclet.ejbdoclet.ejb.PaymentProcessingBean
        implements javax.ejb.SessionBean {
    public void ejbActivate(  ) {
    }
    public void ejbPassivate(  ) {
    }

    public void setSessionContext(javax.ejb.SessionContext ctx) {
    }

    public void unsetSessionContext(  ) {
    }

    public void ejbRemove(  ) {
    }

    public void ejbCreate(  ) throws javax.ejb.CreateException {
    }
}

Example 9-8 shows how to set up the Ant buildfile to generate the home and remote interfaces, as well as providing a subclass of our bean containing implementations of the SessionBean interface methods.

Example 9-8. Using Ant to generate EJB files
<target name="generate.ejb" description="Generates EJB specific files.">
  <taskdef name="ejbdoclet" classname="xdoclet.modules.ejb.EjbDocletTask">
    <classpath>
      <pathelement path="${env.JBOSS_DIST}/client/jboss-j2ee.jar"/>
      <pathelement path="${env.XDOCLET_HOME}/lib/xdoclet.jar"/>
      <pathelement path="${env.XDOCLET_HOME}/lib/xjavadoc.jar"/>
      <pathelement path="${env.XDOCLET_HOME}/lib/xdoclet-ejb-module.jar"/>      
      <pathelement location="${dir.lib}/commons-logging-1.0.jar"/>
    </classpath>
  </taskdef>
  
  <ejbdoclet
      ejbspec="2.0"
      destdir="${dir.generated.src}"
      excludedtags="@version,@author,@see"
      force="${force.ejb}">

      <!-- Rename any package called 'ejb' to 'interfaces'. -->
      <packageSubstitution packages="ejb" substituteWith="interfaces"/>

      <fileset dir="${dir.src}">
        <include name="**/ejb/*Bean.java"/>
      </fileset>

      <homeinterface/>
      <remoteinterface/>
      <session/>
      <deploymentdescriptor destdir="${dir.ejb.metainf}" validatexml="true"/>
  </ejbdoclet>
</target>

The destdir attribute specifies the destination directory for all generated files. Next, the fileset specifies which files should be included or excluded from the generation process. This recipe is only interested in looking at Java source files that end with Bean.java.

Consistent naming conventions serve two purposes. First, they make your code more maintainable. Second, consistency facilitates automation. Specifying that all bean implementation classes must end in Bean.java allows the Ant buildfile to remain simple and easy to understand. Without strict naming conventions, this task and others like it would be nearly impossible.

Here are three subtasks responsible for generating a single file for each bean: the homeinterface subtask is responsible for generating the EJB home interface; the remoteinterface subtask is responsible for generating the EJB remote interface.; and the session subtask is responsible for extending each SessionBean implementation class (our PaymentProcessBean class) and providing default implementations of the SessionBean interface methods and the ejbCreate( ) method. Each EJB subtask uses a pre-existing template file supplied in xdoclet-ejb-module.jar; thus, no extra work is needed on our part.

Once this task is integrated into the Ant build process, the days of creating and maintaining the mundane and frustrating aspects of EJB code are long gone. Also gone are the days of using point-and-click wizards that require human intervention. Ant and XDoclet completely automate EJB code generation. Once your build process is set up you can forget about EJB deployment and focus on solving business problems.

9.7.4 See Also

Recipe 9.5 shows how to use XDoclet to generate the EJB deployment descriptor.