This tutorial on the Enterprise JavaBeans™ (EJB) component model is based on the Java EE 8 API (Application Programming Interface) and EJB version 3.2. Java EE 8 tutorial is here… Please take into account that some EJB 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).
- EJB is the core technology of Java EE with several key versions: EJB 1.0 (1999), EJB 2.1 (2003), EJB 3.0 (2006), EJB 3.1 (2009) and EJB 3.2 (2013)
- Initially created by IBM with first JSR in 1999 and key players: Oracle, IBM, SAP…
Principles
- EJBs are business components, i.e., they implement business functions as Java methods
- EJBs are server-side (shared!) components within a Java EE VM (Virtual Machine) called in this case a Java EE application server
- EJBs are configurable components through XML (EJB 2.x) and/or Java annotations (EJB 3.x)
- EJBs have predefined (strict, EJB 2.x) or (more relaxed, EJB 3.x) formats in order to be in particular handled in a certain way by the Java EE application server on which they are deployed
- EJBs are distributable, i.e., they are mobile from one Java EE VM to another in order to possibly scale up applications through load balancing
Characteristics
- Integrative support for persistence (Java Persistence API or JPA), transaction management (Java Transaction API or JTA), messaging (Java Message Service or JMS), etc.
- Federating technology: a place where (sometimes heterogeneous) Java technologies can meet together: JNDI, JMS, JDBC, JDO, JPA, JTS/JTA, JCA…
- Underlying support for Web Services
- Rigorous development framework (ease of reuse, QoS management like load balancing, transaction management, security…). However, EJBs are sometimes intelligible with difficulty!
- Natural support for the Software as a Service (SaaS) idea in cloud computing
Benefits
- The EJB technology is a Java standard in the sense it is a JSR and therefore, a vendor-free technology
- Maturity, high availability, credibility… and nice facilities in very famous IDEs like Eclipse or NetBeans
Java EE challengers: Django, Drupal, .NET, Spring, Node.js…
BMP is an old EJB style pertaining to J2EE 1.4. Nonetheless, it is also supported in Java EE 5, 6, 7 & 8. In this case,ejbCreate
andejbPostCreate
are two mandatory methods. They can be empty or they can be populated in a mutually consistent manner.ejbCreate
matches to theINSERT
SQL statement whileejbPostCreate
may invokegetPrimaryKey
andgetEJBObject
via the Entity Bean's execution context (or “container”) whose type isEJBContext
(it is setup via thesetEntityContext
mandatory method). As forejbRemove
, it corresponds to theDELETE
SQL statement.ejbLoad
andejbStore
match to theSELECT
andUPDATE
SQL statements. Finally,unsetEntityContext
is dedicated to the possible release of resources.
@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 { …
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:
- In EJB, a Java class is built by a developer to create some business functionality through a given set of business methods. Business methods differ from one category of EJB to another:
- Entity Beans only offer data read/write/query methods plus a redefinition of the
hashCode
andequals
methods inherited fromjava.lang.Object
- Message-Driven Beans have one business method whose name is fixed once and for all:
public void onMessage(javax.jms.Message message);
- Session Beans offer one or more business methods; these methods are the atomic business functionality of a “wrapping” service or an application
- In EJB ver. 2.x, EJBs must own additional “technical” methods (which may be empty) to comply with what is expected by the Java EE application server; in other words, the absence of such technical methods may create errors at deployment time. Technical methods are in essence not exposed in components' interfaces because they are not called by other components. Instead, they are automatically called by the Java EE application server at precise moments within the components' lifecycle (see this section). For example, the imposed format of a Message-Driven Bean in EJB ver. 3.x is as follows.
- In Java,
this
embodies the current instance. Such a self-reference must be avoided in EJB because a given object representing in memory a/some component(s) is not a portable reference across Java EE VMs. Instead ofthis
, a self-reference on the current component is required when, for instance, one wants to pass the component as argument of a business function. Here is the way of getting a reference on the “current” component instead of usingthis
:@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); …
Discriminating features
![]()
Key features
- Stateless Session Beans are the most common EJBs
- They are "shared" between client programs. In reality, in terms of Java instances, they are cloned. Several clones (Java instances) may represent the same component. So, between two business method calls, Stateless Session Beans do not keep a persistent state. Typically, one call uses a Java instance representing the called component while the second call uses another Java instance representing the same component.
Creation and deletion
- Stateless Session Beans are created and deleted by the Java EE application server. More precisely, they are pooled and delivered to client programs (in terms of identities) according their availability. Access (implying either creation or recycling by the server) occurs either through dependency injection with the
@javax.ejb.EJB
annotation or through JNDI lookups.Transaction management
- Bean-Managed Transaction (BMT) versus Container-Managed Transaction (CMT). In CMT mode, a transaction is started when a business method is called. It is closed when this business method ends.
Design patterns
- Stateless Session Beans are the preferred means to deal with Entity Beans
- Stateless Session Beans play the role of façades according to the Façade design pattern
- They are the support for Web Services as follows:
@javax.jws.WebService(serviceName = "Railcar_new_destination") @javax.ejb.Stateless public class Railcar_new_destination { …
Example(s)
- EJB 2.x
<session> <!--ejb-jar.xml file--> <display-name>Customer_management</display-name> <ejb-name>Customer_management+</ejb-name> <home>com.FranckBarbier.Java._Customer_management.Customer_managementRemoteHome</home> <remote>com.FranckBarbier.Java._Customer_management.Customer_managementRemote</remote> <ejb-class>com.FranckBarbier.Java._Customer_management.Customer_managementBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <!--CMT--> </session>
public interface Customer_managementRemoteBusiness { // Business methods… public interface Customer_managementRemote extends javax.ejb.EJBObject, Customer_managementRemoteBusiness { … public interface Customer_managementRemoteHome extends javax.ejb.EJBHome { Customer_managementRemote create() throws javax.ejb.CreateException, java.rmi.RemoteException; } public class Customer_managementBean implements javax.ejb.SessionBean, Customer_managementRemoteBusiness { private javax.ejb.SessionContext _ejb_context; public void setSessionContext(javax.ejb.SessionContext ejb_context) { _ejb_context = ejb_context; } private javax.naming.Context _jndi_context; private CustomerRemoteHome _customerRemoteHome; public void ejbActivate() { // Empty because Stateless } public void ejbPassivate() { // Empty because Stateless } public void ejbRemove() { // Resource release } public void ejbCreate() { try { _jndi_context = new javax.naming.InitialContext(); _customerRemoteHome = (CustomerRemoteHome)_jndi_context.lookup("java:comp/env/ejb/Customer"); } catch (javax.naming.NamingException ne) { throw new javax.ejb.EJBException(ne.getMessage() + ", " + ne.getExplanation()); } } …
- EJB 3.x
@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 { …
Key features
- Singleton Session Beans are designed for concurrent access (thread-safe); concurrent access is managed by their container (to be preferred) or manually (i.e., bean-managed).
Creation and deletion
- By definition, a singleton component has one and only instance, which is automatically created by the Java EE application server at deployment time.
Transaction management
- Bean-Managed Transaction (BMT) versus Container-Managed Transaction (CMT).
Design patterns
- In general, Singleton Session Beans act as orchestrators, typically a control center receiving multiple concurrent requests and re-dispatching orders/jobs according to a centralized business logic.
Other features
@javax.ejb.Startup
is dedicated to Singleton Session Beans; it allows the immediate initialization (i.e., the method annotated by@javax.annotation.PostConstruct
is executed at application installation time instead of, when the bean is for the first time accessed).Example(s)
- 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 { …
Key features
- Stateful Session Beans are created and deleted by client programs. These programs automatically own these components; they are unshared.
- Stateful Session Beans are subject to passivation and activation by the Java EE application server. These two actions allow the server to carry out a load balancing strategy.
Creation and deletion
- They are created once and for all by their clients (i.e., their owners; there is no sharing). Accordingly, the client is in charge of deletion. There is no default way of passing parameters at creation time. For that, the
javax.ejb.Init
annotation is required. Creation occurs either through dependency injection with the@javax.ejb.EJB
annotation or through JNDI lookups. Contrary to Stateless Session Beans, each lookup results in the creation of a new component!Transaction management
- Bean-Managed Transaction (BMT) versus Container-Managed Transaction (CMT)
Design patterns
- Stateful Session Beans record client program session data like, for instance, an e-commerce basket.
Example(s)
- 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... } }
- Other example here…
Key features
- The direct use of JMS in EJB is impossible. For this specific reason, Message-Driven Beans have been introduced in EJB to leverage a controlled utilization of JMS.
- Message-Driven Beans must in essence by associated with JMS resources. Although JMS offers queue-based or topic-based communication, these two kinds of message repository have no difference in the EJB technology while native JMS programs handle queues and topics in a different way.
- Message-Driven Beans cannot be accessed by other EJBs. In other words, Message-Driven Beans do not offer local or remote interfaces. More precisely, Session Beans or “external” programs (servelts, Java SE client programs, third-party client components…) send messages to JMS queues; these messages are consumed by Message-Driven Beans in an automated way.
Creation and deletion
- Similar to Stateless Session Beans, Message-Driven Beans are replicated by the server without developer control. Creation and deletion can be synchronized.
Transaction management
- Message-Driven Beans are either BMT or CMT. In CMT mode, only the
Required
orNotSupported
attributes are possible because no client program can access to Message-Driven Beans through interfaces.Design patterns
- Message-Driven Beans may cope with Entity Beans, Session Beans, even send messages to other JMS queues.
Example(s)
- Sending messages
@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... } } }
- Receiving and processing messages (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 } }
Key features
- Entity Beans are exact (synchronized) images of rows in SQL tables. CRUD actions in databases amount to acting on Entity Bean instances in memory. Entity Beans are constructed on the top of the JPA technology, which is runnable in the Java SE world.
Creation and deletion
Additional information on Entity Beans requires a survey of the JPA technology (here)
- Entity Beans are comparable to Stateless Session Beans. Accordingly, client programs (or client components) get access to, and handle Entity Bean instances by requesting data in databases.
- Entity Bean instances are transparently shared by clients. Several Entity Bean instances may represent, in the middleware, the same row in a table. The middleware (the Java EE application server) delivers these instances according to the current load (client programs' requests).
Transaction management
- Entity Beans are CMT EJBs.
Design patterns
- In general, an Entity Bean type matches to a table in database, e.g., the Invoice entity bean matches to the Invoice table. However, a one-to-one matching is not always a good solution in the sense that one table may lead to several Entity Bean types while, differently, several tables may match to a single Entity Bean type.
Example(s)
- EJB 2.x
<entity> <display-name>Customer</display-name> <ejb-name>Customer</ejb-name> <home>com.FranckBarbier.Java._Customer_management.CustomerRemoteHome</home> <remote>com.FranckBarbier.Java._Customer_management.CustomerRemote</remote> <ejb-class>com.FranckBarbier.Java._Customer_management.CustomerBean</ejb-class> <persistence-type>Bean</persistence-type> <!--BMP--> <prim-key-class>com.FranckBarbier.Java._Customer_management.CustomerPK</prim-key-class> <reentrant>false</reentrant> <resource-ref> <description>Banking system database</description> <res-ref-name>jdbc/Banking_system</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref> </entity>
- EJB 3.x
See here
Lifecycle management
@javax.annotation.PostConstruct
(ejbCreate
in EJB ver. 2.x): it is triggered after dependency injection.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 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: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 assignprivate javax.naming.Context _jndi_context; …
- EJB 2.x
public void ejbPassivate() { try { if (_jndi_context != null) _jndi_context.close(); } … public void ejbActivate() { try { _jndi_context = new javax.naming.InitialContext(); } …
- EJB 3.x
@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(); }
null
to the variable embodying the resource.
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:
- The
javax.ejb.SessionContext
interface is dedicated to Session Beans- The
javax.ejb.EntityContext
interface is dedicated to Entity Beans- The
javax.ejb.MessageDrivenContext
interface is dedicated to Message-Driven Beanspublic 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 avoidingthis
) is as follows:In EJB ver. 3.x, one accesses to the execution context of a session bean instance through dependency injection (example here).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'
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
- Entity Beans may be accessed by Session Beans and Message-Driven Beans. Typically, accessing to Entity Beans occurs through an entity manager as follows:
@javax.persistence.PersistenceContext(name = "My_persistence_unit") // 'My_persistence_unit' is a symbolic name; it must be associated with a Java EE application server's database resource in the 'persistence.xml' file private javax.persistence.EntityManager _entity_manager;
- Sessions Beans may be accessed by Session Beans and Message-Driven Beans as follows:
- Dependency injection that supposes that EJBs are in the same virtual machine:
- Dependency based on implicit naming:
@javax.ejb.Stateless public class Customer_managementBean implements Customer_managementRemote { … // In another EJB: @javax.ejb.EJB Customer_managementRemote cm = null;
- Dependency based on explicit naming:
@javax.ejb.Stateless(mappedName = "Customer_management+", name = "Customer_management") // 'name' attribute is information only; it is not used in resource injection! public class Customer_managementBean implements Customer_managementRemote { … // In another EJB: @javax.ejb.EJB(mappedName = "Customer_management+") Customer_managementRemote cm = null;
JNDI:
- // Normalized form from EJB 3.1 only:
@javax.ejb.Stateless(mappedName = "Customer_management+", name = "Customer_management") public class Customer_managementBean implements Customer_managementRemote { … // Later on: javax.naming.Context jndi_context = new javax.naming.InitialContext(); Customer_managementRemote cm = (Customer_managementRemote)jndi_context.lookup("java:global/My_EJB_module/Customer_management+"); // 'My_EJB_module' is the name of the deployment file, say 'My_EJB_module.jar' or 'My_EJB_module.ear'
- // Non-portable (across Java EE application servers) even common form:
@javax.ejb.Stateless(mappedName = "Customer_management+", name = "Customer_management")
public class Customer_managementBean implements Customer_managementRemote { … // Later on: javax.naming.Context jndi_context = new javax.naming.InitialContext(); Customer_managementRemote cm = (Customer_managementRemote)jndi_context.lookup("Customer_management+");Message-Driven Beans are not accessible by other EJB types Resource dependency management
<resource-ref>
: coupling with JDBC connection pool, JMS connection factory…<resource-env-ref>
: coupling with ready-to-use resource, e.g., JMS queue- Reenforcing the checking of component-to-component dependencies at deployment time
Local, remote or no interface at all?
- The no-interface view is quite similar to a local view (caution: all public methods are then locally exposed!). Either
@javax.ejb.LocalBean
or no interface declaration allows a no-interface view. Such an interface setup is not appropriate when, for instance, one wants to access to the session bean from a client program.No interface declaration:
Alternatively:@javax.ejb.Singleton public class Control_center { …
@javax.ejb.Singleton @javax.ejb.LocalBean public class Control_center { …
@javax.ejb.Local
. This annotation is the way to define a local business interface. Assigning a local business interface to a session bean is as follows:Simple way:
Alternatively:@javax.ejb.Local(Railcar.class) public class RailcarBean implements Railcar { … // The implementation of the Java interface is not mandatory but it greatly reinforces Java type checking
@javax.ejb.Local public interface Railcar { … public class RailcarBean implements Railcar { …
@javax.ejb.Remote
. This annotation is the way to define a remote business interface:Simple way:
Alternatively:@javax.ejb.Remote(Control_center_remote.class) public class Control_center implements Control_center_remote { … // The implementation of the Java interface is not mandatory but it greatly reinforces Java type checking
@javax.ejb.Remote public interface Control_center_remote { … public class Control_center implements Control_center_remote { …
- Sophisticated cases are when one needs both local and remote accesses to the session bean depending upon varied client program types. This approach may be extended by creating multiple local interfaces and/or multiple remote interfaces for the same session bean:
@javax.ejb.Local public interface Control_center_local { … @javax.ejb.Remote public interface Control_center_remote { … public class Control_center implements Control_center_local, Control_center_remote { …
- More details on the way of 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, thejavax.ejb.Init
annotation may be used.
@javax.ejb.Remote
annotationEJBs 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).
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:
@javax.ejb.Singleton
, an annotation destinated to Singleton Session Beans;@javax.ejb.Asynchronous
, an annotation dedicated to Session Beans only (further details from Java EE 8 tutorial):
- asynchronous methods return values having either the
void
or thejava.util.concurrent.Future<T>
type; the latter case allows full control on the asynchronous called method;- asynchronous methods for Stateless Session Beans and Singleton Session Beans are useful when one wants to immediately pick up the control after the call of the asynchronous method; in other cases, it is useless because Stateless Session Beans are duplicated while Singleton Session Beans are thread-safe by construction;
- asynchronous methods for Stateful Session Beans are illustrated within the Railcar control system case study.
@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 clieny 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); } }
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 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.![]()
Key features
- A great advantage of Java EE is the availability of an internal transaction manager based on Java Transaction Service (JTS). From a utilization viewpoint, Java Transaction API (JTA) is the entry point with
javax.transaction.Transaction
as main interface.- Transactions allow the cancellation or the validation of data in DBMS. JTS is independent of most of the DBMS providers. Using JTS imposes the abort of the DBMS' own mechanism for managing transactions. Typically, JTS excludes mechanisms supported by other Java technologies like JDBC, which enables
COMMIT
orROLLBACK
through thejava.sql.Connection
interface. Furthermore, JTS does not support nested transactions as follows (Oracle DBMS SQL code):So, JTS promotes a canonical transaction management framework in Java EE in general and EJB in particular. Namely, transactions are associated with business method calls only (Session Beans) orsavepoint transaction1; insert into …; savepoint transaction2; /* Nesting here */ insert into …; commit transaction2; /* One may validate, if desired, from the beginning of 'transaction2' only */
onMessage
(Message-Driven Beans). By default, a transaction starts and stops in relation with the execution time of a business method oronMessage
.- 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)
- This is the default mode for Session Beans and Message-Driven Beans (Entity Beans are by definition CMT beans).
- This mode greatly lightens EJB developers' code since they are not in charge of starting, stopping… transactions. However, despite these nice facilities, in subtle cases, they must analyze and/or handle the result of transactions' progress with great care.
- CMT transaction types and their resulting basic behaviors are detailed here.
Required
is the default mode. Typically, for a business method namedmy_business_method
:
- No transaction in progress → a new one is started or,
- A transaction is in progress → the behavior of
my_business_method
is viewed as the continuation of the caller business method in the existing transaction (no nesting!).- Other modes can be setup on demand. For example, business methods that do not change the state of components (similar to
const
functions in C++) may be annotated as follows:Containers roll back transactions in case of "system" failures, namely when a@javax.ejb.Stateful public class TerminalBean implements Terminal { @javax.ejb.TransactionAttribute(javax.ejb.TransactionAttributeType.NOT_SUPPORTED) public String get_name() { … …
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:Next, she/he must catch the ongoing transaction, start it, stop it through the@javax.ejb.Stateful @javax.ejb.TransactionManagement(javax.ejb.TransactionManagementType.BEAN) public class RailcarBean implements Railcar { …
COMMIT
orROLLBACK
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 toCOMMIT
while other situations lead toROLLBACK
(e.g., failure withinonMessage
).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. Thejavax.ejb.SessionSynchronization
interface is devoted to this job.
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 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 theejb-jar.xml
file. So, for example, in aged EJB versions (2.x), transaction attributes may be setup inejb-jar.xml
as follows (one declares that all (*
) business methods ofMy_bean
have theRequiresNew
transaction attribute):There are some other important configurations files like<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>
persistence.xml
(JPA) orglassfish-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>
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:
Asterix_V2.jar > Asterix > Asterix.class
Asterix_V2.jar > Asterix > Asterix_implementation.class
Asterix_V2.jar > META-INF > MANIFEST.MF
Asterix_V2.jar > META-INF > ejb-jar.xml
Security features can be setup at design to be experienced at runtime.
- EJB 2.x
- Design time:
<role-name>FranckBarbier</role-name>
- Run time:
if(_ejb_context.isCallerInRole("FranckBarbier")) { … // Client program has appropriate role java.security.Principal principal = _ejb_context.getCallerPrincipal(); // Current authentified client program
- EJB 3.x
@javax.ejb.Stateless @javax.ejb.Remote(CurrencyConverter.class) @javax.jws.WebService(serviceName = “CurrencyConverter”) @javax.annotation.security.DeclareRoles(“FranckBarbier”, “MireilleMathieu”) public class CurrencyImplementation implements CurrencyConverter { @javax.annotation.security.RolesAllowed(“FranckBarbier”) double convert(double amount, Currency source_currency, Currency target_currency, RoundingType rounding /*, etc.*/) …
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 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
andjava.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.
Resources
- EJB 2.x module as NetBeans project, EJB 2.x client program as NetBeans project
- EJB 3.x module as NetBeans project, EJB 3.x client program as NetBeans project
- Java DB database (SQL script)
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).![]()
Accessing the
Customer management
remote home interfacejavax.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 = '"+
.get_bank_code() + "' AND customer_PIN = '" +
primary_key
.get_customer_PIN() + "'");
primary_key
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
- Data synchronisation between database tables and Entity Beans:
ejbLoad
(SQLSELECT
) andejbStore
(SQLUPDATE
)- Activation (
ejbActivate
) and passivation (ejbPassivate
): resource allocation and desallocation (typically, JDBC connections)- Execution context creation (
setEntityContext
) and destruction (unsetEntityContext
): resource allocation and desallocation (typically JNDI connections)EJB 3.x CMP Entity Beans
See JPA.