Enterprise JavaBeans™ -EJB-



Preamble

This tutorial on the Enterprise JavaBeans™ (EJB) component model is based on the Java Enterprise Edition (Java EE) 8 API (Application Programming Interface) and EJB version 3.2. Java EE 8 tutorial is here

Note that Java EE will no longer exist with the emergence of Java 9. Jakarta EE 8 is a rewritten (iso-functional) version of Java EE 8. Jakarta EE 9 is the next version of Java EE 8.

Case studies are provided as Maven/Apache NetBeans projects; they have also been tested by means of this Integrated Development Environment (IDE).

What is Java Enterprise Edition (Java EE)?

Java EE technologies

Java EE products (Java EE application servers)

Java EE challengers: Django, Drupal, .NET, Spring, Node.js

Java EE Application Model is here

Java EE 8 API (here)

EJB history
EJB overview

Principles

Characteristics

Benefits

EJB types

Entity Beans

Example (including JPQL code)

@javax.persistence.Entity(name = "MY_PRISONER")
@javax.persistence.Table(name = "PRISONER")
@javax.persistence.SecondaryTable(name = "INCARCERATION", pkJoinColumns = {@javax.persistence.PrimaryKeyJoinColumn(name = "PRISON_FILE_NUMBER")})
@javax.persistence.NamedNativeQuery(name = "Prisoner.Under_remand_SQL", query = "SELECT * FROM Prisoner WHERE prison_file_number NOT IN (SELECT prison_file_number FROM Conviction)")
@javax.persistence.NamedQueries({@javax.persistence.NamedQuery(name = "Prisoner.All_JPQL", query = "SELECT p FROM Prisoner"),
                                 @javax.persistence.NamedQuery(name = "Prisoner.Under_remand_JPQL", query = "SELECT p FROM Prisoner WHERE p._conviction IS EMPTY")
                                })    
public class Prisoner implements java.io.Serializable { …

Example (Customer_management Stateless Session Bean deals with Customer Entity Bean)

@javax.ejb.Stateless(mappedName = "Customer_management+", name = "Customer_management")
@javax.ejb.Remote({Customer_managementRemote.class})
public class Customer_managementBean implements Customer_managementRemote {

    @javax.persistence.PersistenceContext(name = "FranckBarbier") // Check 'persistence.xml' file...
    private javax.persistence.EntityManager _entity_manager;

    @Override
    public void insert_customer(final String bank_code, final String customer_PIN, final String name, final String address) {
        Customer client = new Customer(bank_code, customer_PIN);
        client.set_name(name);
        client.set_address(address);
        _entity_manager.persist(client);
    }

    @Override
    public void delete_customer(final String bank_code, final String customer_PIN) {
        Customer client = new Customer(bank_code, customer_PIN);
        _entity_manager.remove(_entity_manager.merge(client));
    }

    @Override
    public String view_customer(final String bank_code, final String customer_PIN) {
        String result = "";
        Customer client = _entity_manager.find(Customer.class, new CustomerPK(bank_code, customer_PIN));
        result += client != null ? "Found: " + client : "Not found: " + bank_code + "-" + customer_PIN;
        return result;
    }
}

Session Beans

Message-Driven Beans

EJB metamodel
Implementation class

In the UML metamodel above, one shows that an Enterprise Bean is embodied by one and only one Java implementation class (1..1 cardinality). By default, the Enterprise Bean name is the name of that class. Otherwise:

@javax.ejb.Stateful(mappedName = "My_terminal", name = "Railcar_control_system_terminal")
public class TerminalBean implements Terminal {
    @javax.annotation.Resource
    private javax.ejb.SessionContext _ejb_context = null;
    Terminal _self = null;
    …
        _self = _ejb_context.getBusinessObject(Terminal.class);
    …
Stateless Session Beans

Key features

Creation and deletion

Transaction management

Design patterns

Example

@javax.jws.WebService(serviceName = "Railcar_new_destination")
@javax.ejb.Stateless
public class Railcar_new_destination { …

Example

@javax.ejb.Remote
public interface Customer_managementRemote { // Business methods…

@javax.ejb.Stateless(mappedName = "Customer_management+", name = "Customer_management")
// @javax.ejb.Remote({Customer_managementRemote.class}) // Alternative to using '@javax.ejb.Remote' above
public class Customer_managementBean implements Customer_managementRemote { … 
Singleton Session Beans

Key features

Creation and deletion

Transaction management

Design patterns

Other features

Example (EJB 3.x)

@javax.ejb.Local
public interface Control_center_local { // Business methods locally accessed…

@javax.ejb.Remote
public interface Control_center_remote { // Business methods externally accessed…

@javax.ejb.Singleton
@javax.ejb.Startup
public class Control_center implements Control_center_local, Control_center_remote { …
Stateful Session Beans

Key features

Creation and deletion

Transaction management

Design patterns

Example (EJB 3.x)

@javax.ejb.Stateful
public class Frontend_service {

    private int _counter = 0;

    @javax.ejb.EJB
    private Backend_service _backend_service;

    @javax.ejb.PrePassivate
    public void passivate() {
        _counter--;
    }

    @javax.ejb.PostActivate
    public void activate() {
        _counter++;
    }

    public void service(int i) {
        assert (_backend_service != null);
        _counter += i;
        System.out.println(this + "... " + _counter);
        _backend_service.service(_counter);
    }

    @javax.ejb.Remove
    public void remove() { // Called by the owner!
        // Resource release, if any...
    }
}

Another example here

Message-Driven Beans

JMS

Example (javax.jms.MapMessage)

javax.jms.MapMessage order = _session.createMapMessage();
order.setString("entrée","foie gras");
order.setString("plat principal","magret");
order.setBoolean("dessert du jour ?",false);
_sender.send(order);

Example (javax.jms.ObjectMessage, Java world only!)

javax.jms.ObjectMessage ambient_temperature_changed = _session.createObjectMessage();
ambient_temperature_changed.setObject(_temperature); // '_temperature' is an instance of a type, which implements 'java.io.Serializable'...
_sender.send(ambient_temperature_changed);

Key features

Creation and deletion

Transaction management

Design patterns

Example (message sending)

@javax.ejb.Singleton
@javax.ejb.Startup
public class Starter {

    private javax.jms.QueueConnectionFactory _queue_connection_factory;
    private javax.jms.Queue _queue;
    private javax.jms.QueueConnection _queue_connection;
    private javax.jms.QueueSession _queue_session;
    private javax.jms.QueueSender _queue_sender;

    @javax.annotation.PostConstruct
    public void create() {
        try {
            System.out.println("One starts immediately because of '@javax.ejb.Startup'...");
            javax.naming.Context _jndi_context = new javax.naming.InitialContext();
            _queue_connection_factory = (javax.jms.QueueConnectionFactory) _jndi_context.lookup("jms/MY_QUEUE_FACTORY");
            _queue = (javax.jms.Queue) _jndi_context.lookup("jms/MY_QUEUE");
            _queue_connection = _queue_connection_factory.createQueueConnection();
            _queue_session = _queue_connection.createQueueSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
            _queue_sender = _queue_session.createSender(_queue);

            _queue_sender.send(_queue_session.createTextMessage("0"));
            _queue_sender.send(_queue_session.createTextMessage("1"));
            _queue_sender.send(_queue_session.createTextMessage("1"));
            Thread.sleep(1000);
            _queue_sender.send(_queue_session.createTextMessage("Stop..."));
        } catch (Exception e) {
            // Error...
        }
    }
}

Example (message reception and processing - EJB 3.x)

@javax.ejb.MessageDriven(mappedName = "jms/MY_QUEUE", activationConfig = {
                        @javax.ejb.ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
                        @javax.ejb.ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") })
public class Receiver implements javax.jms.MessageListener {
    @javax.annotation.Resource
    private javax.ejb.MessageDrivenContext _ejb_context; // Optional, but necessary for getting a transaction object (BMT mode)
    @javax.annotation.PostConstruct 
    public void construct() { // Resource acquisition } 
    @Override    
    public void onMessage(javax.jms.Message message) {  
        if (message instanceof javax.jms.TextMessage) …   
    }    
    @javax.annotation.PreDestroy
    public void destroy() { // Resource release }            
}
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 here

The lifecycles of Enterprise Beans

Lifecycle management

Passivation and activation

Because resources on Java EE application servers decrease performance, developers must take care of releasing these resources when unused. This may occur when Enterprise Beans are destroyed. This may also occur when Enterprise Beans like Stateful Session Beans are passivated.

Example (how to optimally handle a naming context)

private javax.naming.Context _jndi_context;
…
@javax.ejb.PrePassivate
public void passivate() {
    if (_jndi_context != null) _jndi_context.close(); // This code may raise an exception if the resource has been obtained through dependency injection
    _jndi_context = null; // Safer in case of dependency injection  
}
@javax.ejb.PostActivate
public void activate() {
    _jndi_context = new javax.naming.InitialContext(); 
}

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 Enterprise Bean 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 Enterprise Bean container opens and closes transactions (CMT mode) on the behalf on the Enterprise Bean, which delegates this charge to its container. For various reasons, Enterprise Beans 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 Beans instance.

Example

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:

Example

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 run-time so that Enterprise Beans 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 Enterprise Beans. 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 Enterprise Beans.

 

Dependency design patterns

Resource dependency management

Local, remote or no interface at all?

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 may be used.

@javax.ejb.Remote annotation

Enterprise Beans 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., Java EE application server running 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:

Example

@javax.ejb.Remote({Obelix.class})
@javax.ejb.Stateless(mappedName = "ejb/Obelix")
public class Obelix_implementation implements Obelix {

    @javax.ejb.Asynchronous // The result is returned to the Enterprise Bean container, not directly to the client component...
    @Override
    public java.util.concurrent.Future<String> complain() {
        return new javax.ejb.AsyncResult<>("Arrrrrgggglllll");
    }

    @Override
    public String strength() {
        return "I'm really the strongest... ";
    }
}
@javax.ejb.EJB(mappedName = "ejb/Obelix", lookup = "ejb/Obelix")
Obelix o = null;

@Override
public void strength() {
    java.util.concurrent.Future<String> result = o.complain();
    try {
        System.out.println(o.strength() + (result.isDone() ? result.get() : "not yet available, operation canceled: " + result.cancel(false)));
    } catch (InterruptedException | java.util.concurrent.ExecutionException ex) {
        java.util.logging.Logger.getLogger(Asterix.class.getSimpleName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
}
Reentrance

Enterprise Beans are by default not reentrant. This means that an Enterprise Bean 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 Enterprise Bean is incompatible with the ongoing transaction, which handles the Enterprise Bean as an exclusive resource.

Otherwise, 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 Enterprise Bean is nevertheless active for the execution duration of a given business method.

Reentrance is mainly concerned with Stateful Session Beans because references to other Enterprise Beans 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

The general functioning of a transaction is as follows:

In this example, one may imagine that operation 2 fails (a raised exception materialized such a failure). The overall business logic is such that operation 3 must not success. Having a transaction associated with operation 1 guarantees a consistent (collaborative) functioning between operation 2 and operation 3.

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 Enterprise 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, they are to be completed and have just completed. The javax.ejb.SessionSynchronization interface is devoted to this job.

Configuration

The deep nature of Enterprise Beans 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 in order 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 in ejb-jar.xml  as follows (one declares that all (*) business methods of My_bean have the RequiresNew transaction attribute):

<ejb-jar …> <!--EJB ver. 2.x--> 
    …
    <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 with <max-pool-size>1</max-pool-size>.

EJB dependencies in ejb-jar.xml file

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         version="3.2"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/ejb-jar_3_2.xsd"> <!--EJB ver. 3.x-->
    <enterprise-beans>
        <session>
            <ejb-name>My_Asterix</ejb-name>
            <business-remote>Asterix.Asterix</business-remote>
            <ejb-class>Asterix.Asterix_implementation</ejb-class>
            <session-type>Stateless</session-type>
            <transaction-type>Container</transaction-type>
            <ejb-ref>
                <ejb-ref-name>ejb/Obelix</ejb-ref-name>
                <ejb-ref-type>Session</ejb-ref-type>
                <remote>Obelix.Obelix</remote>
            </ejb-ref>
        </session>
    </enterprise-beans>
</ejb-jar>
Packaging

EJB applications are packaged in EJB modules or in EJB applications (.ear or .jar files). Files including source files and configuration data must be located in precise directories with a special tree structure. For example:

Security

Security features can be setup at design to be experienced at run-time.

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 Glassfish Java EE 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 Sessions Beans (including Singleton Sessions Beans) 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 Enterprise Beans. 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 Enterprise Beans may be found here

CDI

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