18-749 JBoss/EJB tutorial
by Hwi H. Cheong (Paul)

As professor Priya has probably told you, the learning curve for EJB is pretty steep. The goal of this tutorial is to jump-start you on your EJB implementation using JBoss. The quickest way to start is to use O'Reilly JBoss Workbook as a starting point (/afs/ece/class/ece749/ejb/ejbwJboss.Files.zip). The zip file contains ejbwJboss.ExFiles.zip and ejbwJboss.eBook.pdf. You should go through the .pdf file and get yourself familiar with EJB by running first couple examples. Please refer to the 18749 EJB FAQ page to get things set up. This tutorial will teach you how to build (or start to build) a simple application that uses a stateless bean and an entity bean. The actual method definitions are not included. That's for you to figure out. :)


Session Beans

There are two kinds of session beans: stateful and stateless. A stateless bean is pooled inside the JBoss server, meaning that any instance of the bean can serve a request. Typically there will be one stateless bean serving multiple clients. This is possible because the stateless bean doesn't need to store any specific states about the clients. You receive a request, you serve it, then you forget about it. A stateful bean is different. If you use stateful beans to serve requests, there will be a stateful bean for every client. This enables the bean to store states for its specific client.

Entity Beans

An entity bean is like a table in a database. An instance of the entity bean is like an entry in the table. You don't need to manually create tables in the database for your entity beans. JBoss will do it for you (assuming that you've set up everything correctly). Bug your TA's about setting up JBoss to work with the 18749 mySQL database.

Basically, session beans are for handling server requests (what does the client want the server to do?) and entity beans are for handling database requests (what does the client want to do with the database?). Now that you (kind of) know what session beans and entity beans are, let's get down and dirty!


Let's say we want to build a Chess game application that lets multiple users play Chess with each other. What do we need?

For now, let's shove all the information into the database. Since everything will be stored in the database, we don't need to worry about saving anything in the server. That means? You guessed it. We don't need states in the server and therefore can use stateless session beans. An implementation tip: It's very convenient to have one single session bean that every request goes through, instead of multiple session beans. This way, the clients are aware of only one bean that they'll talk to. That makes our lives a lot simpler. What about entity beans? Having clients access entity beans is like having them access our database directly. Let's, instead, have them access entity beans through the session bean. We'll call our session bean ChessHost and our entity bean ChessPlayer.

There are two types of beans: remote and local. Remote beans are seen by the client. Local beans are not; they're only seen by other beans inside the server. If we make our ChessPlayer entity bean local, then the client wouldn't be able to see it. Also it would be more efficient this way since our ChessHost can access ChessPlayer locally (faster) instead of remotely. Why call your roommate with a cell phone when you can simply talk to him right in the room? The ChessHost session bean has to be remote, since we want our clients to connect to it.

ejb-jar.xml and jboss.xml: These two guys tell JBoss what kind of beans to create, what to call them, etc. For our purpose, the two files should look like the following:

ejb-jar.xml:

...
<enterprise-beans>
   <session>
      <description></description>
      <display-name></display-name>
      <ejb-name>ChessHost</ejb-name> <!--This is what we're calling this session bean-->
      <home>chess.interfaces.ChessHostHome</home> <!--I have my home interface in /src/chess/interfaces/ChessHostHome.java-->
      <remote>chess.interfaces.ChessHost</remote> <!--I have my remote interface in /src/chess/interfaces/ChessHost.java-->
      <ejb-class>chess.ejb.ChessHostBean</ejb-class> <!--I have my definitions in /src/ejb/ChessHostBean.java-->
      <session-type>Stateless</session-type> <!--This bean is stateless-->
      <transaction-type>Container</transaction-type>

      <!--The below ejb-local-ref block tells JBoss that we're referencing a local entity bean inside this session bean-->
      <ejb-local-ref>
         <ejb-ref-name>ChessPlayer</ejb-ref-name>
         <ejb-link>ChessPlayer</ejb-link>
         <ejb-ref-type>Entity</ejb-ref-type>
         <local-home>chess.interfaces.ChessPlayerHome</local-home>
         <local>chess.interfaces.ChessPlayer</local>
      </ejb-local-ref>
   </session>

   <entity>
      <description></description>
      <display-name></display-name>
      <ejb-name>ChessPlayer</ejb-name>
      <local-home>chess.interfaces.ChessPlayerHome</local-home>
      <local>chess.interfaces.ChessPlayer</local>
      <ejb-class>chess.ejb.ChessPlayerBean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.String</prim-key-class> <!--The primary key type is String-->

      <!--List of CMP fields. These are the columns in the database table-->
      <cmp-field><field-name>id</field-name></cmp-field>
      <cmp-field><field-name>name</field-name></cmp-field>
      <cmp-field><field-name>rank</field-name></cmp-field>
      <primkey-field>id</primkey-field> <!--Among the above CMP fields, id is our primary field-->
      <reentrant>False</reentrant>
      <cmp-version>2.x</cmp-version>
      <abstract-schema-name>ChessPlayer</abstract-schema-name>
   </entity>
</enterprise-beans>
...

Our entity bean ChessPlayer has three fields: id, name, and rank. All three of these have to be Serializable. If you want to store a custom-defined class Object, then you should lay a Serializable interface on top of it and implement required methods.

jboss.xml:

...
<enterprise-beans>
   <entity>
      <ejb-name>ChessPlayer</ejb-name>
      <local-jndi-name>ChessPlayer</local-jndi-name>
   </entity>

   <session>
      <ejb-name>ChessHost</ejb-name>
      <jndi-name>ChessHost</jndi-name>
      <ejb-local-ref>
         <ejb-ref-name>ChessPlayer</ejb-ref-name>
         <jndi-name>ChessPlayer</jndi-name>
      </ejb-local-ref>
   </session>
</enterprise-beans>
...

As you may have noticed, the directory structure for our application is slightly different from the one in O'Reilly workbook examples:

project-folder -- /src -- /META-INF -- jboss.xml, ejb-jar.xml
                  
     -- /chess -- /ejb        -- ChessPlayerBean.java, ChessHostBean.java
                  
               -- /interfaces -- ChessPlayerHome.java, ChessPlayer.java, ChessHostHome.java, ChessHost.java
                                 -- /client     -- client.java


Home and Remote Interfaces

Home interfaces are used to create and find beans. For example, inside ChessPlayerHome.java, you should have create and findByPrimaryKey methods. Remote interfaces are for everything else. For ChessPlayer.java, CMP field access methods like getID and setID should be included. For ChessHost.java, any method you want the client to be able to invoke should be included.


Bean Definitions

For entity beans, JBoss defines CMP field accessors for you. All you have to do is declare them as abstract. For our ChessPlayer entity bean definition, you should have the following lines in ChessPlayerBean.java:

public abstract String getId();
public abstract void setId(String str);
public abstract String getName();
public abstract void setName(String str);
public abstract Integer getRank();
public abstract void setRank(Integer rank);

Keep in mind that everything that goes into the database has to be of type Object and Serializable. This means that you can't use primitives (such as int) when dealing with the mySQL database. Notice how getRank() returns Integer, not int.


Running Your Application

If you want to use Java I/O input (for example, readline()), then you should run it yourself and not with ant. (You can still use ant to build your jar file).
To run your client, you could write a shell script that looks something like this:

#!/bin/bash

for i in $JBOSS_HOME/client/*; do JBOSS_CLIENT_CLASSPATH=$JBOSS_CLIENT_CLASSPATH:$i; done
export JBOSS_CLIENT_CLASSPATH

for i in $JBOSS_HOME/lib/*; do JBOSS_CLIENT_CLASSPATH=$JBOSS_CLIENT_CLASSPATH:$i; done
export JBOSS_CLIENT_CLASSPATH

for i in $JBOSS_HOME/server/default/lib/*; do JBOSS_CLIENT_CLASSPATH=$JBOSS_CLIENT_CLASSPATH:$i; done
export JBOSS_CLIENT_CLASSPATH

java -classpath .:$CLASSPATH:./jndi:$JBOSS_CLIENT_CLASSPATH:./build/[your_jar_file_name].jar chess.client.client

TIP: Later, when you implement fault-tolerance, DO NOT try to start more than one server at a time. Starting multiple servers at the same time will give you weird runtime errors on the serverside.


That's it! I hope this tutorial has helped you in jump-starting with EJB.