http://www.developer.com/

Back to article

Creating and Deploying Stateless Session Beans and CMP Entity Beans


September 9, 2004

From the very beginning, I would like to notify you that this article is unusual. I believe we should call it a "manual," not an "article." In this document, we will provide the instructions needed to create a very simple (but still working) stateless session bean, a simple CMP (Container Managed Persistence) entity bean, then deploy everything on a JBoss application server. Because we are creating an entity bean, we will need a persistence store—a database. In our case, it will be MySQL database server. This article will explain how to "teach" JBoss to support CMP on MySQL.

On this initial point, I am eager to warn you that you have to be familiar with J2EE programming, you need to know the basics of EJB programming, you need at least to understand that JBoss is an application server, and that MySQL is a database server. If you have some troubles with these nuances, I suggest that you to skip reading this article and come back only when you feel yourself ready. In general, this article is created to explain how to apply your knowledge of EJB into "real life," and how to create some working stuff. But, this article will not teach you all these technologies.

And at last, to finish with the preparatory part, I will list all the software that is required for us, so we will be able to check up on material given on practice in our article. We will require JBoss 3.2.3 application server (it comes with Tomcat 4.1, so we do not need separated a Web container), MySQL 4.0.16 database server, Apache Ant 1.6.2—a Java-based build tool, and of course any simpliest text editor to copy and paste source code from this article. All software is free; you can download it easily from the companies' official Web sites. Before we will start, make sure you have downloaded everything and properly installed and configured all paths (PATH and CLASSPATH variables). This process is beyond the scope of our article, so we are leaving all these requirements to you.

First, our task will be creating Ant's build.xml, a so-called "make file," which will have all rules and instructions on how to compile, pack, and deploy all our stuff. Here it is; put it in the root directory, where you will be placing all code:

<project name="teste" default="ear">
   <!-- define our environment -->
   <property name="jboss.home"   value="C:\jboss-3.2.3"/>
   <property name="jboss.server.dir"
             value="${jboss.home}\server\default"/>
   <property name="jboss.deploy.dir"
             value="${jboss.server.dir}\deploy"/>
   <property name="source.dir"   value="src"/>
   <property name="classes.dir"  value="classes"/>
   <property name="app.ear"
             value="${ant.project.name}.ear"/>
   <property name="app.war"
             value="${ant.project.name}.war"/>
   <property name="app.ejb.jar"
             value="${ant.project.name}-ejb.jar"/>
   <!-- ---------------------- -->
   <path id="compile.class.path">
      <pathelement location="${jboss.server.dir}\lib\jboss-j2ee.jar"/>
   </path>
   <target name="all" depends="clean,ear,deploy"/>
   <target name="compile">
      <mkdir dir="${classes.dir}"/>
      <javac srcdir="${source.dir}" destdir="${classes.dir}"
             classpathref="compile.class.path" debug="yes"/>
      <copy todir="${classes.dir}">
         <fileset dir="${source.dir}">
            <include name="META-INF/**"/>
         </fileset>
      </copy>
   </target>
   <target name="jar" depends="compile">
      <jar jarfile="${app.ejb.jar}">
         <fileset dir="${classes.dir}">
            <include name="META-INF/jbosscmp-jdbc.xml"/>
            <include name="META-INF/ejb-jar.xml"/>
            <include name="META-INF/jboss.xml"/>
            <include name="META-INF/mysql-ds.xml"/>
            <include name="**/*.class"/>
         </fileset>
      </jar>
   </target>
<target name="war">
      <jar jarfile="${app.war}">
         <fileset dir="${source.dir}">
            <include name="WEB-INF/web.xml"/>
            <include name="*.jsp"/>
         </fileset>
      </jar>
   </target>
   <target name="ear" depends="jar,war">
      <jar jarfile="${app.ear}">
         <fileset dir="${classes.dir}">
            <include name="META-INF/application.xml"/>
            <include name="${app.ejb.jar}"/>
         </fileset>
         <fileset dir=".">
            <include name="${app.ejb.jar}"/>
            <include name="${app.war}"/>
         </fileset>
      </jar>
   </target>
   <target name="deploy">
      <copy file="${app.ear}" todir="${jboss.deploy.dir}"/>
   </target>
   <target name="undeploy">
      <delete file="${jboss.deploy.dir}/${app.ear}" quiet="true"/>
   </target>
   <target name="clean">
      <delete includeEmptyDirs="true" quiet="true">
         <fileset dir=".">
            <include name="*.jar"/>
            <include name="*.war"/>
            <include name="*.ear"/>
         </fileset>
         <fileset dir="${classes.dir}"/>
      </delete>
   </target>
</project>

Just create this file, save it, and forget about it for now. Honestly speaking, you can forget about it at all. As rough as it sounds, you do not necessarily need to be an Ant guru and keep all the structures of an XML configuration file in your head to use all the advantages of the Ant build tool. I do not remember them because I already have skeleton of build.xml and I use it almost all the time. Of course, when I require some other features, I just open Ant's documentation, look for the functions that I need, and use them. That's all. I suggest that you do the same if you do not have the time or desire to learn everything from the very beginning.

Now it's time to create all the directories where we will place sources and configuration files. So, they are src/, src/WEB-INF/, src/META-INF/, and src/teste/.

The next step will be creating clients for our session stateless bean and for CMP entity bean. Yes, we do not have these EJBs ready yet, but they will be ready in few minutes, so why should we depend on the order of creating code? So, here they are our clients for the session stateless bean—src/index1.jsp:

<%@page import="java.rmi.*" %>
<%@page import="java.util.*" %>
<%@page import="javax.naming.*" %>
<%@page import="javax.rmi.*" %>
<%@page import="teste.*" %>
<html>
<head><title>Teste</title></head>
<body>
<%
   try {
      Context ic = new InitialContext();
      Object o = ic.lookup("ejb/Example/Teste");
      TesteHome home = (TesteHome)
                PortableRemoteObject.narrow(o, TesteHome.class);
      Teste eo = home.create();
      out.println(eo.say());
   } catch (Exception ex) {
      out.println(ex);
   }
%>
</body>
</html>

and for the CMP entity bean—src/index2.jsp:

<%@page import="java.rmi.*" %>
<%@page import="java.util.*" %>
<%@page import="javax.naming.*" %>
<%@page import="javax.rmi.*" %>
<%@page import="teste.*" %>
<html>
<head><title>Story</title></head>
<body>
<%
   try {
      Context ic = new InitialContext();
      Object o = ic.lookup("ejb/Example/Story");
      StoryHome home = (StoryHome)
                PortableRemoteObject.narrow(o, StoryHome.class);
      Story eo = home.create();
      Collection stories = home.findAll();
      for (Iterator i = stories.iterator(); i.hasNext(); ) {
         Story story = (Story) i.next();
         out.println("<b>"+story.getStoryId()+"</b>"+"
                     "+story.getPubDate()+"<br>");
      }
   } catch (Exception ex) {
      out.println(ex);
   }
%>
</body>
</html>

Let me explain how it works. A session stateless bean itself will be pretty easy. It only has a business method—say(), so our client for this src/index1.jsp bean will run this business method and print the output. That's all. Our entity bean will be a bit more complex. In the src/index2.jsp client, we will create a new entity, and, right after that, we'll grab all entities and will show a list of their fields (they have only two fields—StoryId and PubDate, but you will see that below). So, this will be the only action that our entity bean client does. However, I need to mention that once a CMP entity bean is created, a record in the MySQL database table also will be auto-created. That's the idea of a CMP entity bean. This will be done without any single code line dealing with JDBC operations; not in your client, and neither in your entity bean. You will see how cool it is below.

It's time for beans. First comes the session stateless bean—src/teste/TesteBean.java:

package teste;
import javax.ejb.*;
public class TesteBean implements SessionBean {
   public void setSessionContext(SessionContext ctx) {
      System.out.println("In setSessionContext(...)");
   }
   public void ejbCreate() {
      System.out.println("In ejbCreate()");
   }
   public void ejbActivate() {
      System.out.println("In ejbActivate()");
   }
   public void ejbPassivate() {
      System.out.println("In ejbPassivate()");
   }
   public void ejbRemove() {
      System.out.println("In ejbRemove()");
   }
   public String say() {
      System.out.println("In business method say()");
      return "Hello World! I am just tiny Stateless Session Bean!";
   }
}

Now comes the session bean's remote component interface—src/teste/Teste.java:

package teste;
import javax.ejb.*;
import java.rmi.RemoteException;
public interface Teste extends EJBObject {
   public String say() throws RemoteException;
}

And the session bean's home interface—src/teste/TesteHome.java:

package teste;
import javax.ejb.*;
import java.rmi.RemoteException;
public interface TesteHome extends EJBHome {
   public Teste create() throws CreateException, RemoteException;
}

As mentioned above, there is only one business method, say(), and it does nothing except returning the String value. Okay, once you havecopy-pasted and saved everything, we smoothly come to the entity bean.

The same is true for the session bean. Here comes the entity bean source code—src/teste/StoryBean.java:

package teste;
import java.rmi.*;
import java.util.*;
import javax.ejb.*;
public abstract class StoryBean implements EntityBean {
   private EntityContext ctx;
   private Integer generatePrimaryKey() {
      System.out.println("In generatePrimaryKey()");
      return new Integer((new Object()).hashCode());
   }
   public abstract Integer getStoryId();
      public abstract void setStoryId(Integer storyId);
   public abstract Date getPubDate();
   public abstract void setPubDate(Date pubDate);
   private void create() {
      System.out.println("In create()");
      setStoryId(generatePrimaryKey());
      setPubDate(new Date());
   }
   public Integer ejbCreate() throws RemoteException, CreateException {
      System.out.println("In ejbCreate()");
      create();
      return null;
   }
   public void ejbPostCreate() {
      System.out.println("In ejbPostCreate()");
   }
   public void ejbRemove() {
      System.out.println("In ejbRemove()");
   }
   public void ejbStore() {
      System.out.println("In ejbStore()");
   }
   public void ejbLoad() {
      System.out.println("In ejbLoad()");
   }
   public void ejbActivate() {
      System.out.println("In ejbActivate()");
   }
   public void ejbPassivate() {
      System.out.println("In ejbPassivate()");
   }
   public void setEntityContext(EntityContext ctx) {
      System.out.println("In setEntityContext(...)");
      this.ctx = ctx;
   }
   public void unsetEntityContext() {
      System.out.println("In unsetEntityContext(...)");
      this.ctx = null;
   }
}
(Please note that the bean is abstract because it has abstract methods for CMP. They will be defined in configuration files; you will see it.) Now, let's look at the remote component interface for the entity bean—src/teste/Story.java:

package teste;
import java.rmi.*;
import java.util.*;
import javax.ejb.*;
public interface Story extends EJBObject {
   public Integer getStoryId() throws RemoteException;
  public Date getPubDate() throws RemoteException;
}

There are only two interesting methods for us—getters for StoryId and PubDate fields. And now, let's take a look at the home interface—src/teste/StoryHome.java:

package teste;
import java.rmi.*;
import java.util.*;
import javax.ejb.*;
public interface StoryHome extends EJBHome {
   public Story create() throws RemoteException, CreateException;
   public Story findByPrimaryKey(Integer key) throws RemoteException,
      FinderException;
   public Collection findAll() throws RemoteException, FinderException;
}

You probably will never believe this, but we are finished with the source code; the only things left are the configuration files for our JAR, WAR, and EAR. But stop! We also need to create a MySQL database, a user for this database, and create a table "for" our entity beans. For this purpose, you can use MySQL Control Center tool (if you are using MySQL on a Windows computer) or the simple mysqladmin tool. Of course, you also can just simply use the next SQL queries to create the database, new user, and table. Here they are:

CREATE DATABASE jbossdb;
INSERT INTO mysql.db VALUES (
'localhost',
'jbossdb',
'userjboss',
'Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y');
INSERT INTO mysql.user VALUES(
'localhost',
'userjboss',
password('zzz'),
'N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N',
   'N','N','N','N','N','','','','',0,0,0);

These queries will help you to create the database (first query), make user userjboss able to work with the newly created database jbossdb (second query), and create this new user userjboss with a password of zzz (third query).

Now, use the following query to create the table:

USE jbossdb;
CREATE TABLE story (
   story_id integer, 
   pub_date timestamp
);

Okay; now databases, user, tables—everything seems to be ready. Let's continue with the configuration files for JBoss. We need to create src/WEB-INF/web.xml for Tomcat (which is with JBoss). Here it is:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app />

It's empty, yes. We are using JSP, and do not have something to configure and to put into the Web container's configuration file. But we need to have it because it's required by the J2EE spec (of course, you know that!).

Now, we will create the next configuration files—src/META-INF/application.xml:

<?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>Teste and Story</display-name>
   <!-- EJB module -->
   <module>
      <ejb>teste-ejb.jar</ejb>
   </module>
      <!-- Web module -->
   <module>
      <web>
         <web-uri>teste.war</web-uri>
         <context-root>teste</context-root>
      </web>
   </module>
</application>

and the configuration file—src/META-INF/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>
   <enterprise-beans>
      <session>
         <ejb-name>Teste</ejb-name>
         <jndi-name>ejb/Example/Teste</jndi-name>
      </session>
      <entity>
         <ejb-name>Story</ejb-name>
         <jndi-name>ejb/Example/Story</jndi-name>
      </entity>
   </enterprise-beans>
   <resource-managers />
</jboss>

Here, we define JNDI names for our session stateless bean and CMP entity bean. The names are pretty easy, so if you even never saw this configuration file before, you will be able to understand everything using your own simple logic.

The next configuration file is—src/META-INF/ejb-jar.xml:

<?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>Teste and Story</description>
   <display-name>Teste and Story</display-name>
   <enterprise-beans>
      <session>
         <display-name>Teste Session Stateless Bean</display-name>
         <ejb-name>Teste</ejb-name>
         <home>teste.TesteHome</home>
         <remote>teste.Teste</remote>
         <ejb-class>teste.TesteBean</ejb-class>
         <session-type>Stateless</session-type>
         <transaction-type>Container</transaction-type>
      </session>
      <entity>
         <display-name>Story Entity CMP Bean</display-name>
         <ejb-name>Story</ejb-name>
         <home>teste.StoryHome</home>
         <remote>teste.Story</remote>
         <ejb-class>teste.StoryBean</ejb-class>
         <reentrant>False</reentrant> 
         <persistence-type>Container</persistence-type>
         <cmp-version>2.x</cmp-version>
         <abstract-schema-name>story</abstract-schema-name>
         <primkey-field>storyId</primkey-field>
         <prim-key-class>java.lang.Integer</prim-key-class>
         <cmp-field><field-name>storyId</field-name></cmp-field>
         <cmp-field><field-name>pubDate</field-name></cmp-field>
      </entity>
   </enterprise-beans>
   <assembly-descriptor />
</ejb-jar>

I want you to pay attention to the <entity> section. Just take a look and keep in your mind the last four lines in this section. We are defining the primary key, and describing fields of our entity bean.

Here is next configuration file—src/META-INF/jbosscmp-jdbc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jbosscmp-jdbc PUBLIC 
"-//JBoss//DTD JBOSSCMP-JDBC 3.0//EN" 
"http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_0.dtd">
<jbosscmp-jdbc>
   <defaults>
      <datasource>java:/MySqlDS</datasource>
      <datasource-mapping>mySQL</datasource-mapping>
      <create-table>false</create-table>
      <remove-table>false</remove-table>
      <pk-constraint>true</pk-constraint>
      <preferred-relation-mapping>foreign-key</preferred-relation-mapping>
   </defaults>
   <enterprise-beans>
      <entity>
         <ejb-name>Story</ejb-name>
         <table-name>story</table-name>
         <cmp-field>
            <field-name>storyId</field-name>
            <column-name>story_id</column-name>
            <not-null/>
         </cmp-field>
         <cmp-field>
            <field-name>pubDate</field-name>
            <column-name>pub_date</column-name>
            <not-null/>
         </cmp-field>
      </entity>
   </enterprise-beans>
</jbosscmp-jdbc>

Don't worry about the first part of this file, but again, pay attention to the bottom part. Here, we are defining what each field from entity bean means compared to the database table. So, as you can see, we are saying that the storyId field is story_id column from our table, and pubDate is pub_date in the database. Besides, all descriptions of fields are informative enough, and do not demand further explanation. Certainly, for a deeper understanding of their essence, you should study the EJB specification, but that's beyond the scope of this article.

And one last configuration file is src/META-INF/mysql-ds.xml. You can take it from C:\jboss-3.2.3\docs\examples\jca\mysql-ds.xml (we assume that JBoss is installed in this directory) and modify it to have the correct database name, user's name, and password. Or, you can just use the following one:

<?xml version="1.0" encoding="UTF-8"?>
<!-- ========================================================== -->
<!--                                                            -->
<!--  JBoss Server Configuration                                -->
<!--                                                            -->
<!-- ========================================================== -->
<!-- $Id: mysql-ds.xml,v 1.1 2002/07/22 22:57:24 d_jencks Exp $ -->
<!-- ========================================================== -->
<!--  Datasource config for MySQL using 2.0.11 driver           -->
<!-- ========================================================== -->
<datasources>
   <local-tx-datasource>
      <jndi-name>MySqlDS</jndi-name>
      <connection-url>jdbc:mysql://localhost:3306/jbossdb
         </connection-url>
      <driver-class>org.gjt.mm.mysql.Driver</driver-class>
      <user-name>userjboss</user-name>
      <password>zzz</password>
      </local-tx-datasource>
</datasources>

Well, we are almost done. We need to compile everything, create JAR, WAR, and EAR, and deploy. Please check that JBoss is running, so you will see in its logs how everything will be deployed. Change the directory to the root directory where you placed all files, where build.xml is located, and type:

ant all

If everything was done correctly, you should see the same screen as the one below:



Click here for a larger image.

Please check your JBoss logs and you will see that everything was deployed correctly (like below):



Click here for a larger image.

This is a really huge portion of different debug information; do not worry about understanding it all. Just check that everything was deployed correctly, with no errors. And now, at least, when everything is deployed, you can open your favourite browser and type this next URL, http://localhost:8080/teste/index1.jsp, and you will see the following screen:



Click here for a larger image.

Then, type http://localhost:8080/teste/index2.jsp and you will get almost the same screen (yup, I've run this page many times, so I already have a lot of entities created!). On the left, and in bold, you will see the entities' ID and the creation date; all fields that have an entity bean.



Click here for a larger image.

Of course, now you will want to check that these entities "forced" to create equal records in the MySQL database. If you will execute any database client and execute the next query:

SELECT * FROM jbossdb.story;

you will see the following:



Click here for a larger image.

Oh, well! My congratulations; we succeeded! As you can see, applications that use CMP entity beans quickly become complex. The source code itself remains simple and easy, but the configuration files for the application server still make problems for newbies and even for rather experienced developers. But, from my own experience, I know that sometimes it's much easier to see something in practice, force it to work, and only after that get deeper into understanding all the parts.

I hope that this article will help you to understand how to use the JBoss application server in practice, how to deploy a session bean, and how to make a CMP entity bean work with help of MySQL. EJB technology possesses a lot of advantages and various unbelievable opportunities. It is important to learn how to use them in practice, even with the help of the simplest applications.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date