Enterprise JavaBeans™ (EJB)


Creative Commons License
This Enterprise JavaBeans™ (EJB) tutorial is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
Preamble
This tutorial on the Enterprise JavaBeans™ (EJB) component model is based on the Java EE 7 Application Programming Interface (API) (see API here) and EJB version 3.2. Please take into account that some constructs will not work with old EJB versions.
Case studies are provided as NetBeans projects; they have also been tested by means of this IDE (Integrated Development Environment).
History
Overview

Principles

Characteristics

Benefits

EJB platforms (i.e., Java EE application servers, see this section)
EJB types
EJB metamodel
EJB metamodel

Implementation class

In the UML metamodel above, one shows that an EJB is embodied by one and only one Java implementation class (1..1 cardinality). By default, the EJB name is the name of that class. Otherwise:
@javax.ejb.Stateful
public class TerminalBean implements Terminal {
    @javax.annotation.Resource
    private javax.ejb.SessionContext _ejb_context = null;
    Terminal _self = null;
    …
        _self = _ejb_context.getBusinessObject(Terminal.class);
    …

Discriminating features

EJB overview
Stateless Session Beans

Key features

Creation and deletion

Transaction management

Design patterns

Example(s)

Singleton Session Beans

Key features

Creation and deletion

Transaction management

Design patterns

Other features

Example(s)

Stateful Session Beans

Key features

Creation and deletion

Transaction management

Design patterns

Example(s)

Message-Driven Beans

Key features

Creation and deletion

Transaction management

Design patterns

Example(s)

Entity Beans

Key features

Creation and deletion

Additional information on Entity Beans requires a survey of the JPA technology (here)

Transaction management

Design patterns

Example(s)

The lifecycles of Enterprise Beans

Lifecycle management

Passivation and activation

Because resources on application servers decrease performance, developers must take care of releasing these resources when unused. This may occur when beans are destroyed. This may also occur when beans, like Stateful Session Beans, are passivated. This example shows how to optimally handle a naming context:
private javax.naming.Context _jndi_context;

Note: in general, resources acquired by dependency injection must not be managed by developers. In other words, the best solution when releasing such resources is to assign null to the variable embodying the resource.
The notion of EJB “container”
An EJB container is a kind of controller. Effectively, a container is an instance of a Java class generated by the application server at deployment time. This class mixes the Java code released by the EJB's developer with the configuration attribute values assigned by this developer to its EJB. For example, an EJB container opens and closes transactions (CMT mode) on the behalf on the EJB, which delegates this charge to its container. For various reasons, EJBs must sometimes access to their execution context materialized by their container in order to access resources/facilities. As an illustration, the BMT mode requires the access to the current transaction (example).

Interacting with a container (a.k.a. execution context)

In EJB ver. 2.x, one may, for instance, access to the execution context of a session bean instance as follows:
public class X_bean implements SessionBean {
    private SessionContext _ejb_context;
    public void setSessionContext(SessionContex ejb_context) {
        _ejb_context = ejb_context;
    }
    …
The access to the session bean itself (then avoiding this) is as follows:
X_remote x_remote = _ejb_context.getEJBObject(); // 'X_remote' is a Java interface that is declared in a XML file as the remote interface of 'X_bean' 
In EJB ver. 3.x, one accesses to the execution context of a session bean instance through dependency injection (example here).
EJB dependencies
Dependencies are links at runtime so that EJBs are able to interact. Dependencies are also concerned by links between software components and services on one side and, resources (databases, JMS queues/topics…) on another side.
From Java 5, resource injection (or dependency injection) is a key mechanism to setup dependencies between EJBs. From Java 6, the Context and Dependency Injection (CDI) mechanism is a more powerful mechanism. Historically, the EJB technology uses the Java Naming and Directory Interface (JNDI) technology to dynamically create references between EJBs. 

Dependency design patterns

Reenforcing the checking of dependencies at deployment time

Resource dependencies

<resource-ref>: coupling with JDBC connection pool, JMS connection factory…
<resource-env-ref>: coupling with ready-to-use resource, e.g., JMS queue
Local, remote or no interface at all?

Accessing Session Beans

The case of home interfaces being local or remote

Local or remote home interfaces are a concept from older EJB versions, prior to 3.x especially. However, they remain useful in certain cases, namely when one want to initialize Session Beans with specific values at creation time. For that, the javax.ejb.Init annotation is used.

@javax.ejb.Remote annotation

EJBs are located (packaged) in an EJB module. An EJB module is a unit of deployment. An instance of an EJB of type A in an EJB module can access to an instance of an EJB of type B in another EJB module only if and only if B is annotated with @javax.ejb.Remote. @javax.ejb.Remote has to be used sparingly. It is especially useful when one distributes EJB modules to different Java EE VMs (i.e., application server instances).
Concurrency

From EJB 3.x, it exists two major mechanisms to deal with concurrency, more precisely, to control the concurrent access to a component. These are:

Reentrance
EJBs are by default not reentrant. This means that an EJB involved in a business method cannot receive at the same time a request from elsewhere demanding the execution of the same, or another offered, business method. Reentrance is firstly linked to transactions. If the executing business method is configured such that it is included within a transaction, calling at the same time the EJB is incompatible with the ongoing transaction, which handles the EJB as an exclusive resource. Secondly, reentrance is also not possible even if the transaction mode is set to BMT (@javax.ejb.TransactionManagement(javax.ejb.TransactionManagementType.BEAN)) or is not supported (@javax.ejb.TransactionAttribute(javax.ejb.TransactionAttributeType.NOT_SUPPORTED)). In this case, no transaction is in progress but the EJB is nevertheless active for the execution duration of a given business method.
Reentrance example
Reentrance is mainly concerned with Stateful Session Beans because references to other EJBs do not require to be kept. Reentrance issues have been better addressed in EJB ver 3.x with the arrival of Singleton Session Beans.
Transaction management

Key features

Container-Managed Transaction (CMT)

@javax.ejb.Stateful
public class TerminalBean implements Terminal {
… @javax.ejb.TransactionAttribute(javax.ejb.TransactionAttributeType.NOT_SUPPORTED)
public String get_name() { …
Containers roll back transactions in case of "system" failures, namely when a javax.ejb.EJBException is raised. However, there are some tricky cases when such an exception is not raised. At the same time, one wants to roll back the transaction because of a local business dysfunction, which is not captured by the container. In this specific case, one rolls back the current transaction while the CMT mode is active:
@javax.annotation.Resource
private javax.ejb.SessionContext _ejb_context = null;

assert(_ejb_context != null);
javax.transaction.UserTransaction ut = _ejb_context.getUserTransaction();

if(/* some test */) ut.setRollbackOnly();

Bean-Managed Transaction (BMT)

In this case, the developer must setup the bean so that it may benefit from the BMT status:
@javax.ejb.Stateful
@javax.ejb.TransactionManagement(javax.ejb.TransactionManagementType.BEAN)
public class RailcarBean implements Railcar { …
Next, she/he must catch the ongoing transaction, start it, stop it through the commit or rollback functions:
@javax.annotation.Resource
private javax.ejb.SessionContext _ejb_context = null;

assert(_ejb_context != null);
javax.transaction.UserTransaction ut = _ejb_context.getUserTransaction();
ut.begin();

if(/* some test */) ut.commit();
else ut.rollback();

Transactions in Message-Driven Beans

Message consumption is associated with an acknowledgement mechanism. Message acknowledgement is automatic unless specific settings. By default, acknowledgment leads to commit while other situations lead to rollback (e.g., failure within onMessage).

Fine-grain transaction control

There is a support for managing transactions with higher accuracy (CMT Stateful Session Beans only), namely when transactions are to be started, are to be completed and have just completed. The javax.ejb.SessionSynchronization interface is devoted to this job.
Configuration
The deep nature of EJBs is the possibility of configuring deployment values. These values are that of parameters, which are interpretable by a Java EE application server. The latter transforms the developer's values into code to match to the execution environments characteristics, constraints, etc. Application server administration allows the introduction of homemade parameters but, it is outside the scope of this tutorial. Configuration preferably occurs through XML files (EJB 2.x and older versions) or annotations (EJB 3.x). The last approach does not exclude the utilization of XML files in tricky cases. The core configuration file is the ejb-jar.xml file. So, for example, in aged EJB versions (2.x), transaction attributes may be setup ejb-jar.xml  as follows (one declares that all ("*") business methods of My_bean have the RequiresNew transaction attribute):
<ejb-jar>
<assembly-descriptor>

<container-transaction>
<method>
<ejb-name>My_bean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
There are some other important configurations files like persistence.xml (JPA) or glassfish-resources.xml. The latter is a proprietary file of the Glassfish Java EE application server. Any configured value within this file is not portable across application servers. For example, one may force Glassfish to create no more than 1 instance of a given Message-Driven Bean.

EJB dependencies in ejb-jar.xml file

<session>
<display-name>My_client_component</display-name>

<ejb-ref>
<ejb-ref-name>Customer_management+</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>

<remote>com.FranckBarbier.Java._Customer_management.Customer_managementRemote</remote>
<ejb-link>Customer_managementBean</ejb-link>
</ejb-ref>
<max-pool-size>1</max-pool-size>
Packaging
EJB applications are packaged in EJB modules or in EJB applications (EAR files). Files including source files and configuration data must be located in precise directories with a special tree structure.
Security
Security features can be setup at design to be experienced at runtime.
Advanced Features

Service computing in EJB

The big picture: the Java EE platform itself provides "technical" services as provided by any middleware. The major Java EE services are transaction management, security, performance (load balancing), persistence, naming, messaging… Most of them often come from the Java EE sublayer. As an illustration, practically, the Java EE Glassfish application server may be built on the top of CORBA that itself supports technical services: COS naming, CORBA Object Transaction Service (OTS)… OSGi is another candidate for Java EE application server implementation. In EJB, there are also business services that are the primary way of modularizing applications through the sharing of these software components among several enterprise applications. Endowing these business services with Internet interoperability leads to giving the status of Web services to Stateless Sessions Beans.

Support for Web services

Stateless Session Beans (including singletons) annotated with @javax.ejb.WebService can be accessed as a Web service (example here).

Timer service

For reliability reasons, the Java SE API for timers (i.e., java.util.Timer and java.util.TimerTask…) has to be avoided in EJB applications because of distribution especially across Java VMs. Moreover, since Stateful Session Beans can be passivated, timer services are not allowed for this kind of EJBs. This book (§ 8.5.6) briefly explains how to implement timer services in Stateful Session Beans. Otherwise, a concise example of using timer services in EJBs may be found here.

CDI

The beans.xml file is dedicated to CDI. It can be empty.

Comparing BMP and CMP Entity Beans

Resources

EJB 2.x BMP Entity Beans

Introduction

This section discusses the use of BMP Entity Beans. This old programming style based on JDBC was born within EJB 2.x. However, it is supported by EJB 3.x even if it is no more recommended. In effect, SQL statements are embedded in Java code with poor maintenance capabilitity. The offered case study is based on a Customer management Stateless Session Bean, which deals with a Customer Entity Bean (see figure).
Customer management example

Accessing the Customer management remote home interface

javax.naming.Context jndi_context = new javax.naming.InitialContext();
Customer_managementRemoteHome customer_managementRemoteHome = (Customer_managementRemoteHome) javax.rmi.PortableRemoteObject.narrow(jndi_context.lookup("Customer_management+"),Customer_managementRemoteHome.class);

Creating a Customer management instance (remote interface)

Customer_managementRemote customer_management = customer_managementRemoteHome.create();

Creating a Customer instance

customer_management.insert_customer("0000000000", "11-01-1963", "Franck Barbier", "www.PauWare.com");

Primary keys of Entity Beans

Primary keys are mandatory. String type is the default type. Alternatively, it is required the creation of a dedicated Java class which plays the role of primary key:
public class CustomerPK implements java.io.Serializable {
private String _bank_code;
public String get_bank_code() { return _bank_code; }
public void set_bank_code(String bank_code) { _bank_code = bank_code; }
private String _customer_PIN;
public String get_customer_PIN() { return _customer_PIN; }
public void set_customer_PIN(String customer_PIN) { _customer_PIN = customer_PIN; }
public CustomerPK() { }

Setup in ejb-jar.xml file (see here)

Usage (typically in Customer management Stateless Session Bean)

private CustomerRemoteHome _customerRemoteHome;

public void ejbCreate() {
try {
_jndi_context = new InitialContext();
_customerRemoteHome = (CustomerRemoteHome) _jndi_context.lookup("java:comp/env/ejb/Customer");
} catch (NamingException ne) { throw new EJBException(ne.getMessage() + ", " + ne.getExplanation()); }
}

CustomerRemote customer = _customerRemoteHome.findByPrimaryKey(new CustomerPK(bank_code,customer_PIN));
CustomerPK primary_key = (CustomerPK)customer.getPrimaryKey();

Finder methods in Remote Home Interface

public interface CustomerRemoteHome extends javax.ejb.EJBHome {
CustomerRemote findByPrimaryKey(CustomerPK primary_key) throws javax.ejb.FinderException, java.rmi.RemoteException;

Corresponding Finder methods in Implementation class

public CustomerPK ejbFindByPrimaryKey(CustomerPK primary_key) throws FinderException {
try {
_sql_connection = _data_source.getConnection();
_sql_connection.setAutoCommit(false);
ResultSet resultSet = _sql_connection.createStatement().executeQuery("SELECT * FROM Client WHERE bank_code = '"+
primary_key.get_bank_code() + "' AND customer_PIN = '" + primary_key.get_customer_PIN() + "'");
if(resultSet.next()) return
primary_key;
else throw new ObjectNotFoundException();

}
catch(SQLException sqle) { throw new EJBException(sqle.getMessage()); }

}

Creation

public CustomerPK ejbCreate(final String bank_code,final String customer_PIN,final String name,final String address) throws CreateException {
try {
_sql_connection = _data_source.getConnection();
_sql_connection.setAutoCommit(false);
if(_sql_connection.createStatement().executeUpdate("INSERT INTO Client VALUES('"+ bank_code + "','" + customer_PIN + "','" + name + "','" + address +"')") == 0) throw new javax.ejb.CreateException("ejbCreate/INSERT failed…");
}
catch(SQLException sqle) { throw new javax.ejb.CreateException(sqle.getMessage()); }
_bank_code = bank_code;
_customer_PIN = customer_PIN;
_name = name;
_address = address;
return new CustomerPK(_bank_code,_customer_PIN);
}

Destruction

public void ejbRemove() {
try {
if(_sql_connection != null) {
if(_sql_connection.createStatement().executeUpdate("DELETE FROM Client WHERE bank_code = '" + _bank_code + "' AND customer_PIN = '" + _customer_PIN + "'") == 0) throw new EJBException("ejbRemove/DELETE failed…");
_sql_connection.close();

}
}
catch(SQLException sqle) { throw new EJBException(sqle.getMessage()); }

}

Other issues

EJB 3.x CMP Entity Beans

See JPA.