EJB Session Bean

Objectives

We are going to develop an EJB Stateful Session Bean with a standalone client. (In the next lab, we will use a HTML client).
This EJB is a cart bean where can be saved items such as books. It had methods to add an item, remove an item, and retrieve a list of items.

Stateful Session Bean Life-cycle

Accelerate development with XDoclet

As seen in the theoratical part, developing an EJB invovles at least the development of one class, two interfaces (may be four) and two XML deployment descriptors!
It would be nice to find a way to generate most of those files.
We will use the concept of "Attribute Oriented-Programming" with tools such as EJBGen or XDoclet.

The first one, EJBGen only targets BEA WLS.
Here is an article (online) on how to use EJBGen.
The second one, XDoclet, supports many different application servers, jBoss, JOnAS, Pramati, OC4J, .. but also some frameworks such as JDO, JMX, WebWork or Struts.
Here is the documentation

We will use Xdoclet from now on for all our EJBs development.

1. Client Java -> EJB Stateful Session Bean

Folders structure


Using XDoclet, the following files are generated: ejb-jar.xml and jboss.xml in ejb-module/META-INF, all the Java files in gen-src.

EJB CartBean

We are going to develop an EJB Stateful Session Bean (CartBean). "Stateful" means that the container maintains de conversational state between the client and itself.

Here is code source.

A few XDoclet tags


/**
 * CartBean
 * 
 * @ejb.bean
 *   name = "Cart"
 *   type = "Stateful"
 *   jndi-name = "org.jyperion.sample.sessionbean.CartBean"
 *   view-type = "remote"
 *
 * @author janaudy
 * @date 19, April 2003
 */ 
public class CartBean implements SessionBean { ...
The @ejb.bean tag specifies that this class is an EJB, of name (name) "Cart", of type (type) "Stateful", with the JNDI name (jndi-name) "org.jyperion.sample.sessionbean.CartBean" (will be use for lookups from the client side), and finally, the bean's visibility (view-type) of type "remote".
Simple?

/**
 * @ejb.interface-method
 * 
 * @param nom
 */ 
public void addBook(String nom) { ...
@ejb.interface-method specifies that this method is accessible from the remote interface.

Uainf those tags, XDoclet will generate:

ejb-jar.xml

The generated file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar >

   <description><![CDATA[No Description.]]></description>
   <display-name>Generated by XDoclet</display-name>

   <enterprise-beans>

      <!-- Session Beans -->
      <session >
         <description><![CDATA[CartBean]]></description>

         <ejb-name>Cart</ejb-name>

         <home>org.jyperion.j2ee.sample.sfsb.CartHome</home>
         <remote>org.jyperion.j2ee.sample.sfsb.Cart</remote>
         <ejb-class>org.jyperion.j2ee.sample.sfsb.CartBean</ejb-class>
         <session-type>Stateful</session-type>
         <transaction-type>Container</transaction-type>

      </session>

     <!--
       To add session beans that you have deployment descriptor info for, add
       a file to your XDoclet merge directory called session-beans.xml that contains
       the <session></session> markup for those beans.
     -->

      <!-- Entity Beans -->
     <!--
       To add entity beans that you have deployment descriptor info for, add
       a file to your XDoclet merge directory called entity-beans.xml that contains
       the <entity></entity> markup for those beans.
     -->

      <!-- Message Driven Beans -->
     <!--
       To add message driven beans that you have deployment descriptor info for, add
       a file to your XDoclet merge directory called message-driven-beans.xml that contains
       the <message-driven></message-driven> markup for those beans.
     -->

   </enterprise-beans>

   <!-- Relationships -->

   <!-- Assembly Descriptor -->
   <assembly-descriptor >
     <!--
       To add additional assembly descriptor info here, add a file to your
       XDoclet merge directory called assembly-descriptor.xml that contains
       the <assembly-descriptor></assembly-descriptor> markup.
     -->

   <!-- finder permissions -->

   <!-- transactions -->

   <!-- finder transactions -->
   </assembly-descriptor>

</ejb-jar>

jboss.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 3.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_3_0.dtd">

<jboss>

   <unauthenticated-principal>nobody</unauthenticated-principal>

   <enterprise-beans>

     <!--
       To add beans that you have deployment descriptor info for, add
       a file to your XDoclet merge directory called jboss-beans.xml that contains
       the <session></session>, <entity></entity> and <message-driven></message-driven>
       markup for those beans.
     -->

      <session>
         <ejb-name>Cart</ejb-name>
         <jndi-name>org.jyperion.sample.sessionbean.CartBean</jndi-name>

      </session>

   </enterprise-beans>

   <resource-managers>
   </resource-managers>

</jboss>
The 2 XML files are in the folder META-INF of the EJB module.
Then, we will generate a JAR file (build phase)..

Client

Here is the JbossCartClient code.
The client module also has a META-INF folder that contains another XML file that defines the beans accessed by this client.

Furthermore, the client must defines a jndi.properties file that specifies the factory, IP address and port of the application server.

application-client.xml


<?xml version="1.0"?>
<!DOCTYPE application-client PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application Client 1.2//EN" "http://java.sun.com/j2ee/dtds/application-client_1_2.dtd">

<application-client>

  <display-name>Cart</display-name>
  
  <ejb-ref>
    <ejb-ref-name>Cart</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <home>org.jyperion.j2ee.sample.sfsb.CartHome</home>
    <remote>org.jyperion.j2ee.sample.sfsb.Cart</remote>
  </ejb-ref>

</application-client>

jndi.properties


java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost

EAR module: Enterprise ARchive

We will use the EAR format from now on.

Why an EAR?
Using this format, we can bundle all the necessary tiers (presentation (WAR), logic (JAR), data (JAR), ...) within one logical file.

The EAR META-INF folder contains another XML descriptors that defines the modules to deploy:

<?xml version="1.0"?>
<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.2//EN" "http://java.sun.com/j2ee/dtds/application_1_2.dtd">
<application>

  <display-name>Jyperion Stateful Session Bean sample</display-name>

  <description>
  </description>

  <module>
    <ejb>Cart.jar</ejb>
  </module>
</application>
We will use ANT to build this file as well.

build

build.properties


####################
# J2EE Libs Folder #
####################
jboss.home=/Users/janaudy/Softwares/jboss-3.2.2RC4/
jboss.deploy=${jboss.home}/server/default/deploy/
ejb.libs=/Users/janaudy/Softwares/jboss-3.2.2RC4/server/default/lib

################
# XDoclet Libs #
################
xdoclet.libs=/Users/janaudy/Softwares/xdoclet-bin-1.2b3/lib

##################
# Modules and co #
##################

base=..

ear-module=${base}/ear-module

client-module=${base}/client-module
client-meta-inf=${client-module}/META-INF

ejb-module=${base}/ejb-module
ejb-meta-inf=${ejb-module}/META-INF

web-module=${base}/web-module
web-classes=${web-module}/WEB-INF/classes

src=${base}/src

gen-src-folder=${base}/gen-src

build.xml


<?xml version="1.0"?>

<project name="SessionBean" default="all" basedir="..">
    <property file="build.properties"/>
    
    <taskdef name="java2html" 
             classname="com.java2html.Java2HTMLTask">
      <classpath>
        <pathelement location="../../../../misc/java2html/j2h.jar"/>
      </classpath> 
    </taskdef>

    <!-- =================================================================== -->
    <!-- Define the class path                                               -->
    <!-- =================================================================== -->
    <path id="my.class.path">
        <fileset dir="${ejb.libs}">
            <include name="jboss-j2ee.jar"/>
            <include name="jbosssx.jar"/>
        </fileset>
        <fileset dir="${xdoclet.libs}">
            <include name="*.jar"/>
        </fileset>
    </path>

    <!-- =================================================================== -->
    <!-- Initialise                                                          -->
    <!-- =================================================================== -->
    <target name="init">
      <tstamp>
        <format property="TODAY" pattern="d-MM-yy"/>
      </tstamp>
      <taskdef name="xdoclet"
               classname="xdoclet.DocletTask"
               classpathref="my.class.path"/>
      <taskdef name="ejbdoclet"
               classname="xdoclet.modules.ejb.EjbDocletTask"
               classpathref="my.class.path"/>
    </target>

    <!-- =================================================================== -->
    <!-- Prepares the directory structure                                    -->
    <!-- =================================================================== -->
    <target name="prepare" depends="init">   
      <delete file="${ejb-meta-inf}/orion-ejb-jar.xml"
              verbose="true"/>
      <delete file="${ear-module}/Cart.jar"
              verbose="true"/>
      <delete file="Cart.ear"
              verbose="true"/>
      <delete file="CartClient.jar"
              verbose="true"/>
              
      <delete file="${ejb-meta-inf}/ejb-jar.xml"
              verbose="true"/>
      <delete file="${ejb-meta-inf}/jboss.xml"
              verbose="true"/>
    </target>

    <!-- =================================================================== -->
    <!-- Invoke XDoclet's ejbdoclet                                          -->
    <!-- =================================================================== -->
    <target name="ejbdoclet" depends="prepare">
        <echo>+---------------------------------------------------+</echo>
        <echo>|                                                   |</echo>
        <echo>| R U N N I N G   E J B D O C L E T                 |</echo>
        <echo>|                                                   |</echo>
        <echo>+---------------------------------------------------+</echo>

        <ejbdoclet destdir="${gen-src-folder}"
                   excludedtags="@version,@author,@todo"
                   addedtags="@xdoclet-generated at ${TODAY},@copyright The XDoclet Team,@author XDoclet,@version 1.2 beta 2"
                   ejbspec="2.0"
                   verbose="true">

          <fileset dir="${ejb-module}">
            <include name="org/jyperion/j2ee/sample/sfsb/CartBean.java"/>
          </fileset>

          <deploymentdescriptor destdir="${ejb-meta-inf}"/>

          <homeinterface/>
          <remoteinterface/>
          <localhomeinterface/>
          <localinterface/>
          
          <jboss version="3.0"
                 unauthenticatedPrincipal="nobody"
                 xmlencoding="UTF-8"
                 destdir="${ejb-meta-inf}"
                 validatexml="true"
                 preferredrelationmapping="relation-table"/>      
        </ejbdoclet>
    </target>

    <!-- =================================================================== -->
    <!-- Compiles all the classes                                            -->
    <!-- =================================================================== -->

    <target name="compile" depends="ejbdoclet">
        <echo>+---------------------------------------------------+</echo>
        <echo>|                                                   |</echo>
        <echo>| C O M P I L I N G   S O U R C E S                 |</echo>
        <echo>|                                                   |</echo>
        <echo>+---------------------------------------------------+</echo>

        <javac destdir="${ejb-module}"
               classpathref="my.class.path"
               debug="on"
               deprecation="on"
               optimize="off">

            <src path="${gen-src-folder}"/>
            <src path="${ejb-module}"/>
        </javac>
        
        <javac destdir="${client-module}"
               classpathref="my.class.path"
               debug="on"
               deprecation="on"
               optimize="off">

            <src path="${gen-src-folder}"/>
            <src path="${client-module}"/>
        </javac>
    </target>

    <!-- =================================================================== -->
    <!-- Main                                                                -->
    <!-- =================================================================== -->
    <target name="jar">
        <echo>+---------------------------------------------------+</echo>
        <echo>|                                                   |</echo>
        <echo>| R U N N I N G  X D O C L E T                      |</echo>
        <echo>|                                                   |</echo>
        <echo>+---------------------------------------------------+</echo>
        
        <echo>Jaring ...</echo>
        <jar destfile="${ear-module}/Cart.jar"
             basedir="${ejb-module}"/>  
                
        <jar destfile="Cart.ear"
             basedir="${ear-module}"/>
             
        <jar destfile="CartClient.jar"
             basedir="${client-module}"/>
    </target>
    
    <!-- =================================================================== -->
    <!-- Main                                                                -->
    <!-- =================================================================== -->
    <target name="j2h">
      <echo>Running Java2HTML</echo>
      
      <java2html title="EJB SessionBean (Cart) et Client Java" 
                 simple="no"
                 tabsize="4"
                 marginsize="2"
                 header="true"
                 footer="false"
                 destination="../doc">
        <fileset dir="${base}">
          <include name="**/*.java"/>
        </fileset>
      </java2html> 
    </target>
    
    <!-- =================================================================== -->
    <!-- Deploy                                                              -->
    <!-- =================================================================== -->
    <target name="deploy">
      <echo>Deploying in jBoss ...</echo>
      
      <copy file="Cart.ear" 
            todir="${jboss.deploy}"/> 
    </target>
    
    <target name="all" depends="compile, jar, j2h, deploy"/>
</project>

2. Client Web -> EJB Stateless Session Bean via un Servlet

We now know how to use JSPs, Servlets, EJBs, ANT and XDoclet.
The next step is to be able to invoke a Stateless Session Bean from a browser.
You will write a HTML client that invokes a method on a Stateless Session Bean that returns the VM free memory.

Stateless Session Bean Life-cycle

UML View


Exercise


The servlet becomes the client.