Enterprise JavaBeans (EJB) Guide

Share on:

Table of Contents

Read First

This document was converted from an old file format type and there are some evident proofing and formatting issues. As of 2020, EJB is legacy technology but the tutorial is left here for posterity only.

Overview

Enterprise JavaBeans (EJB) is a server-side component architecture for the development and deployment of distributed object systems for the Java platform. Applications written using the EJB architecture are scalable, transactional, and multi-user secure.

EJB is not a technology that you can test right away like Swing. You have to consider that there is more than one player in the game; an EJB container (usually supplied by 3rd party vendor), a client and the client-jar generated by the 3rd party deployment tool. For this reason, in order to try out my examples it will be also necessary to use Caucho’s Resin 3.0. Although EJB should be portable, there are subtle incompatibilities that make difficult to write truly cross-container examples (including table-mapping information).

Bean types

Entity Bean

  • Represents a thing in a persistent store. Usually represents a row in a table.
  • An entity bean is something.

Session Bean

  • It typically represents a process.
  • A session bean does something.
  • There are two kinds of Session Beans:
    • STATELESS
      • Can’t remember anything between method calls.
      • Forgets about the client once the method call completes.
      • Is more scalable than a statefull session bean.
    • STATEFUL
      • Can remember conversation state or client-specific state.

Message-driven Bean

  • Can listen for messages from a JMS messaging service.
  • Clients never call message-driven beans directly.
  • Has no EJBObject because the requests come from the messaging service.

Session Beans

A session bean represents a single client inside the J2EE server. To access an application that is deployed on the server, the client invokes the session bean’s methods. The session bean performs work for its client, shielding the client from complexity by executing business tasks inside the server. As its name suggests, a session bean is similar to an interactive session. A session bean is not shared–it may have just one client, in the same way that an interactive session may have just one user. Like an interactive session, a session bean is not persistent. (That is, its data is not saved to a database.) When the client terminates, its session bean appears to terminate and is no longer associated with the client.

A “Hello World” Framework

The most basic EJB component requires three files and an XML deployment descriptor. The example is about a “Greeter” component which has only one duty; to return a “Hello World” string to its clients. Components are assembled into a ejb-jar file that includes a META-INF directory in which the XML deployment descriptor is included.

 1    GreeterEjb.jar
 2      |
 3      |-org.garba.sessionbeans/
 4      |          |
 5      |          |-Greeter.class
 6      |          |-GreeterBean.class
 7      |          |-GreeterHome.class
 8      |
 9      |-META-INF/
10           |
11           |-ejb-jar.xml
12        

The Component Interface

The business methods implemented by the Greeter bean go in this interface. The interface extends EJBObject. The Greeter bean must not implement this interface.

When you think about a business component you may want to start by defining the methods it has. The component interface is the place where you define all the methods of your “own”. Then, you implement these methods in the bean class but the bean class DOES NOT INCLUDE the component interface. The container does that because it has to add services in-between.

Greeter.java

 1package org.garba.sessionbeans;
 2
 3import java.rmi.RemoteException;
 4import javax.ejb.EJBObject;
 5
 6public interface Greeter extends EJBObject {
 7
 8public String getGreeting() throws RemoteException;
 9
10} 

Component interface rules:

  • It must import java.rmi.RemoteException
  • It must import java.ejb.*
  • It must extend EJBObject.
  • It must declare one or more business methods that throw a RemoteException.
  • Arguments and return types must be legal RMI-IIOP types (primitive, Serializable, Remote or arrays/collections of these types).
  • Overloaded methods are allowed.
  • Each method must declare a RemoteException.
  • You can declare your own application exceptions as long as they are checked exception (i.e. subclasses of Exception, not of RuntimeException).
  • Business methods names must not begin with the string “ejb”.

The Bean Class

The real business logic goes here. The bean class always implements a Session, Enity or MessageDriven interface.

GreeterBean.java

 1package org.garba.sessionbeans;
 2
 3import javax.ejb.SessionBean;
 4import javax.ejb.SessionContext;
 5
 6public class GreeterBean implements SessionBean {
 7
 8public void ejbActivate() {
 9System.out.println("ejb activate");
10}
11
12public void ejbPassivate() {
13System.out.println("ejb passivate");
14}
15
16public void ejbRemove() {
17System.out.println("ejb remove");
18}
19
20public void setSessionContext(SessionContext ctx) {
21System.out.println("session context");
22}
23
24public String getGreeting() {
25return "Hello World";
26}
27
28/* It does not come from the SessionBean interface */
29
30public void ejbCreate() {
31System.out.print("ejb create");
32}
33
34}

Rules for the session bean class

  • The following methods from the SesssionBean interface must be implemented:
    • void ejbActivate()
    • void ejbPassivate()
    • void ejbRemove()
    • void setSessionContext(SessionContext ctx)
  • Business methods must be declared as public, and must not be declared as final or static.
  • Method names must not begin with the string “ejb”.
  • You must not pass “this” as an argument or return value because the bean itself must never be exposed.
  • Arguments and return types for Remote component interface methods must be legal RMI-IIOP types (primitive, Serializable, Remote or arrays/collections of these types)
  • You don’t have to declare the exceptions declared in the component interface, unless you might actually throw those excpetions from your own methods.
  • You must never declare a RemoteException
  • You must not declare any application exceptions (checked exceptions) that were not declared in the component interface for that matching business method.
  • The bean class is always Serializable even if you don’t see “implements Serializable”
  • The class must be public and cannot be final or abstract.
  • The constructor must be public and without arguments. It is always recommended to let the compiler insert the default constructor given that anyways there’s nothing to be put inside the constructor.

The Home Interface

This interface is used to ask for a reference to the component interface.

GreeterHome.java

 1package org.garba.sessionbeans;
 2
 3import java.rmi.RemoteException;
 4import javax.ejb.CreateException;
 5import javax.ejb.EJBHome;
 6
 7public interface GreeterHome extends EJBHome {
 8
 9public Greeter create() throws CreateException, RemoteException;
10
11}

Home interface rules:

  • It must import java.rmi.RemoteException
  • It must import javax.ejb.*;
  • Declares a create() method that returns the component interface (The object that extendes EJBObject) and inexorably declares a CreateException and RemoteException. (The bean developer may add other checked exceptions)
  • FOR STATELESS SESSION BEANS (this example):
    • There can only be one create() without arguments.
  • FOR STATEFUL SESSION BEANS:
    • There can be multiple create() methods.
    • A no arguments create() is not mandatory.
    • The name of a create methods must begin with “create”. Example: createUser().
    • Arguments must be legal RMI-IIOP types (primitive, Serializable, Remote or arrays/collection containing this type of objects)

The ejb-jar.xml file

Describes the structure of the EJB component; how the three files (Greeter.java, GreeterBean.java, GreeterHome.java) are related.

ejb-jar.xml

 1<?xml version="1.0" encoding="UTF-8"?>
 2<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 
 3'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
 4<ejb-jar>
 5<display-name>Ejb1</display-name>
 6<enterprise-beans>
 7    <session>
 8        <display-name>GreeterBean</display-name>
 9        <ejb-name>GreeterBean</ejb-name>
10        <home>org.garba.sessionbeans.GreeterHome</home>
11        <remote>org.garba.sessionbeans.Greeter</remote>
12        <ejb-class>org.garba.sessionbeans.GreeterBean</ejb-class>
13        <session-type>Stateful</session-type>
14        <transaction-type>Bean</transaction-type>
15        <security-identity>
16            <description></description>
17            <use-caller-identity></use-caller-identity>
18        </security-identity>
19    </session>
20</enterprise-beans>
21</ejb-jar>

The Client

The client could be located in a remote machine. The way to reach the JNDI service the first time depends of the EJB vendor. In this example we assume that the Greeter component has been deployed on a local J2EE SDK 1.3 server

GreeterClient.java

 1import org.garba.sessionbeans.Greeter;
 2import org.garba.sessionbeans.GreeterHome;
 3
 4import javax.naming.Context;
 5import javax.naming.InitialContext;
 6import javax.rmi.PortableRemoteObject;
 7
 8public class GreeterClient {
 9
10public static void main(String[] args) {
11try {
12Context ic = new InitialContext();
13Object o = ic.lookup("greeter");
14GreeterHome home = (GreeterHome) PortableRemoteObject.narrow(o, GreeterHome.class);
15Greeter greeter = home.create();
16System.out.println(greeter.getGreeting());
17} catch (Exception ex) {
18ex.printStackTrace();
19}
20}
21
22}

Result:

Hello World `

Line 12: To do a JNDI lookup, the client must first get an InitialContext which is entry point of the directory hierarchy.
Line 13: The client does a lookup on JNDI, using the logical name under which the bean was deployed.
Line 14: For a Remote home interface, the stub returned from JNDI must be both cast and narrowed.
Line 15: The create() method always returns the component interface and thus a cast is no needed.\

EJBHome Methods Seen by The Client

Along with the create() methods that you supply in the home interface, you also get the following EJBHome methods:

EJBMetaData getEJBMetadata()

Returns the EJBMetaData interface that provides further class information about the bean.\

 1GreeterHome home = (GreeterHome) PortableRemoteObject.narrow(o, GreeterHome.class);
 2EJBMetaData metaData = home.getEJBMetaData();
 3System.out.println("Is session: " + metaData.isSession());
 4System.out.println("Is stateless session: " + metaData.isStatelessSession());
 5System.out.println("Home interface class: " + metaData.getHomeInterfaceClass());
 6// Session beans do not have a primary key
 7if (!metaData.isSession()) {
 8System.out.println("Primary key class: " + metaData.getPrimaryKeyClass());
 9}
10System.out.println("Remote interface class: " + metaData.getRemoteInterfaceClass());

Result:

1Is session: true
2Is stateless session: false
3Home interface class: interface org.garba.sessionbeans.GreeterHome
4Remote interface class: interface org.garba.sessionbeans.Greeter

HomeHandle getHomeHandle()

Return a Serializable object that allows to retrive the home at a later time without needing to go through the JNDI lookup.

 1GreeterHome home = (GreeterHome) PortableRemoteObject.narrow(o, GreeterHome.class);
 2
 3// Prepare a file called saved.dat
 4
 5FileOutputStream fos = new FileOutputStream("saved.dat");
 6ObjectOutputStream dst = new ObjectOutputStream(fos);
 7
 8// Write handle to disk
 9
10HomeHandle homeHandle = home.getHomeHandle();
11dst.writeObject(homeHandle);
12
13dst.close();
14fos.close();
15
16// Read handle from disk
17
18FileInputStream fis = new FileInputStream("saved.dat");
19ObjectInputStream src = new ObjectInputStream(fis);
20
21HomeHandle homeHandleFromDisk = (HomeHandle)src.readObject();
22
23// Get a new working GreeterHome and confirm success
24
25GreeterHome newHome = (GreeterHome) PortableRemoteObject.narrow(homeHandleFromDisk.getEJBHome(), GreeterHome.class);
26Greeter greeter = newHome.create();
27System.out.println(greeter.getGreeting());

Result:

1     Hello World

Once you run this program for the first time, you can comment all the JNDI calls and start straight off from the reading part. If you switch off the application server the handle does not usually get back a working home interface and it is necessary to repeat the whole JNDI lookup process again. Don’t forget to use PortableRemoteObject.narrow instead of just a plain cast.

void remove(Handle h)

Tells the home that you no longer need a session bean.

1Greeter greeter = home.create();
2System.out.println(greeter.getGreeting());
3home.remove(greeter.getHandle());
4System.out.println(greeter.getGreeting()); // Warning! ERROR

Result:

1    Hello World
2    java.rmi.NoSuchObjectException

The error originates after the second access to the component method. Please note that the greeter object doesn’t become null. The stub still exists but has lost its connection with the server.

void remove(Object key)

Deletes the entity from the persistent store. This method cannot be used for session beans. Session and entity beans have a common interface with different remove methods that must be chosen according to the bean type.

EJBObject Methods Seen by The Client

Along with the business methods that you supply in the component interface, you also get the following EJBObject methods:

Object getPrimaryKey()

Obtains the primary key of an entity bean. It doesn’t apply to session beans (produces an exception).

EJBHome getEJBHome()

Returns a reference to the bean’s home. It allows to create more beans if you have lost the home (for example if you have passed the bean to a method) without incurring in the JNDI lookup process.

 1public static void main(String[] args) {
 2try {
 3Context ic = new InitialContext();
 4Object o = ic.lookup("greeter"); 
 5GreeterHome home = (GreeterHome) PortableRemoteObject.narrow(o, GreeterHome.class);
 6Greeter greeter = home.create();
 7sillyMethod(greeter);
 8} catch (Exception ex) {
 9ex.printStackTrace();
10}
11}
12
13public static void sillyMethod(Greeter greeter) {
14try {
15GreeterHome rospoHome = (GreeterHome)greeter.getEJBHome();
16Greeter greeter1 = rospoHome.create();
17Greeter greeter2 = rospoHome.create();
18Greeter greeter3 = rospoHome.create();
19System.out.println(greeter1.getGreeting());
20System.out.println(greeter2.getGreeting());
21System.out.println(greeter3.getGreeting());
22} catch (RemoteException e) {
23e.printStackTrace();
24} catch (CreateException e) {
25e.printStackTrace();
26} 
27}

Result:

1Hello World
2Hello World
3Hello World

Handle getHandle()

Gets a reference to the EJBObject that may be stored to get back to the bean at a later time.

1    GreeterHome home = (GreeterHome) PortableRemoteObject.narrow(o, GreeterHome.class);
2    Greeter greeter = home.create();
3    
4    saveToDisk(greeter.getHandle());
5    Handle handle = (Handle)loadFromDisk();
6    
7    Greeter savedGreeter = (Greeter) PortableRemoteObject.narrow(handle.getEJBObject(), Greeter.class);
8    System.out.println(savedGreeter.getGreeting());

If you want to see how saveToDisk()/loadFromDisk() may be implemented check the EJBHome.getHomeHandle() example.
Never forget to use PortableRemoteObject.narrow() to get the right object.

void remove()

If it is a session bean, the bean becomes no longer active and the container saves resources. If it is an entity bean, the container not only makes the bean inactive, it also erases the information represented by the bean from the persistent store.

1Greeter greeter = home.create();
2System.out.println(greeter.getGreeting());
3greeter.remove();
4System.out.println(greeter.getGreeting()); // Warning! ERROR

Result:

1Hello World
2java.rmi.NoSuchObjectException

The error originates after the second access to the component method. Please note that the greeter object doesn’t become null. The stub still exists but it has lost its connection with the server.

boolean isIdentical(Object o)

Checks whether two EJB objects are equal as seen by the container. The equals() method is inappropriate because it compares two objects on the same HEAP. i.e. the same JVM.

Some rules apply:

  • For stateless session beans: isIdentical() returns true if both references come from the same home, even if the stubs are referring to two different Remote EJB objects.
  • For stateful session beans: isIdentical() returns false for any two unique stubs in all cases.
  • For entity beans: isIdenticial() returns true if the stubs refer to two entities with the same primary key.

The EJBLocalHome Interface

We have previously seen the Remote interfaces that allow to access a component either from another JVM instance or from a distant computer (maybe even in another country). The local interfaces, EJBLocalHome for EJBHome and EJBLocalObject for EJBObject allow access to the components when the EJB container and the client share the same virtual machine. The local interfaces are simpler and have less methods because many methods only make sense when there’s a stub and a “remote” server.

Methods Seen by The Client

EJBLocalHome EJBHome
EJBMetaData EJBMetaData()
HomeHandle getHomeHandle()
void remove(Handle h)
void remove(Object key) void remove(Object key)

Example

 1package org.garba.sessionbeans;
 2
 3import javax.ejb.CreateException;
 4import javax.ejb.EJBLocalHome;
 5
 6public interface GreeterLocalHome extends EJBLocalHome {
 7
 8public GreeterLocal create() throws CreateException;
 9
10}

As you can see, the create() method at line 8 doesn’t declare a RemoteException as GreeterHome.java.

Rules for The EJBLocalHome Interface

  • It must import javax.ejb.*
  • It must extend EJBLocalHome.
  • All create methods must declare a CreateException.
  • All create methods must return a local component interface.
  • Your own exceptions should be checked exceptions (Not subclasses of RuntimeException)
  • You must not declare a RemoteException for any methods

The EJBLocalObject Interface

The EJBLocalObject interface has only one method less than the EJBObject interface but the method for getting the home only returns an object of EJBLocalHome type.

Methods Seen by The Client

EJBLocalObject EJBObject
Object getPrimaryKey() Object getPrimaryKey()
EJBLocalHome getEJBLocalHome() EJBHome getEJBHome()
Handle getHandle()
void remove() void remove()
boolean isIdentical() boolean isIdentical()

Example

1package org.garba.sessionbeans;
2
3import javax.ejb.*;
4
5public interface GreeterLocal extends EJBLocalObject {
6
7public String getGreeting();
8
9}

As you can see, the getGreeting() method at line 7 doesn’t declare a RemoteException as Greeter.java.

Rules for The EJBLocalObject Interface

  • It must import javax.ejb.*
  • It must extend EJBLocalObject.
  • You must declare one or more methods.
  • Your own exceptions should be checked exceptions (Not subclasses of RuntimeException).
  • Business methods names must not begin with the string “ejb”.
  • You must not declare a RemoteException for any methods.

A Local Client

 1import org.garba.sessionbeans.GreeterLocal;
 2import org.garba.sessionbeans.GreeterLocalHome;
 3
 4import javax.ejb.CreateException;
 5import javax.naming.Context;
 6import javax.naming.InitialContext;
 7import javax.naming.NamingException;
 8
 9public class GreeterClient {
10
11public static void main(String[] args) {
12
13Context ic = null;
14GreeterLocalHome home = null;
15GreeterLocal greeterLocal = null;
16
17try {
18
19ic = new InitialContext();
20Object o = ic.lookup("greeterLocal");
21
22// There is no need for PortableRemoteObject.narrow()
23
24home = (GreeterLocalHome) o; 
25greeterLocal = home.create();
26
27} catch (NamingException e) {
28e.printStackTrace();
29} catch (CreateException e) {
30e.printStackTrace();
31}
32
33// This method doesn't throw a RemoteException
34
35System.out.println(greeterLocal.getGreeting());
36
37}
38}

Note the differences between a local client and a client for remote components:

  • Lines 1 to 7 (Import statements): There is no need for javax.rmi.*; or java.rmi.*;
  • Line 24: Narrowing is no longer necessary.
  • Lines 27 to 31 (Catch statement): Catching RemoteException is no longer necessary.
  • Line 35: The method getGreeting() doesn’t declare a RemoteException so there’s no need to catch it.

Key Moments in a Session Bean’s Life

rospo

Creation

Stateless session beans can have only one create() in the home interface and thus only one ejbCreate() counterpart. Stateful session beans instead, must have at least one create() method (not precisely without arguments) but can have many more. A method such as createUser(String user) in the home interface will require an ejbCreateUser(String user) method in the bean class. A certified container won’t deploy an EJB component that doesn’t follow this pattern.

Creation events in order

  1. Object construction
  2. setSessionContext()
  3. ejbCreate()

Passivation

A passivated bean is temporarily saved in some kind of secondary storage, to conserve resources between client calls. If a pasivated bean times out, the bean will simply die, without first being reactivated. A bean that is in a transaction cannot be pasivated

Passivation events in order

  1. ejbPassivate()
  2. ejbActivate()

Removal

Session bean removal takes place when the client explicitley calls the remove() method or when the container decides that the bean has timed out. Under normal circumstances the ejbRemove() method will be called before the bean dies although some unexpected error might prevent this from happening.

Passivation

When the container calls the ejbPassivate() method of a stateful bean, it is necessary to do the necessary work to leave all variables in a serialization-compatible state. Transient variables are not expected to return to their initial (or null) state when serialized, they must be properly set in ejbActivate(). A stateful session bean will never be passivated while the bean is still in a transaction.

When ejbPassivate() completes, every non-transient variable must be a reference to one of the following:

  • a Serializable object
  • a null value
  • a bean’s remote component or home interface (regardless of the serialization state of the stub’s class)
  • a bean’s local component or home interface (regardless of its serialization state)
  • a SessionContext object
  • the bean’s special JNDI context and its subcontexts
  • the UserTransaction interface
  • a resource manager connection factory (like, an instance of javax.sql.DataSource)

Example of good behavior

 1Connection connection = null;
 2
 3public void ejbCreate() {
 4try {
 5connection = DBPool.getConnection();
 6} catch (Exception ex) {
 7// Error
 8}
 9}
10
11public void ejbPassivate() throws EJBException, RemoteException {
12connection = null;
13    }
14
15public void ejbActivate() throws EJBException, RemoteException {
16try {
17connection = DBPool.getConnection();
18} catch (Exception ex) {
19// Error
20}

Removal

A session bean dies for one of these three reasons:

  1. The client calls remove()

The formal way of removing a bean is calling remove() in the home or the component interface. In every case the container will call ejbRemove() on the bean so there is a chance to perform a bit of cleanup.

  1. The bean times out

If it is a stateful bean in active state, ejbRemove() will be called as expected, instead if the container finds the bean in passivated mode, the container WILL NOT CALL ejbRemove(). In this case the bean will be sent straight to the Garbage Collector.

  1. The bean throws a system exception (or the container crashes)

The container WILL NOT CALL ejbRemove() under any circumstance.

The SessionContext Interface

The container calls setSessionContext() only once in the bean’s life, at the beginning of its life. The SessionContext interface (that extends EJBContext) provides the only methods that allow the bean to talk to the container and get information about the client in the case of stateful session beans

EJBHome getEJBHome()

EJBLocalHome getEJBLocalHome()

Get a reference to the home for remote and local components respectively.

EJBObject getEJBObject()

EJBLocalObject getEJBLocalObject()

Get a reference to bean’s EJB object for remote and local components respectively.

Principal getCallerPrincipal()

boolean isCallerInRole(String s)

get security information about the client

void setRollbackOnly(boolean b)

force a transaction to rollback (CMT only)

boolean getRollbackOnly()

find out if the transaction has already been set to rollback (CMT only)

UserTransaction getUserTransaction()

get a transaction reference, and start your own transaction (BMT only)

Entity Beans

An entity bean represents a business object in a persistent storage mechanism. Typically, each entity bean has an underlying table in a relational database, and each instance of the bean corresponds to a row in that table. Entity beans are persistent, allow shared access, have primary keys, and may participate in relationships with other entity beans. Persistence may be implemented by the bean developer (BMP) Bean-Managed Persistence or by the Container (CMP) Container-Manager Persistence

A “Customer” framework

The most basic entity includes the same amount of elements that the session “hello world” example. In this case we use CMP in order to make things easier.

 1    CustomerEjb.jar
 2      |
 3      |-org.garba.entitybeans/
 4      |          |
 5      |          |-Customer.class
 6      |          |-CustomerBean.class
 7      |          |-CustomerHome.class
 8      |
 9      |-META-INF/
10           |
11           |-ejb-jar.xml
12        

The Component Interface

An entity bean Remote component interface extends EJBObject. There’s no separate interface for session beans and entity beans. This interface usually contains getters and setters for field values that correspond to columns in the database table as seen in the example.

Customer.java

 1package org.garba.entitybeans;
 2
 3import javax.ejb.EJBObject;
 4import java.rmi.RemoteException;
 5
 6public interface Customer extends EJBObject {
 7
 8public Integer getCustomerId() throws RemoteException;
 9public String getName() throws RemoteException;
10public void setName(String name) throws RemoteException;
11
12}

Component Interface Rules:

  • It must import java.rmi.RemoteException
  • It must import javax.ejb.*
  • It must extend EJBObject.
  • Arguments and return types must be legal RMI-IIOP types (primitive, Serializable, Remote or arrays/collections of these types).
  • Overloaded methods are allowed.
  • Each method must declare a RemoteException.
  • You can declare your own application exceptions as long as they are checked exceptions (i.e. subclasses of Exception, not of RuntimeException).
  • Methods names must not begin with the string “ejb”.

The Bean Class

The EntityBean interface has three additional methods not present in the SessionBean interface that the bean developer must implement:

  • public void unsetEntityContext()
  • public void ejbLoad()
  • public void ejbStore()
  • void setEntityContext(EntityContext ec) (not exactly new but different)

CustomerBean.java

 1package org.garba.entitybeans;
 2
 3import javax.ejb.EntityBean;
 4import javax.ejb.EntityContext;
 5
 6public abstract class CustomerBean implements EntityBean {
 7
 8private static int counter = 0;
 9private int beanNumber;
10
11private EntityContext context;
12
13public CustomerBean () {
14counter++;
15beanNumber = counter;
16System.out.println("Constructor (" + beanNumber + ")");
17}
18
19public abstract Integer getCustomerId();
20public abstract String getName();
21public abstract void setName(String name);
22
23public void setEntityContext(EntityContext context) {
24System.out.println("setEntityContext (" + beanNumber + ")");
25this.context = context;
26}
27
28public Integer ejbCreate(String name) {
29System.out.println("ejbCreate (" + beanNumber + ")");
30this.setName(name);
31return null;
32}
33
34public void ejbPostCreate(String name) {
35System.out.println("ejbPostCreate (" + beanNumber + ")");
36}
37
38public void unsetEntityContext() {
39System.out.println("unsetEntityContext (" + beanNumber + ")");
40}
41    
42public void ejbLoad() {
43System.out.println("ejbLoad (" + beanNumber + ")");
44}
45
46public void ejbStore() {
47System.out.println("ejbStore (" + beanNumber + ")");
48}
49
50public void ejbRemove() {
51System.out.println("ejbRemove (" + beanNumber + ")");
52}
53
54public void ejbActivate() {
55System.out.println("ejbActivate (" + beanNumber + ")");
56}
57
58public void ejbPassivate() {
59System.out.println("ejbPassivate (" + beanNumber + ")");
60}
61
62}

EntityBean interface

setEntityContext(EntityContext ec)
Container gives the bean reference to its context.

unsetEntityContext()
Called when the Container wants to reduce the size of the pool.

ejbPassivate()
Called when the bean is about to return to the pool, following a transaction.

ejbActivate()
Called when the bean is taken out of the pool to service a client’s business method call.

ejbRemove()
Called when the client calls remove(), and wants to delete this entity from the database.

ejbLoad()
Called when the bean has been refreshed with data from the underlying persistent store.

ejbLoad()
Called when the Container is about to update the database to reflect the state of the bean.\

Bean class rules:

  • The following methods from the EntityBean interface must be implemented:

    • public void ejbActivate()
    • public void ejbPassivate()
    • public void ejbRemove()
    • public void setEntityContext(EntityContext ctx) (different)
    • public void unsetEntityContext() (new)
    • public void ejbLoad() (new)
    • public void ejbStore() (new)
  • In this case we use CMP so getters and setters must be abstract (thus the class must be also abstract). If the bean programmer must implement validation he can choose not to expose the setter/getter in the component interface and expose instead a method that contains the validation code:

    public abstract setRealName(String realname); public void setName(String name) { if (name == null) name = "Unnamed"; this.setRealName(name); }

  • If you have create() methods in your home, you must match each create() with two methods: ejbCreate() and ejbPostCreate().

  • Business methods must be declared as public, and must not be declared as final or static.

  • Method names must not begin with the string “ejb”.

  • You must not pass “this” as an argument or return value because the bean itself must never be exposed.

  • Arguments and return types for Remote component interface methods must be legal RMI-IIOP types (primitive, Serializable, Remote or arrays/collections of these types)

  • You don’t have to declare the exceptions declared in the component interface, unless you might actually throw those excpetions from your own methods.

  • You must never declare a RemoteException

  • You must not declare any application exceptions (checked exceptions) that were not declared in the component interface for that matching business method.

  • The bean class is always Serializable even if you don’t see “implements Serializable”

  • The class must be public and cannot be final or abstract.

  • The constructor must be public and without arguments. It is always recommended to let the compiler insert the default constructor given that anyway there’s nothing to be put inside the constructor.

The Home Interface

An entity bean home interface is substantially different from that of a session bean, because entity beans are typically found rather than created. In an entity home, a create() method is not required. Entity beans home interfaces can have single-row or multi-row finder methods. Both create and finder methods return the component interface of a bean, although multi-entity finders return a Collection of component interface references. Every entity bean home is required to have at least one method -the findByPrimaryKey() method that searches for a particular entity and returns its component interface or throws an exception. Multiple-entity finders do not throw an exception if no matching entities are found. They simply return an empty collection.

The entity home interface can also have home businesses methods, for operations that apply to more than one entity, as opposed to one specific entity. The real benefit of home business methods is that -unlike create and finder methods- can return something other than an EJB object reference. If the clients wants only data, for example, a Collection of Strings representing the name and phone number of each customer, a home business method can do that while a finder can not.

CustomerHome.java

 1package org.garba.entitybeans;
 2
 3import java.rmi.RemoteException;
 4
 5import javax.ejb.CreateException;
 6import javax.ejb.EJBHome;
 7import javax.ejb.FinderException;
 8
 9public interface CustomerHome extends EJBHome {
10
11public Customer findByPrimaryKey(Integer key) throws FinderException, RemoteException;
12public Customer create(String name) throws CreateException, RemoteException;
13
14}

Home Interface Rules:

  • It must import java.rmi.RemoteException
  • It must import javax.ejb.*
  • It must extend EJBHome.
  • Declare (optionally) one or more create() methods which must return the Remote component interface, and declare both a RemoteException and a CreateException. Each create() method must begin with the prefix “create”.
  • Declare the findByPrimaryKey() method, which must return a remote component interface, and declare both a RemoteException and a FinderException.
  • Declare one or more home business methods.
    • Arguments and return types must be RMI-IIOP compatible (Serializable, primitive, Remote, or array/collections of any of those).
    • Overloaded methods are allowed.
    • Each method must declare a RemoteException.
    • The user may declare his own exceptions, but they must not be runtime exceptions, i.e. subclasses of Exception and not subclasses of RuntimeException.
    • Methods can have arbitrary names, as long as they don’t begin with “create”, “find”, or “remove”.

The ejb-jar.xml file

The XML descriptor is a bit different from the session bean’s example.

 1<?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?>
 2 <!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 
 3 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
 4 <ejb-jar>
 5    <display-name>Ejb1</display-name>
 6    <enterprise-beans>
 7       <session>
 8          <display-name>GreeterBean</display-name>
 9          <ejb-name>GreeterBean</ejb-name>
10          <home>org.garba.sessionbeans.GreeterHome</home>
11          <remote>org.garba.sessionbeans.Greeter</remote>
12          <ejb-class>org.garba.sessionbeans.GreeterBean</ejb-class>
13          <session-type>Stateful</session-type>
14          <transaction-type>Bean</transaction-type>
15          <security-identity>
16             <description></description>
17             <use-caller-identity></use-caller-identity>
18          </security-identity>
19       </session>
20    </enterprise-beans>
21 </ejb-jar>

The Database

This is what the data in the store may look like.

1  customer_id   name
2  -------------- ----------
3  1              Pasquale
4  2              Andrea
5  3              Rospone

The Client

In this example we use a client that is called from a servlet environment.

CustomerClient.java

 1package org.garba.tests;
 2
 3import java.io.PrintWriter;
 4import java.rmi.RemoteException;
 5
 6import javax.ejb.CreateException;
 7import javax.ejb.FinderException;
 8import javax.ejb.RemoveException;
 9import javax.naming.Context;
10import javax.naming.InitialContext;
11import javax.naming.NamingException;
12import javax.rmi.PortableRemoteObject;
13
14import org.garba.entitybeans.Customer;
15import org.garba.entitybeans.CustomerHome;
16
17public class CustomerClient {
18
19public final static void test(PrintWriter out) {
20
21try {
22
23Context ic = new InitialContext();
24Object o = ic.lookup("java:comp/env/ejb/CustomerBean");
25CustomerHome home = (CustomerHome) PortableRemoteObject.narrow(o, CustomerHome.class);
26
27// Find an existing entity
28
29Customer customer = home.findByPrimaryKey(new Integer(1));
30out.println("Customer #1 name: " + customer.getName()); 
31
32// Create a new entity
33
34Customer newCustomer = home.create("Massimo");
35out.println("New Customer Id: " + newCustomer.getCustomerId());
36
37// Remove new entity
38
39newCustomer.remove();
40
41} catch (NamingException e) {
42out.println(e);
43} catch (RemoteException e) {
44out.println(e);
45} catch (FinderException e) {
46out.println(e);
47} catch (CreateException e) {
48out.println(e);
49} catch (RemoveException e) {
50out.println(e);
51}
52
53}
54
55}

Result (Web output):

1    Customer #1 name: Pasquale
2    New Customer Id: 4                         `

Container’s console output (Resin 3.0.6 in this case):

1. The container puts a bean into the pool

Constructor (1)

setEntityContext (1)

2. home.findByPrimaryKey(new Integer(1));

Constructor (2)

setEntityContext (2)

ejbActivate (2)

ejbLoad (2)

3. home.create(“Massimo”);

Constructor (3)

setEntityContext (3)

ejbCreate (3)

ejbPostCreate (3)

4. newCustomer.remove();

ejbRemove (3)

unsetEntityContext (3)

The Client View

You can note the component interface extends EJBObject, and the home interface extends EJBHome, thus you are expected to find the same methods that session bean clients see. Of course, you will also find your own methods, such as getCustomerId/getName()/setName() in the component interface and findByPrimaryKey() in the home interface.

Key Moments in an Entity Bean’s Life

Please note that ejbSelectXXX() is invoked when the bean is in the pool if the call originates in the home. If the select method is called from a method in the bean’s component interface, the method is running on a specific entity, so the bean is in the method-ready state.

Creation

The container calls ejbCreate() only when the client requests the creation of a new entity that didn’t exist before. With “new entity” we refer to a new record in the storage (i.e. database). ejbCreate() doesn’t instantiate a new java object. Any bean from the pool may be returned. Remember that entity beans must not necessarily provide creation infrastructure. The bean developer chooses whether he wants to allow clients to create new entities.

Creation events in order

  1. ejbCreate()

    This is the only chance you have to modify your persistent fields before the material “insert” in the storage takes place. Thus, this is also the only place where you can set the entity’s primary key. Most Containers will complain if the primary key is null (we didn’t set it in our example) -this is just a proprietary Resin’s behavior with MySQL’s auto-generated primary keys. You cannot pass an EJBObject reference here because it still doesn’t exist.

    Rules:

    • Each ejbCreateXXX() in the bean class must match each createXXX() in the home interface.
    • The method name must begin with the prefix “ejbCreate()”.
    • The method must be declared public, and must not be declared static or final.
    • The declared return type must be the entity bean’s primary key type, even though you will return null from the method.
    • The method arguments must be the same as the arguments of the matching createXXX()
    • You may declare a throws clause with CreateException, or any arbitrary checked exception as long as it was also declared in the home interface.
    • You must not declare a RemoteException.

    Rules for primary keys:

    • A primary key class must be Serializable and public.
    • You can use a single persistent field from your bean class as your primary key, by identifying both the field name and the class type in the DD.
    • If you need two or more persistent field to uniqueley identify your entity, make a custom compound primary key class.
    • A compound key class must be made up of the fields that are defined as persistent fields in the bean class. The fields in the bean class must have public accessor methods.
  2. ejbPostCreate()

    At this time you have an EJBObject reference and you are able to access your CMR (container-managed relationships).

    Rules:

    • Each ejbPostCreateXXX() in the bean class must match each createXXX() in the home interface.
    • The method name must begin with the prefix “ejbPostCreate”.
    • The method must be declared public, and must not be declared static or final.
    • The declared return type must be void
    • The method arguments must be the same as the matching ejbCreateXXX().
    • You may declare a throws clause with CreateException, or any arbitrary checked exception as long as it was also declared in the home interface.
    • You must not declare a RemoteException.

Home Business Methods

Home Business Methods are used to interact with a group of entities rather than one specific entity. They don’t return the bean’s component interface as opposed to the Finder and Create methods.

In the Bean Class:

public Collection ejbHomeShowAll() { // Statements // ... }

In the Home Interface:

public Collection showAll() throws RemoteException {}

Rules for Home Business Methods

  1. There must be an ejbHomeXXX method in the bean class for every home method in the home interface
  2. The method must be declared public and NOT static.
  3. If the home interface is remote, the arguments and return values must be legal types for RMI-IIOP.
  4. Checked exceptions are allowed as long as they have been declared in the home interface.

Message-Driven Beans

Message-driven are stateless components for processing asynchronus JMS messages. They consume and process messages concurrently. Information and examples soon.

Key Moments in a Message-Driven Bean’s Life

EJB-QL

An EJB-QL statement is declared by appending a query block to the entity declaration. EJB-SQL queries may receive parameters by declaring them in the <method-params> section and then by referring to them using ?1, ?2, ?3, etc. according to the number of parameters.

 1<entity>
 2    ......
 3    ......
 4    <query>
 5        <query-method>
 6            <method-name>findEmployeesBySalary</method-name>
 7            <method-params>
 8                <method-param>java.lang.Integer</method-param>
 9            </method-params>
10        </query-method>
11        <ejb-ql>
12            SELECT DISTINCT OBJECT(e) FROM employees e WHERE e.salary >= ?1
13        </ejb-ql>
14    </query>
15</entity>

The table name should be written as in the <abstract-schema-name> and fields as in <field-name>

Finder Methods

Finder methods are declared in the home class and always use the prefix “find”. They are public and visible to the clients. As the default findByPrimaryKey() method implemented by the container, these methods may throw a FinderException.

In the Home Class:

   public Collection findEmployeesBySalary(Integer salary) throws FinderException;

Select Methods

Select methods are very much like finder methods but they are declared in the bean class and are private to the bean. They cannot be invoked by the client. Select methods must be declared abstract.

In ejb-jar.xml:

   <method-name>ejbSelectEmployeesBySalary</method-name

In the Home Class:

   NOTHING!

In the Bean Class:

   public abstract Collection ejbSelectEmployeesBySalary(Integer salary) throws FinderException;

Examples

SELECT

  SELECT OBJECT (e) FROM employees e - Get a collection of all employee beans.

  SELECT e.name FROM employees e - Get as collection the name of all employees.

  SELECT OBJECT (e) FROM employees e WHERE e.cardNumber = 455433 - Get the employee who owns this credit card

THE IN OPERATOR

  SELECT DISTINCT OBJECT(b) FROM bosses b, IN (b.employees) e WHERE e.salary >= 500 - Select the bosses that have at least one employee that earns more than 500 bucks. The DISTINCT keyword ensures that the query does not return duplicates

ARITHMETIC OPERATORS

  SELECT OBJECT(e) FROM employees e WHERE (e.salary * 12) < 12000;

BETWEEN

  SELECT OBJECT(e) FROM employees e WHERE e.salary BETWEEN 6000 AND 8000;
  SELECT OBJECT(e) FROM employees e WHERE e.salary NOT BETWEEN 8000 AND 9000;

‘IN’ IN WHERE CLAUSE

  SELECT OBJECT(e) FROM employees e WHERE e.country IN ('de','es','it');

IS NULL

  SELECT OBJECT(e) FROM employees e WHERE e.wifeName IS NULL;   SELECT OBJECT(e) FROM employees e WHERE e.wifeName IS NOT NULL;

IS EMPTY

  SELECT OBJECT(b) FROM bosses b WHERE b.employees IS EMPTY; - Checks for a boss without employees

  SELECT OBJECT(b) FROM bosses b WHERE b.employees IS NOT EMPTY; - Checks for a boss with employees

Each boss has 0, 1 or more employees

Functional Expressions

  • CONCAT(String1, String2) - String1 + String 2
  • LENGTH(String)
  • LOCATE(String1, String2 [, start]) - Tells in which position String1 is found within String2. start is optional and indicates the position in String2 at which the search should start.
  • SUBSTRING(String1, start, length) - SUBSTRING(“neos bastardo”,2,3) = eos
  • ABS(number) - Absolute value of a number of type (int, float or double)
  • SQRT(double) - Square root

References

  • Head First EJB first edition by Kathy Sierra & Bert Bates. ISBN 0-596-00571-7 (Oct 2003, O’REILLY)
  • Enterprise JavaBeans 3rd Edition by Richard Monson-Haefel. ISBN 0-596-00226-2 (September 2001, O’REILLY)
  • Caucho’s Resin documentation