JaVa
   

Enterprise JavaBeans

In this section, we create two simple enterprise beans and demonstrate how to build an EJB JAR file. We also create a local proxy class—the shadow bean—that knows how to speak to the enterprise beans. The shadow bean implements the Greeting interface and can be plugged-in in place of the GreetingBean from the model library. The session bean communicates with the entity bean, so its deployment descriptor entry must reference the entity bean. Let's start by examining the files in this project.

Directory Structure of the Enterprise Beans Project

This project has more files than any other project in this example:

Root directory of the Enterprise beans | build.xml
|
+---src
| \---xptoolkit
| | | |
| +---ejbs
| | GreetingEntityBean.java
| | GreetingEntityHome.java
| | GreetingEntityRemote.java
| | GreetingSessionBean.java
| | GreetingSessionRemote.java
| | GreetingSessionHome.java
| | | |
| \---model
| GreetingShadow.java
|
+---META-DATA
| ejb.xml
| |
+---jboss_clientlib
| jnp-client.jar
| jbosssx-client.jar
| jboss-client.jar
| ejb.jar
|
\---jboss_props
 jndi.properties


We used JBoss, an open-source implementation of Enterprise JavaBeans. For JBoss, four JAR files and one properties file are used for the client-side deployment. If you decide to use JBoss, then you have the same or similar JAR files for packaging your client (depending on the version of JBoss you're using).

There are six Java source files, which we'll discuss in depth. These six files define two Enterprise JavaBeans. In addition, one deployment descriptor describes the two Enterprise JavaBeans to the EJB container. There is nothing specific about the deployment descriptor or the Enterprise JavaBeans to JBoss; they should work on any J2EE-compliant EJB app server. Of course, build.xml is the Ant buildfile that takes all this raw material and forms it into a client-side JAR file and an EJB JAR file that can be deployed in any J2EE EJB app server. In the following parts of this section, we describe each of these files. Let's start with the entity bean.

The HelloWorld Entity Bean

Like most classes in this example, the entity bean is a simple object that returns a greeting. In fact, because the entity is generally used to represent a view of rows in a database, we had to cheat a little and define a static variable to simulate a primary key. The entity bean consists of three classes: the GreetingEntityHome interface; the GreetingEntityRemote interface; and the implementation, the GreetingEntityBean class. The Home and Remote interfaces are part of the beans contract with the EJB container. The following listing shows the source code for the Hello World entity bean, GreetingEntityBean.

package xptoolkit.ejbs;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.EJBException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
public class GreetingEntityBean implements EntityBean{
 static int pkid;
 public Integer ejbCreate(){
 return new Integer(++pkid);
 }
 public void ejbPostCreate()throws CreateException{
 }
 public Integer ejbFindByPrimaryKey(Integer i)throws FinderException{
 return new Integer(1);
 }
 public void ejbActivate(){}
 public void ejbLoad(){}
 public void ejbPassivate(){}
 public void ejbRemove(){}
 public void ejbStore(){}
 public void setEntityContext(EntityContext cotext){}
 public void unsetEntityContext(){}
 public String getGreeting(){
 return "Hello World!";
 }
}


The next listing shows the GreetingEntityHome interface.

package xptoolkit.ejbs;
import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import java.rmi.RemoteException;
public interface GreetingEntityHome extends EJBHome{
 GreetingEntityRemote create() throws RemoteException, CreateException;
 GreetingEntityRemote findByPrimaryKey(Integer i)throws RemoteException, FinderException;
}


The next listing shows the GreetingEntityRemote interface.

package xptoolkit.ejbs;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface GreetingEntityRemote extends EJBObject {
 public String getGreeting()throws RemoteException;
}


As you can probably see by looking at the implementation of GreetingEntityBean, it does not do much. Most of the methods are to fulfill the contract with the container. A few of the methods fake a primary key id semantic, but that is beyond the scope of this tutorial. Instead, focus on the following method of the entity bean implementation:

 public String getGreeting(){
 return "Hello World!";
 }


This method returns the now-infamous greeting. The session bean connects with the entity bean and calls this method. In order to do this, the deployment descriptor must be configured properly.

The Session Bean

This session bean's sole purpose in life is to connect to the entity bean and call the entity bean's getGreeting() method. Like the entity bean, the HelloWorld session defines three source files: one for the implementation bean class, one for the remote interface, and one for the home interface. The three classes are the GreetingSessionBean class, the GreetingSessionRemote interface, and the GreetingSessionHome interface, respectively. The next three listings provide the full source code for the remote, home, and implementation. Here is the source for the GreetingSessionBean class:

package xptoolkit.ejbs;
import javax.rmi.PortableRemoteObject; import javax.naming.InitialContext;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.EJBException;
import javax.ejb.CreateException;
public class GreetingSessionBean implements SessionBean{
 public void setSessionContext(SessionContext context) throws EJBException{
 }
 public void ejbCreate() throws CreateException{
 } 
 public void ejbRemove() throws EJBException{
 }
 public void ejbActivate() throws EJBException{
 }
 public void ejbPassivate() throws EJBException{
 }
 public String getGreeting(){
 GreetingEntityHome home;
 GreetingEntityRemote remote;
 InitialContext jndiContext;
 Object ref;
 String greeting="bye bye";
 try {
 jndiContext = new InitialContext();
 ref = jndiContext.lookup
 ("java:comp/env/ejb/GreetingEntityBean");
 home = (GreetingEntityHome) PortableRemoteObject.narrow (ref, GreetingEntityHome.class);
 remote = home.create();
 greeting = remote.getGreeting();
 System.out.println("Greeting: " + greeting);
 }
 catch (Exception e){
 throw new EJBException(e);
 }
 return greeting;
 }
}


Following is the source for the GreetingSessionHome interface.

package xptoolkit.ejbs;
import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import java.rmi.RemoteException;
public interface GreetingSessionHome extends EJBHome{
 public GreetingSessionRemote create() throws CreateException, RemoteException;
}


And finally, here is the source for the GreetingSessionRemote interface.

package xptoolkit.ejbs;

import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface GreetingSessionRemote extends EJBObject{
 public String getGreeting()throws RemoteException;
}


Like the entity bean, the session bean's main focus is the getGreeting() method. The getGreeting() method of the session bean calls the entity bean's getGreeting() method. But first it has to look it up. Let's break down the getGreeting() method down step by step, because it is important when we discuss the deployment descriptor in the next part of this section. First the method defines five variables, as follows:

 GreetingEntityHome home;
 GreetingEntityRemote remote;
 InitialContext jndiContext;
 Object ref;
 String greeting="bye bye";


The home variable creates an instance of GreetingEntityBean (a reference to its remote). The remote variable holds a reference to the remote proxy of GreetingEntityBean. The session bean never calls GreetingEntityBean directly; instead, it talks to the remote interface (a detailed discussion is beyond the scope of this tutorial). The jndiContext looks up the GreetingEntityBean's home interface in the naming service provided by the EJB container that uses the JNDI API interface. The ref object holds a reference to the home object before it is narrowed into the home interface. (The narrow concept is similar to casting, but it could involve a CORBA narrow, which is beyond the scope of this tutorial—please view it as a cast.) Finally, the greeting variable holds the greeting message returned from the entity bean. Now, let's look at the rest of the method step by step. First we get the JNDI initial context. The entity beans environment is defined in the deployment descriptor, and the EJB container provides this context (environment) to the enterprise bean at runtime (more on this in the deployment descriptor part of this section) as follows:

 jndiContext = new InitialContext();


Next we get the home reference from the jndiContext. Note that the home is mapped to this location by the deployment descriptor as follows:

 ref = jndiContext.lookup
 ("java:comp/env/ejb/GreetingEntityBean");


Now that we have the home interface reference, we can narrow it down to a real home interface (the PortableRemoteObject.narrow is needed for compatibility with CORBA IIOP—that is, RMI over IIOP) as follows:

 home = (GreetingEntityHome) PortableRemoteObject.narrow (ref, GreetingEntityHome.class);


Now we have a real home object proxy; thus, we can use the home to create the remote object reference to the entity bean as follows:

 remote = home.create();


Once we have the remote interface, we can begin using it to call the entity bean. The first and only method we call is getGreeting(), as follows:

 greeting = remote.getGreeting();


Just to prove to ourselves that we were able to call the session bean, we print out the message, which shows up in the stdout for the EJB server we are using for this tutorial:

 System.out.println("Greeting: " + greeting);


As you can see, the method for session bean just delegates the call to the entity bean. The nice thing is that the entity bean is looked up with JNDI. This approach demonstrates one enterprise bean talking to another enterprise bean. In the next section, we create a proxy bean that takes care of all the complexities of dealing with JNDI on the client side.

The Shadow Proxy Bean

The shadow bean is a proxy and an adapter for the client side. It is a proxy in that it has the same methods as the remote interface and all calls to it are delegated to the remote interface. The shadow handles all access to the JNDI interface, the home reference to create the session bean's remote interface. The shadow even handles non-compliant J2EE servlet engines, by allowing the JNDI properties to be loaded as a class resource. Note that a servlet engine can be servlet specification–compliant without being J2EE container-compliant. It is an adapter in that it adapts the remote interface of the session bean to the xptoolkit.model.Greeting interface. The code for the proxy is shown next.

package xptoolkit.model;
import java.util.Properties;
import javax.rmi.PortableRemoteObject; import javax.naming.InitialContext;
import xptoolkit.ejbs.GreetingSessionRemote;
import xptoolkit.ejbs.GreetingSessionHome;
import xptoolkit.ejbs.GreetingEntityRemote;
import xptoolkit.ejbs.GreetingEntityHome;
import java.io.InputStream;
public class GreetingShadow implements Greeting{
 GreetingSessionRemote remote;
 static Properties env = new Properties();
 static { InputStream is;
 try{
 is=
 GreetingShadow.class.getResourceAsStream("/jndi.properties");
 env.load(is);
 }
 catch(Exception e){
 System.out.println(""+e);
 System.out.println();
 e.printStackTrace();
 }
 }
 public GreetingShadow() {
 GreetingSessionHome home;
 InitialContext jndiContext;
 Object ref;
 try{
 jndiContext = new InitialContext(env);
 ref = jndiContext.lookup("GreetingSessionBean");
 home = (GreetingSessionHome) PortableRemoteObject.narrow (ref, GreetingSessionHome.class);
 remote = home.create();
 }
 catch(Exception e){
 System.out.println(e.getMessage());
 System.out.println();System.out.println();
 e.printStackTrace();
 }
 }
 public String getGreeting() {
 String greeting="bye bye";
 try{
 greeting=remote.getGreeting();
 }
 catch(Exception e){}
 return greeting;
 }
}


The bean tries to load the properties needed to run the JNDI naming service (that is, to create the initial context):

 static { InputStream is;
 try{
 is=
 GreetingShadow.class.getResourceAsStream("/jndi.properties");
 env.load(is);
 }
 catch(Exception e){
 System.out.println(""+e);
 System.out.println();
 e.printStackTrace();
 }
 }


As you can see, this code is not very robust. However, if you are expecting the properties for JNDI to be loaded elsewhere, the code won't complain. For example, some Web app servers allow you to set up the JNDI environment in the configuration file for the server. If you decide to use these features of the Web app server, then this code works regardless—and because it is in the static initializer block, it runs only once. The constructor of the GreetingShadow class tries to load the initial context based on the properties loaded in the static initializer block. If those properties are not set, JNDI uses the initial context that is configured for this environment; if nothing is set or the wrong JNDI initial context is mapped, then you will not be able to look up the home object of GreetingSessionBean. Otherwise, the following code is a lot like the code for the session to talk to the entity, except now it is the shadow (client side proxy) talking to the session. Look at the following code and compare it to the session code talking to the entity code:

 public GreetingShadow() {
 GreetingSessionHome home;
 InitialContext jndiContext;
 Object ref;
 try{
 jndiContext = new InitialContext(env);
 ref = jndiContext.lookup("GreetingSessionBean");
 home = (GreetingSessionHome) PortableRemoteObject.narrow (ref, GreetingSessionHome.class);
 remote = home.create();
 }
 catch(Exception e){
 System.out.println(e.getMessage());
 System.out.println();System.out.println();
 e.printStackTrace();
 }
 }


Next, all methods (such as getGreeting()) are delegated to the remote interface that was create in the constructor. This is demonstrated as follows:

 public String getGreeting() {
 String greeting="bye bye";
 try{
 greeting=remote.getGreeting();
 }
 catch(Exception e){}
 return greeting;
 }


The important thing to note is that because the GreetingShadow implements the Greeting interface, it can be swapped out for GreetingBean. All we have to do is set the system property "Greeting.class" to "GreetingShadow", and the GreetingFactory will instantiate it on a call to getGreeting() instead of GreetingBean. If you recall, HelloServlet reads a "Greeting.class" servlet parameter and uses it to set the system property "Greeting.class". Thus, if we change the "Greeting.class" servlet parameter in the Web app deployment descriptor, we can swap the local GreetingBean with the remote proxy GreetingShadow. We accomplish this trick with the buildfile for the Web app at the end of this section. First, let's look at the deployment descriptor for the Enterprise JavaBeans. (Note that the jndi.properties file may need to be changed for your environment; refer to the EJB documentation of your EJ app server.)

The Enterprise JavaBeans Deployment Descriptor

As we discussed in , "J2EE Deployment Concepts," the Enterprise JavaBean deployment descriptor sets up the environment for the Enterprise JavaBeans that we are going to deploy. The next listing shows the Enterprise JavaBean deployment descriptor (ejb-jar.xml) for our Hello World app.

<ejb-jar>
<description>
This ejb-jar files contains the Enterprise beans for the model 2 Hello World app.
</description>
<enterprise-beans>
 <entity>
 <description>
 The GreetingEntityBean is a do nothing bean to demonstrate
 how to deploy an Enterprise bean with Ant.
 </description>
 <ejb-name>GreetingEntityBean</ejb-name>
 <home>xptoolkit.ejbs.GreetingEntityHome</home>
 <remote>xptoolkit.ejbs.GreetingEntityRemote</remote>
 <ejb-class>xptoolkit.ejbs.GreetingEntityBean</ejb-class>
 <transaction-type>Container</transaction-type>
 <reentrant>True</reentrant>
 <prim-key-class>java.lang.Integer</prim-key-class>
 <persistence-type>Bean</persistence-type>
 </entity>
 <session>
 <description>
 The GreetingSessionBean is a do nothing bean to demonstrate
 how to deploy an Enterprise bean with Ant.
 </description>
 <ejb-name>GreetingSessionBean</ejb-name>
 <home>xptoolkit.ejbs.GreetingSessionHome</home>
 <remote>xptoolkit.ejbs.GreetingSessionRemote</remote>
 <ejb-class>xptoolkit.ejbs.GreetingSessionBean</ejb-class>
 <session-type>Stateful</session-type>
 <transaction-type>Container</transaction-type>
 <ejb-ref>
 <description>
 This sets up a references from the Entity bean to the
 session bean.
 Thus, the session bean can look up the Entity bean in its environment space.
 </description>
 <ejb-ref-name>ejb/GreetingEntityBean</ejb-ref-name>
 <ejb-ref-type>Entity</ejb-ref-type>
 <home>xptoolkit.ejbs.GreetingEntityHome</home>
 <remote>xptoolkit.ejbs.GreetingEntityRemote</remote>
 <ejb-link>GreetingEntityBean</ejb-link>
 </ejb-ref>
 </session>
</enterprise-beans>
<assembly-descriptor>
 <container-transaction>
 <method>
 <ejb-name>GreetingSessionBean</ejb-name>
 <method-name>*</method-name>
 </method>
 <trans-attribute>Supports</trans-attribute>
 </container-transaction>
 <container-transaction>
 <method>
 <ejb-name>GreetingEntityBean</ejb-name>
 <method-name>*</method-name>
 </method>
 <trans-attribute>Supports</trans-attribute>
 </container-transaction>
</assembly-descriptor>
</ejb-jar>


This deployment descriptor is fairly standard. We'll point out some areas of interest. Inside the session bean's descriptor element is a sub-element to define a reference to the entity bean, as follows:

 <ejb-ref>
 <description>
 This sets up a references from the Entity bean to the
 session bean.
 Thus, the session bean can look up the Entity bean in
 its environment space.
 </description>
 <ejb-ref-name>ejb/GreetingEntityBean</ejb-ref-name>
 <ejb-ref-type>Entity</ejb-ref-type>
 <home>xptoolkit.ejbs.GreetingEntityHome</home>
 <remote>xptoolkit.ejbs.GreetingEntityRemote</remote>
 <ejb-link>GreetingEntityBean</ejb-link>
 </ejb-ref>


This is how the session bean can find the entity bean. The container takes care of the setting for the initial context (unlike the shadow, where we loaded the properties from jndi.properties). In addition, the container reads the ejb-ref element and maps GreetingEntityBean's home into the GreetingSessionBean's environment context. Cool! As you can see, we have a lot of files to manage. We have JAR files and a JNDI property file that must be locatable by the client-side shadow's class loader. We also have a set of classes (remote and home) that must be packaged in the client-side shadow's JAR file. On the flip side, we have a deployment descriptor and six Java files that need to be packaged in the server-side JAR file for two the Enterprise JavaBeans.

The Ant buildfile, which is described in the next section, takes care of all these complexities. It documents the process and ensures the build is repeatable.

The Enterprise Bean Buildfile

As we stated, the output of the buildfiles is two JAR files. One JAR file is for the EJB's container; and the other file is for the server. Also, unlike the war command, there is no special command to build EJB JAR files. Frankly, you don't need a special command because EJB JAR files are very similar to regular JAR files. The following listing contains the complete buildfile for the enterprise beans.

<project name="enterprise_beans" default="all" >
 <target unless="setProps" description="setup the properites.">
 <property value="/tmp/app" /> <property value="/jboss/jboss/deploy" />
 </target>
 <target depends="setProps" description="initialize the properties.">
 <tstamp/>
 <property value="${outdir}/ejbs" />
 <property value="${ejbout}/ejb-jar" />
 <property value="${ejbout}/ejb-jar-client" />
 <property value="${outdir}/dist" />
 <property value="${outdir}/lib" />
 <property name="meta-data" value="${build}/META-INF" />
 <property name="build_lib" value="./../lib" />
 <property name="ejb_lib" value="/JBoss/jboss/lib/ext" />
 </target>
 <target name="clean_jboss" if="jboss">
 <delete file="${jboss}/greetbeans.jar" />
 </target>
 <target depends="init,clean_jboss" description="clean up the output directories.">
 <delete dir="${build}" />
 <delete dir="${meta-data}" />
 <delete dir="${client}" />
 <delete dir="${dist}/greet-ejbs.jar" />
 </target>
 <target depends="init" description="prepare the output directory.">
 <mkdir dir="${build}" />
 <mkdir dir="${lib}" />
 <mkdir dir="${meta-data}" />
 <mkdir dir="${client}" />
 <mkdir dir="${dist}" />
 </target>
 <target depends="prepare" description="compile the Java source.">
 <javac srcdir="./src" destdir="${build}" >
 <classpath >
 <pathelement location="." />
 <fileset dir="${build_lib}">
 <include name="**/*.jar"/>
 </fileset>
 <fileset dir="${lib}">
 <include name="**/*.jar"/>
 </fileset>
 <fileset dir="${ejb_lib}">
 <include name="**/*.jar"/>
 </fileset>
 </classpath>
 </javac>
 </target>
 <target name="config_jboss_jndi" if="jboss">
 <copy todir="${client}" > <fileset dir="./jboss_props" />
 </copy>
 </target>
 <target name="config_jndi" depends="config_jboss_jndi" />
 <target depends="compile,config_jndi"
 description="package the Java classes into a jar.">
 <copy todir="${client}" > <fileset dir="${build}" excludes="**/*Bean*" includes="**/*.class*" />
 </copy>
 <copy file="./META-DATA/ejb.xml" tofile="${meta-data}/ejb-jar.xml" />
 <jar jarfile="${dist}/greet-ejbs.jar"
 basedir="${build}" />
 <jar jarfile="${lib}/client-greet-ejbs.jar"
 basedir="${client}" />
 </target>
 <target name="deploy_jboss" depends="package" if="jboss">
 <copy file="${dist}/greet-ejbs.jar" todir="${jboss}" />
 <copy todir="${lib}" >
 <fileset dir="./jboss_clientlib" />
 </copy>
 </target>
 <target depends="package,deploy_jboss"
 description="deploys the jar file to the ejb server.">
 </target>
 <target depends="clean,deploy" description="perform all targets."/> </project>


Analyzing the Buildfile for Enterprise Beans

This buildfile is structured like the other buildfiles. It has similar targets that do similar things. One key difference is that the package target for this buildfile outputs two JAR files instead of one, as follows:

 <target depends="compile,config_jndi"
 description="package the Java classes into a jar.">
 <copy todir="${client}" > <fileset dir="${build}" excludes="**/*Bean*" includes="**/*.class*" />
 </copy>
 <copy file="./META-DATA/ejb.xml" tofile="${meta-data}/ejb-jar.xml" />
 <jar jarfile="${dist}/greet-ejbs.jar"
 basedir="${build}" />
 <jar jarfile="${lib}/client-greet-ejbs.jar"
 basedir="${client}" />
 </target>


Because the client JAR file (client-greet-ejbs.jar) does not need the implementation classes (GreetingEntityBean and GreetingSessionBean), the output classes are copied to a temporary directory and a file set is defined that excludes all files with the substring Bean in them. This is demonstrated as follows:

 <copy todir="${client}" > <fileset dir="${build}" excludes="**/*Bean*" includes="**/*.class*" />
 </copy>


After we copy the buildfiles to the ${client} output directory, we jar them into the client file as follows:

 <jar jarfile="${lib}/client-greet-ejbs.jar"
 basedir="${client}" />


The astute reader may wonder about the jndi.properties file that the shadow needs to create the initial context. Notice that the package target depended on compile and config_jndi targets. The config_jndi depends on the config_jboss_jndi target, which in turn copies the jndi.properties file located in ./jboss_props to the client directory that is jarred, as follows:

 <target name="config_jndi" depends="config_jboss_jndi" />
 <target name="config_jboss_jndi" if="jboss">
 <copy todir="${client}" > <fileset dir="./jboss_props" />
 </copy>
 </target>


Notice that the config_jboss_jndi target is executed only if the jboss environment variable is set. Thus you can easily turn it off by not setting this environment variable. If you wanted to set up a similar mechanism for, for example, Orion (another J2EE app server environment that supports EJB), you could add a config_orion_jndi dependency to the config_jndi target, and then define a config_orion_jndi target that copies the jndi.properties file from the ./orion directory. Note that it is not necessary to use either if the servlet engine you are using is also a J2EE-compliant container. That takes care of the client-side bean. What about the sever-side bean—the EJB bean for the EJB container, which needs the deployment descriptor? Remember, we said that Ant does not have a special task as it does for the WAR file. Thus, we need to build an exact replica of the directory structure needed by the EJB JAR file and then jar the directory. The prepare target prepares the output directory for the server side. Then, the package target copies the deployment descriptor to the correct location and jars the location as follows. First we copy the deployment descriptor to the right location:

 <copy file="./META-DATA/ejb.xml" tofile="${meta-data}/ejb-jar.xml" />


Next we jar the location:

 <jar jarfile="${dist}/greet-ejbs.jar"
 basedir="${build}" />


You may want to examine the metadata and build property settings, demonstrated as follows:

 <target depends="setProps" description="initialize the properties.">
 <tstamp/>
 <property value="${outdir}/ejbs" />
 <property value="${ejbout}/ejb-jar" />
 <property value="${ejbout}/ejb-jar-client" />
 <property value="${outdir}/dist" />
 <property value="${outdir}/lib" />
 <property name="meta-data" value="${build}/META-INF" />
 <property name="build_lib" value="./../lib" />
 </target>


Another difference between this buildfile and the webapp project buildfile is that the deploy target works with JBoss instead of Resin and Tomcat like the Web app. The deploy target is set up in a similar fashion to the way the config_jndi is set up, in that it can easily be modified to support more types of app server—even remote servers using the ftp task, as we mentioned earlier in this chapter. Here is the deploy target:

 <target name="deploy_jboss" depends="package" if="jboss">
 <copy file="${dist}/greet-ejbs.jar" todir="${jboss}" />
 <copy todir="${lib}" >
 <fileset dir="./jboss_clientlib" />
 </copy>
 </target>
 <target depends="package,deploy_jboss"
 description="deploys the jar file to the ejb server.">
 </target>


Notice that it copies the server-side JAR (greet-ejbs.jar) to the JBoss deployment directory, and it copies all the support libraries that the client-side JAR needs to the common directory (<copy todir="${lib}". . . <fileset dir="./jboss_clientlib" ...).

The next part of this section covers a little magic that we do to the webapp buildfile to use either the enterprise beans or the local version of the greeting bean.

Defining the ejb Property in the Web app Buildfile

All the buildfile snippets in this part of the section are for the webapp project. By defining the "ejb" property, the webapp project has the option of deploying/configuring whether the Web app that is deployed uses enterprise beans. Notice that the prepare_meta target, which is a dependency of the prepare target, has two dependencies prepare_meta_ejb and prepare_meta_noejb, demonstrated as follows:

 <target name="prepare_meta" depends="prepare_meta_ejb, prepare_meta_noejb">
 <copy todir="${meta}" filtering="true">
 <fileset dir="./meta-data"/>
 </copy>
 </target>


The prepare_meta_ejb target is executed only if the "ejb" property is set as follows:

 <target name="prepare_meta_ejb" if="ejb">
 <filter token="Greeting.class"
 value="xptoolkit.model.GreetingShadow"/>
 </target>


If the "ejb" property is set, then the target creates a filter token called Greeting.class. Here, we set the value of Greeting.class to GreetingShadow. Conversely, the prepare_meta_ejb target is executed only if the "ejb" property is not set, as follows:

 <target name="prepare_meta_noejb" unless="ejb">
 <filter token="Greeting.class" value="xptoolkit.model.GreetingBean"/>
 </target>


Here, we set GreetingBean as "Greeting.class". But how is this used by the app? You may recall that HelloWorldServlet uses the servlet parameter "Greeting.class" to set the system property "Greeting.class" that is used by the GreetingFactory to create an instance of Greeting. We put an Ant filter key in the webapp project deployment descriptor, as follows:

 <servlet>
 <servlet-name>HelloWorldServlet</servlet-name>
 <servlet-class>xptoolkit.web.HelloWorldServlet</servlet-class>
 <init-param>
 <param-name>Greeting.class</param-name>
 <param-value>@Greeting.class@</param-value>
 </init-param>
 </servlet>


If we copy this file using the filter command after the filter token Greeting.class has been set, then @Greeting.class@ is replaced with the value of our token Greeting.class, which is set to xptoolkit.model .GreetingShadow in the prepare_meta_ejb target and to xptoolkit.model.GreetingBean in the prepare_meta_noejb target. Notice that the prepare_meta target copies the deployment descriptor with filtering turned on, as follows:

 <copy todir="${meta}" filtering="true">
 <fileset dir="./meta-data"/>
 </copy>


Running the Buildfiles

In order to get the webapp project to run with the enterprise beans, we do the following.

  1. Navigate to the EJBeans directory and build the EJB buildfile, as follows:

    C:\CVS\...\MVCHelloWorld\EJBeans>ant
    


    For the EJB file, the deploy target is part of the all-target dependency, so it is deployed just by running it.

  2. Build the webapp project with the "ejb" property set so that the correct Greeting.class is set in the webapp project deployment descriptor, as follows:
    C:\CVS\...\MVCHelloWorld\Webapp>ant -Dejb=true deploy
    


    Note that you may want to run both of these buildfiles with the clean option before you attempt to deploy.

  3. Now that we have the app deployed, we start the EJB server (we used JBoss installed in /jboss) and the Web app server (we used Resin installed in /resin) and try the app as before. If we set up everything correctly, we get something like the following message in the stdout of our EJB container. The following is from JBoss:
    [GreetingSessionBean] Greeting: Hello World!
    


You should have a buildfile for each major subsystem or component in your app. A medium-size app could have 50 to 100 subsystems and components with separate buildfiles. The small example that we presented in this chapter now has five buildfiles that must be executed in the correct order. Having 5 buildfiles and expecting a deployer, system admin, or fellow developer to execute them in the right order is loony; expecting a fellow developer, system admin, or deployer to build 50 buildfiles in the correct order is suicide.

Besides, human intervention defeats the purpose of automating the buildfile process (having a repeatable build). What we need is a master buildfile that manages all the complexities of this simple Hello World app. In the next section, we create such a buildfile to manage the complexities of building these five projects and perform some special deployment magic.


JaVa
   
Comments