Hello OpenEJB World! -- A basic CMP entity bean example

|  |  |  | OpenEJB OpenJMS OpenORB Castor Tyrex |

Main
Welcome!
Download
Mailing Lists
The Team

Users
Quickstart
Hello World!
CMP Example
CMP Guide
Deploy
Startup
Validation
Configuration
Support
Request Feature
FAQ

Servers
Local Server
Remote Server
Tomcat

Integrators
Why OpenEJB
Overview
Design
Specification
Presentation

Developers
Custom Services
Release Plan
Source Code
SourceForge

Hello OpenEJB World! A basic CMP entity bean example

1Abstract
2Before starting
3Create the entity bean class
4Create the entity bean's home interface
5Create the entity bean's remote interface
6Create the ejb-jar.xml
7Compile the EJB
8Package the EJB
9Create necessary structures in the database (e.g. PostgreSQL)
10Configure the data source
11Deploy the EJB jar
12A basic client application
13Compile the application
14Run it!
15What if it didn't work

## Abstract

The aim of this document is to help you out with deploying container-managed persistence (CMP) entity beans into OpenEJB. The database being used is PostgreSQL 7.2.1, but the steps are described in such a way that it should be very easy to switch over to another database system (chances are that when you send a request to the OpenEJB user mailing list about how to enable your preffered RDBMS in OpenEJB, someone, possibly from the development group, will pick it up and modify the document, accordingly).

The first sections deal with developing a CMP entity bean. It is a CMP entity's view of Employee.You may skip them if your bean is ready and waiting to be deployed.

We will be configuring PostgreSQL afterwards and creating the table and sequence the entity bean is to be mapped to. OpenEJB interacts with databases and XML files with the help of Castor. Although Castor plays a great role in OpenEJB the document doesn't assume you are already familiar with it nor will it be described here thoroughly. It is strongly adviced to learn more about Castor, indeed. Please visit Castor home page to find out more.

A few words are aimed to rise your awareness of the structure of OpenEJB configuration file - openejb.conf - and how to modify it. That is going to be a very broad overview and some facts are skipped for the sake of simplicity.

The last but not least step will be to start up everything (i.e. OpenEJB, PostgreSQL) and access the Employee entity bean from the out-of-process client (i.e. the client, which is outside of JVM OpenEJB runs in).

Everything was tested on MS Windows 2000 with PostgreSQL 7.2.1 installed on Cygwin 1.3.12-2 as well as with Sun JDK 1.3.1_04 and PostgreSQL JDBC driver.

## Before starting

This example assumes you have already followed the steps outlined in the basic EJB example.

## Create the entity bean class

First, create the package where we will place our ejb and application files.

C:\> cd \my\app
C:\my\app> mkdir org\acme\employee

In your favorite editor, create the file below.

C:\my\app\org\acme\employee\EmployeeBean.java

    package org.acme.employee;

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

    public class EmployeeBean implements EntityBean
    {
      public int id;
      public String firstname;
      public String lastname;
      public String email;
      public EntityContext context;

      public EmployeeBean()
      {
      }

      public Integer ejbCreate( String firstname, String lastname, String email )
        throws CreateException
      {
        this.firstname = firstname;
        this.lastname = lastname;
        this.email = email;
        return null;
      }

      public void ejbPostCreate( String firstname, String lastname, String email )
        throws CreateException
      {
      }

      public void says( String message )
      {
        System.out.println( "[" + firstname + " " + lastname +
              "(" + id + ") " + email + "] " + message );
      }

      public void setEntityContext( EntityContext context )
        throws EJBException, RemoteException
      {
        this.context = context;
      }

      public void unsetEntityContext()
        throws EJBException, RemoteException
      {
      }

      public void ejbActivate()
        throws EJBException, RemoteException
      {
      }

      public void ejbLoad()
        throws EJBException, RemoteException
      {
      }

      public void ejbPassivate()
        throws EJBException, RemoteException
      {
      }

      public void ejbRemove()
        throws RemoveException, EJBException, RemoteException
      {
      }

      public void ejbStore()
        throws EJBException, RemoteException
      {
      }
    }

## Create the entity bean's home interface

In your favorite editor, create the file below.

C:\my\app\org\acme\employee\EmployeeHome.java

    package org.acme.employee;

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

    public interface EmployeeHome extends EJBHome
    {
      public Employee create( String firstname, String lastname, String email )
        throws RemoteException, CreateException;

      public Employee findByPrimaryKey( Integer id )
        throws RemoteException, FinderException;
    }

## Create the entity bean's remote interface

In your favorite editor, create the file below.

C:\my\app\org\acme\employee\Employee.java

    package org.acme.employee;

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

    public interface Employee extends EJBObject
    {
      public void says( String message )
        throws RemoteException;
    }

## Create the ejb-jar.xml

C:\my\app> mkdir META-INF

And finally it's time for the deployment descriptor of the entity bean. In your favorite editor, create the file below.

C:\my\app\META-INF\ejb-jar.xml

    <?xml version="1.0"?>
    <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"
                             "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
    <ejb-jar>
      <enterprise-beans>
        <entity>
          <ejb-name>EmployeeBean</ejb-name>
          <home>org.acme.employee.EmployeeHome</home>
          <remote>org.acme.employee.Employee</remote>
          <ejb-class>org.acme.employee.EmployeeBean</ejb-class>
          <persistence-type>Container</persistence-type>
          <prim-key-class>java.lang.Integer</prim-key-class>
          <reentrant>False</reentrant>
          <cmp-field>
            <field-name>id</field-name>
          </cmp-field>
          <cmp-field>
            <field-name>firstname</field-name>
          </cmp-field>
          <cmp-field>
            <field-name>lastname</field-name>
          </cmp-field>
          <cmp-field>
            <field-name>email</field-name>
          </cmp-field>
          <primkey-field>id</primkey-field>
          <resource-ref>
            <res-ref-name>jdbc/postgresql</res-ref-name>
            <res-type>javax.sql.DataSource</res-type>
            <res-auth>Container</res-auth>
          </resource-ref>
        </entity>
      </enterprise-beans>
      <assembly-descriptor>
        <container-transaction>
          <method>
            <ejb-name>EmployeeBean</ejb-name>
            <method-name>*</method-name>
          </method>
          <trans-attribute>Supports</trans-attribute>
        </container-transaction>
      </assembly-descriptor>
    </ejb-jar>

## Compile the EJB

C:\my\app> javac -d . -classpath %OPENEJB_HOME%\lib\ejb-1.0.jar org\acme\employee\*.java

## Package the EJB

C:\my\app> jar -cvMf employee.jar org/acme/employee/*.class META-INF

## Create necessary structures in the database (e.g. PostgreSQL)

The procedure outlined here describes how to prepare PostgreSQL to work with the Employee entity bean. It basically shows the SQL statements to create the structures: a table and sequence. As sequences are a few RDBMS' feature you will have to work it around if your chosen database doesn't support it or try out PostgreSQL. Will it ever be a better time to find out what PostgreSQL is really able to do?

Firstly, let's start up PostgreSQL. It's assumed you followed the procedures on how to configure PostgreSQL to allow external users connect to the system, and a database is already created. Just to make sure we are on the same track from now on the name of the database will be reffered to as mydb, and the user and its password are username and password, respectively.

Now, please connect to the database (i.e. invoke psql mydb username) and run the following SQL scripts:

    create table employee (
        id integer primary key,
        first_name varchar(15),
        last_name varchar(15),
        email varchar(30));
    create sequence employee_seq;

## Configure the data source

Having done everything up to that point has showed you really nothing about OpenEJB itself. As you might have guessed, we're about to change it and see how different files influence the way OpenEJB works.

When OpenEJB starts up, the default configuration file is %OPENEJB_HOME%\conf\openejb.conf. We will, however, be using another one - our own configuration file. We're doing it because:

| it shall get you familiar with the configuration file's elements
| it shall avoid bothering you with log messages that do not pertain to our example
| it shall get you a feeling of how easy (or not) is to manage OpenEJB

Thus, here goes the configuration file - openejb.xml - which will satisfy the aforementioned requirements.

Create the file in the conf directory.

C:\my\app> mkdir conf

In your favorite editor, create the file below.

C:\my\app\conf\openejb.xml

    <?xml version="1.0"?>

    <openejb>
      <Container id="Default CMP Container" ctype="CMP_ENTITY">
        Global_TX_Database  c:/my/app/conf/postgresql.cmp_global_database.xml
        Local_TX_Database   c:/my/app/conf/postgresql.cmp_local_database.xml
      </Container>
      <Deployments jar="c:/my/app/employee.jar"/>
      <Connector id="Default JDBC Database">
        JdbcDriver org.postgresql.Driver
        JdbcUrl jdbc:postgresql://localhost/mydb
        UserName username
        Password password
      </Connector>
      <SecurityService id="Default Security Service"/>
      <TransactionService id="Default Transaction Manager"/>
    </openejb>

Before we go on a few words about the structure of the file.

Container - there are 4 different container types:

| CMP_ENTITY
| BMP_ENTITY
| STATEFUL
| STATELESS

The type of a container determines what capabilities it provides. The Employee entity bean is a CMP entity bean so the chosen container is of the CMP_ENTITY type.

Since entity beans represent (in most cases) records in a table there must be a way to point an EJB container to it. OpenEJB does it via Global_TX_Database and Local_TX_Database parameters. The values of the parameters are configuration files which are specific to the underlaying platform OpenEJB depends on - Castor. Their structure is hopefully self-explanatory thus the document will not explain it in more details.

These files are shown right after the bullet points below.

Deployments - the element specifies the locations of beans to be deployed onto OpenEJB. The element may have only one of the two attributes (they are mutually exclusive):

| jar - points to one particular bean
| dir - points to a directory that contains beans

The locations may be relative or absolute paths, where the former is constructed to be an absolute path with %OPENEJB_HOME% prepended.

Connector - the element allows to specify what parameters to use while connecting to the default JDBC database. The contents of the element consists of lines which begin with the name of the parameters you could have seen before - these constitute the required JDBC parameters to connect to the database, i.e. PostgreSQL.

The id attribute must be "Default JDBC Database" unless you know what changes are required upon changing the value.

| SecurityService and TransactionService - these elements are included in the file because they are required and without them OpenEJB refuses to run. Nothing to configure so we skip them silently.

NOTE

In future versions of OpenEJB, you won't be required to include the SecurityService or TransactionService lines in your config files unless there is some part of them that you wish to configure.

Create the following files in the conf directory using your favorite editor.

C:\my\app\conf\postgresql.cmp_global_database.xml

    <?xml version="1.0"?>

    <database name="Global_TX_Database" engine="postgresql">
      <jndi name="java:comp/env/jdbc/postgresql" />
      <mapping href="c:/my/app//conf/postgresql.cmp_or_mapping.xml" />
    </database>

C:\my\app\conf\postgresql.cmp_or_mapping.xml

    <?xml version="1.0"?>

    <mapping>
      <class name="org.acme.employee.EmployeeBean" identity="id" key-generator="SEQUENCE">
        <map-to table="employee"/>
        <field name="id" type="integer" direct="true">
          <sql name="id" type="integer"/>
        </field>
        <field name="firstname" type="string" direct="true">
          <sql name="first_name" type="varchar" dirty="check"/>
        </field>
        <field name="lastname" type="string" direct="true">
          <sql name="last_name" type="varchar" dirty="check"/>
        </field>
        <field name="email" type="string" direct="true">
          <sql name="email" type="varchar" dirty="check"/>
        </field>
      </class>
    </mapping>

C:\my\app\conf\postgresql.cmp_local_database.xml

    <?xml version="1.0"?>

    <database name="Local_TX_Database" engine="postgresql">
      <driver class-name="org.postgresql.Driver" url="jdbc:postgresql://localhost/mydb">
        <param name="user" value="username"/>
        <param name="password" value="password"/>
      </driver>
      <mapping href="C:\my\app\conf\postgresql.cmp_or_mapping.xml"/>
    </database>

NOTE

As connections to PostgreSQL are being performed through JDBC data source PostgreSQL JDBC2 driver's classes have to be available to OpenEJB. Thus, you need to download from PostgreSQL JDBC2 driver home page and save it in %OPENEJB_HOME%\lib directory as pgjdbc2.jar. The driver will be loaded by OpenEJB at startup.

## Deploy the EJB jar

Use the OpenEJB Deploy Tool to deploy your jar.

C:\my\app> cd %OPENEJB_HOME%
C:\openejb> openejb deploy -a -conf c:\my\app\conf\openejb.xml c:\my\app\employee.jar

The tool will ask for the resource to link the bean's reference to. You should type in 1 as it points to the resource declared in our configuration file which is the "Default JDBC Database", i.e. PostgreSQL.

NOTE

Since the OpenEJB deployment tool writes to your jar file, make sure that no other programs are using it when you deploy (i.e. if you use an editor such as Forte for Java to create the jar file, that editor may still be using it). If you get an error such as "Error in writing existing jar file" close any programs that may be using the jar and try deploying again.

## A basic client application

Create the basic client application to access your Employee entity bean. The client looks up the entity EJBHome object, creates a user, then finds it with findByPrimaryKey and finally compare the references to each other in order to find out whether or not they points to the same EJBObject managed by OpenEJB. The following lines show invocation of the bussines method - says - and at the end removes the bean's representation from the table.

Please refer to the basic EJB example to choose proper InitialContext environment variables.

C:\openejb> cd c:\my\app

Create the file in your favorite editor.

C:\my\app\org\acme\employee\EmployeeClient.java

    package org.acme.employee;

    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.rmi.PortableRemoteObject;
    import java.util.Properties;

    public class EmployeeClient
    {
       public static void main( String[] args )
               throws Exception
       {
          Properties env = new Properties();
          //The JNDI properties you set depend
          //on which server you are using.
          String jndiProvider = "org.openejb.client.RemoteInitialContextFactory";
          env.put( "java.naming.factory.initial", jndiProvider );
          env.put( "java.naming.provider.url", "localhost:4201" );
          env.put( "java.naming.security.principal", "fakeuser" );
          env.put( "java.naming.security.credentials", "fakepass" );
          Context ctx = new InitialContext( env );

          Object obj = ctx.lookup( "EmployeeBean" );
          obj = PortableRemoteObject.narrow( obj, EmployeeHome.class );
          EmployeeHome home = ( EmployeeHome ) obj;

          Employee empl_create = home.create( "Jacek", "Laskowski",
                "OpenEJB-" + System.currentTimeMillis() + "@SF.net" );

          Integer primaryKey = ( Integer ) empl_create.getPrimaryKey();
          Employee empl_find = home.findByPrimaryKey( primaryKey );

          System.out.println( "Are the \"create\" and \"find\" users identical ? "+
                empl_create.isIdentical( empl_find ) );

          empl_find.says( "Hello OpenEJB World!" );
          empl_find.remove();
       }
    }

## Compile the application

C:\my\app> javac -d . -classpath .;%OPENEJB_HOME%/lib/ejb-1.0.jar;%OPENEJB_HOME%/lib/jndi_1.2.1.jar org\acme\employee\EmployeeClient.java

## Run it!

Create the file in your favorite editor.

C:\my\app\RunIt.bat

    @echo off

    set OPENEJB_HOME=C:\openejb
    set PATH=%PATH%;%OPENEJB_HOME%\bin

    set OLD_CLASSPATH=%CLASSPATH%

    set CP=
    for %%i in (%OPENEJB_HOME%\lib\*.jar) do call cp.bat %%i
    for %%i in (%OPENEJB_HOME%\dist\*.jar) do call cp.bat %%i
    set CP=c:\my\app\employee.jar;%CP%

    set CLASSPATH=%JAVA_HOME%\lib\tools.jar;%CP%

    %JAVA_HOME%\bin\java -Dopenejb.home=%OPENEJB_HOME% org.acme.employee.EmployeeClient

    set CLASSPATH=%OLD_CLASSPATH%

Open the command line window and start up OpenEJB with the following command:

C:\> cd %OPENEJB_HOME%
C:\openejb> start openejb start -conf c:\my\app\conf\openejb.xml

Now run the script!

C:\> cd \my\app
C:\my\app> RunIt

You will see the following sample output on the out-of-process client's console:

    C:\my\app>RunIt
    Are the "create" and "find" users identical ? true

And the following on the OpenEJB server's console:

    OpenEJB Remote Server 0.8.1    build: 20020731-0133
    http://openejb.sf.net
    ----------------STARTUP----------------
    [init] OpenEJB Container System
    [init] OpenEJB Remote Server
      ** Starting Services **
      NAME             IP              PORT
      ejb server       127.0.0.1       4201
      admin console    127.0.0.1       4200
    -----------------INFO------------------
    To log into the admin console, telnet to:
     telnet 127.0.0.1 4200
    ---------------------------------------
    Ready!
    [FirstName LastName(1) OpenEJB-1029465232802@SF.net] Hello OpenEJB World !

## What if it didn't work

If you ran into any problems, first check your openejb.log file at %OPENEJB_HOME%\openejb.log. Look for any lines that begin with "WARN", "ERROR", or "FATAL".

There is a list of the most common problems with possible solutions:

Problem:

    OpenEJB Remote Server 0.8.1    build: 20020731-0133
    http://openejb.sf.net
    ----------------STARTUP----------------
    [init] OpenEJB Container System
    java.lang.ClassNotFoundException: org.postgresql.Driver
            at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
            at java.security.AccessController.doPrivileged(Native Method)
            ...

Solution: Add the PostgreSQL JDBC2 driver to OpenEJB's CLASSPATH. See the Configure the data source section for more details.

If nothing has helped, email it to the OpenEJB user mailing list and let people know you are using the basic CMP entity bean example.

Java, EJB, JDBC, JNDI, JTA, Sun, Sun Microsystems are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and in other countries. XML, XML Schema, XSLT and related standards are trademarks or registered trademarks of MIT, INRIA, Keio or others, and a product of the World Wide Web Consortium. All other product names mentioned herein are trademarks of their respective owners.