Getting Started with OpenEJB

Simple is good. But working in Java can get to be complicated. Which is why I’ve started exploring OpenEJB, an open source, modular, configurable, and extendable EJB Container System and EJB Server. OpenEJB peels away some of the obfusticating java infrastructure and gets down to the bare code.

“Hello World” is the canonical beginning program, and the EJB version comes as a Stateless Session Bean. The bean is 99% boilerplate, so I’ll skip that and go straight to the ant build file. But everything is included in the zip distribution.

Before we get started, I assume that you are familiar with ant. If you don’t, then may I suggest that you start with my Ant tutorials. Just in case there is a version specific problem, here is my Java Software configuration. And windows users are forewarned that there is one required change [that I know of].

My build file starts out with some property assignments. You must change the OPENEJB property to reflect the location of your OpenEJB distribution. The remaining properties should be fine. Note that I’m using two properties for the EJB jar file, JarName and BeanJar – JarName is the jar basename and BeanJar is a location bound to the full path.

1
2
3
4
5
6
7
<property name="src-dir"  location="src" />
<property name="obj-dir" location="obj" />
<property name="TALK" value="false" />
<property name="DEBUG" value="true" />
<property name="OPENEJB" location="/Projects/Java/openejb" />
<property name="JarName" value="hello.jar" />
<property name="BeanJar" location="${JarName}" />

Next, I define three classpaths. The first classpath is used to compile the driver class and the Session Bean class and interfaces. The second classpath is used when executing against a remote server. And the third classpath is used when executing against a local server. The execution classpaths were determined by trial and error and may be missing a jar file or two.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<path id="classpath.base">
<pathelement location="${obj-dir}" />
<pathelement location="${OPENEJB}/lib/ejb-2.0.jar" />
</path>
<path id="classpath.runRemote">
<path refid="classpath.base" />
<pathelement location="${OPENEJB}/dist/openejb_client-0.9.2.jar" />
<pathelement location="${OPENEJB}/dist/openejb-0.9.2.jar" />
</path>
<path id="classpath.runLocal">
<path refid="classpath.base" />
<fileset dir="${OPENEJB}/dist" includes="*.jar" />
<pathelement location="${OPENEJB}/lib/log4j-1.2.1.jar" />
<pathelement location="${OPENEJB}/lib/jta_1.0.1.jar" />
<pathelement location="${OPENEJB}/lib/jca_1.0.jar" />
<pathelement location="${OPENEJB}/lib/castor-0.9.3.9.jar" />
<pathelement location="${OPENEJB}/lib/castor-0.9.3.9-xml.jar" />
<pathelement location="${OPENEJB}/lib/xercesImpl-2.0.2.jar" />
<pathelement location="${OPENEJB}/lib/jakarta-regexp-1.1.jar" />
<pathelement location="${OPENEJB}/lib/idb_3.26.jar" />
</path>

Targets to build the object directory, compile the java source into the object directory, and clean it all up.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<target name="init">
<mkdir dir="${obj-dir}" />
</target>
<target name="compile" depends="init">
<javac srcdir="${src-dir}"
destdir="${obj-dir}"
debug="${DEBUG}"
>

<classpath refid="classpath.base" />
</javac>
</target>
<target name="clean-compile">
<delete verbose="${TALK}" dir="${obj-dir}" />
</target>

A target to build our EJB jar file. Note that it includes both the object code and the META-INF directory. And the companion target to clean up.

1
2
3
4
5
6
7
8
9
10
11
12
<target name="jar" depends="compile">
<jar destfile="${BeanJar}">
<fileset dir="${obj-dir}"
includes="org/**" />

<fileset dir="${src-dir}"
includes="META-INF/**"
/>

</jar>
</target>
<target name="clean-jar">
<delete verbose="${TALK}" file="${BeanJar}" />
</target>

A couple of targets to deploy the jar file to OpenEJB. In the deploy-check target, we use the uptodate task to compare the deployed jar in ${OPENEJB}/beans to our local jar and set the deploy.notNeeded property for the deploy target. Note that ${BeanJar} is a location and early bound to the full path name, and ${JarName} is a normal property represents the jar basename.

The deploy target executes the openejb deploy command unless deploy.notNeeded is set in the deploy-check target. This build file uses the unix executable script, openejb.sh. Changing to openejb.bat should work on windows, but I haven’t tested it. We use the following deploy options:

  • -a: automate deployment as much as possible.
  • -c: copy the jar rather than move (-m).
  • -f: force the move/copy, overwriting an existing jar with the same name.
1
2
3
4
5
6
7
8
9
10
11
<target name="deploy-check">
<uptodate property="deploy.notNeeded"
targetfile="${OPENEJB}/beans/${JarName}"
srcfile="${BeanJar}"
/>

</target>
<target name="deploy" depends="jar,deploy-check" unless="deploy.notNeeded">
<exec dir="${OPENEJB}" executable="${OPENEJB}/openejb.sh">
<arg line="deploy -a -c -f ${BeanJar}"/>
</exec>
</target>

And a pair of targets for execution against a remote and a local OpenEJB server. The choice between local and remote servers is defined by the Properties object used in creating the InitialContext object. We provide the appropriate properties as system properties.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<target name="remote" depends="deploy">
<java classname="HelloDriver"
fork="true">

<classpath refid="classpath.runRemote"/>
<sysproperty key="java.naming.factory.initial"
value="org.openejb.client.RemoteInitialContextFactory" />

<sysproperty key="java.naming.provider.url" value="127.0.0.1:4201" />
<sysproperty key="java.naming.security.principal" value="me" />
<sysproperty key="java.naming.security.credentials" value="me" />
</java>
</target>

<target name="local" depends="deploy">
<java classname="HelloDriver"
fork="true">

<classpath refid="classpath.runLocal"/>
<sysproperty key="openejb.home" value="${OPENEJB}" />
<sysproperty key="java.naming.factory.initial"
value="org.openejb.client.LocalInitialContextFactory" />

</java>
</target>

The HelloDriver class with our main program. The build file supplies the OpenEJB properties as system properties and the code just passes them on to the InitialContext constructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import org.acme.*;

import java.util.Properties;
import java.rmi.*;
import javax.ejb.*;
import javax.rmi.*;
import javax.naming.*;


public class HelloDriver
{


public static void main( String[] args )
{

try {
InitialContext ctx = new InitialContext( System.getProperties() );
Object obj = ctx.lookup( "Hello" );
HelloHome home = (HelloHome)
PortableRemoteObject.narrow( obj,HelloHome.class );

Hello hello = home.create();
System.out.println( hello.hello() );
}
catch ( NamingException ne ) {
ne.printStackTrace();
}
catch ( CreateException ce ) {
ce.printStackTrace();
}
catch ( RemoteException re ) {
re.printStackTrace();
}
}

}
//
// $Id: HelloDriver.java,v 1.1.1.1 2004/01/26 19:26:08 dwight Exp $
//

Now we can deploy the Bean to OpenEJB:

1
$ ant deploy
Buildfile: build.xml

init:
    [mkdir] Created dir: /Projects/Learn/OpenEJB/hello/obj

compile:
    [javac] Compiling 4 source files to /Projects/Learn/OpenEJB/hello/obj

jar:
      [jar] Building jar: /Projects/Learn/OpenEJB/hello/hello.jar

deploy-check:

deploy:
     [exec] --------------SUPPORT INFO-------------
     [exec] Darwin 6.8 Darwin Kernel Version 6.8: Wed Sep 10 15:20:55 PDT 2003;
                                         root:xnu/xnu-344.49.obj~2/RELEASE_PPC 
     [exec] Using JAVA_HOME:     /usr/bin/..
     [exec] Using OPENEJB_HOME:  /Projects/Java/openejb-0.9.2
     [exec] .
     [exec] OpenEJB Deploy Tool 0.9.2    build: 20030605-0409
     [exec] http:/\/openejb.sf.net

     [exec] This jar contains the following beans:
     [exec]   Hello


     [exec] -----------------------------------------------------------
     [exec] Deploying bean: Hello
     [exec] -----------------------------------------------------------

     [exec] ==--- Step 1 ---==

     [exec] Auto assigning the ejb-name as the deployment id for this bean.

     [exec] Deployment ID: Hello
     [exec] ==--- Step 2 ---==

     [exec] Auto assigning the container the bean will run in.

     [exec] Container: Default Stateless Container

     [exec] -----------------------------------------------------------
     [exec] Done collecting deployment information!
     [exec] Creating the openejb-jar.xml file...done
     [exec] Writing openejb-jar.xml to the jar...done

     [exec] Congratulations! Your jar is ready to use with OpenEJB.

     [exec] If the OpenEJB remote server is already running, you will
     [exec] need to restart it in order for OpenEJB to recognize your bean.

     [exec] NOTE: If you move or rename your jar file, you will have to
     [exec] update the path in this jar's deployment entry in your 
     [exec] OpenEJB config file.



BUILD SUCCESSFUL
Total time: 22 seconds

Execute against a Remote Server (Don’t forget to start the OpenEJB Server).

1
$ ant remote
Buildfile: build.xml

init:

compile:

jar:

deploy-check:

deploy:

remote:
     [java] Hello, World!



BUILD SUCCESSFUL
Total time: 9 seconds
And execute against a Local Server.

$ ant local
Buildfile: build.xml

init:

compile:

jar:

deploy-check:

deploy:

local:
     [java] OpenEJB 0.9.2    build: 20030605-0409
     [java] http:/\/openejb.sf.net
     [java] Hello, World!



BUILD SUCCESSFUL
Total time: 1 minute 4 seconds

Download the code for this OpenEJB Stateless Session Bean and get started on something a bit more substantial.

Disclaimer: I don’t claim to be an expert on OpenEJB. Please send comments and corrections.