Enterprise Java Beans (EJBs) are classes that encapsulate business functionalities, each of a certain kind. Unlike normal Java classes, however, EJBs run in a container environment , which means the server adds system-level services to them. These services include lifecycle management (instantiating and destroying, when and how), transactionality (building logical, atomic, rollback-enabled units of work), and security (which users can invoke which methods). Because Java MVC runs inside such a container, namely Jakarta EE, EJBs are a good way for Java MVC applications to encapsulate their business functionalities.
The EJB technology includes session beans and message driven beans. However, the latter go beyond the scope of this book, so here we will talk about session EJBs only.
About Session EJBs
Session EJBs can be accessed locally (in the same application), remotely (over the network, via method invocation), or via some web service interface (distributed applications across heterogeneous networks, HTML, XML, or JSON data formats).
Singleton: With a singleton session EJB, the container instantiates only one instance and all clients share this single instance. You can do this if the EJB does not have a state that discriminates clients, and concurrent access does not impose problems.
Stateless: EJBs of the “stateless” kind do not maintain a state, so a particular client can have different instances assigned to subsequent EJB invocations (the container handles this; the client doesn’t know about this assignment).
Stateful: Stateful EJBs maintain a state and a client can be sure it will receive the same session EJB instance from the container for subsequent uses of the same EJB. You will often hear that stateful EJB clients maintain a conversational state concerning using stateful EJBs. Stateful session EJBs cannot implement web services, because web services are not allowed to have state and no session information is communicated .
Defining EJBs
To define a singleton EJB, a stateless EJB, or a stateful EJB, you add one of these annotations—@Singleton, @Stateless, or @Stateful, respectively—to the EJB implementation.
Of course, all those classes must go to different files. We put them together for illustration purposes only.
No-interface: You use this method if you don’t want to describe the EJB access via an interface. This is only possible with local clients running inside the same application. While the separation into interfaces (describing what gets done in interfaces) and the implementation (the how, implemented in non-abstract classes) is generally a good idea for clean code, a no-interface view can make sense for simple EJBs. For no-interface EJBs, you just declare the implementation, as follows:
Local: If you want to define local access to session EJBs (EJBs and EJB clients running in the same application) and want to use an interface view for that, you can mark the interface with @Local and let the EJB implementation class implement the interface:
@Remote: Use the @Remote annotation for this session EJB to be accessible from outside the application. You can simply replace @Local with @Remote and everything that was said for the local access and concerning the interfaces is true unaltered for remote access. So you write the following, for example:
Remote access means parameters in method calls are passed by value, not by reference! So, although local and remote interfaces are declared co-natural to each other, you must be careful with method parameters under certain circumstances.
Accessing EJBs
Obviously, for this to work, the Java MVC application must have access to the compiled remote interfaces. That means in the EJB server build, you must have somehow included a step to extract the interfaces from the generated classes. We’ll talk about that in detail later.
Other Java Enterprise Edition (JEE or Jakarta EE) application servers probably apply other naming schemes for remotely accessible EJBs. So you must consult their documentation and/or get the remotely visible JNDI entry listing. For the latter, you can try programmatic access (commented out in the previous listing), or use some administration features implemented for the remote EJB server.
EJB Projects
Jakarta EE projects don’t have to be web projects; they can also just expose services to clients accessing their remote EJB interfaces. Web interfaces, like REST or web service interfaces, are your first choice for interoperability with web browsers and non-Jakarta EE servers. But for faster communication among Jakarta EE participants in a larger system with different network nodes, using Component-to-EJB communication might be a better choice.
Web projects can also expose remote EJBs to appropriate clients. If you want to have a streamlined project without web capabilities, the procedure to do that inside Eclipse is described in the following paragraphs.
This ensures that, after each jar task execution, the stubs are created. You can then run the jar task to create the full EJB jar and the stubs. You’ll find both in the build/libs folder. You may have to press F5 on that folder to update the view. Any client wishing to communicate with the EJBs must include the interface JAR as a dependency. Of course, the EJB project itself must be deployed on the server for the EJBs to work.
EJBs with Dependencies
Until now, we developed only very simple EJBs without the need to use libraries included as JARs. Once you need to add libraries to an EJB, you’ll run into trouble. The reason for this is that there is no standard way to add dependencies to isolated EJB modules. If you need to add library JARs, the best way is to pack the EJB module into an Enterprise Archive (EAR).
EARs are archives that bundle EJBs, web applications (WARs), and library JARs. Dealing with EARs instead of isolated EJBs somewhat increases the complexity of the administration activities. But adding library JARs to EARs is the best way of including dependencies with non-web applications.
- 1.
Build a new Gradle project. Go to New ➤ Other... ➤ Gradle ➤ Gradle Project.
- 2.
Choose any name you like. It’s a good idea to add “ear” to the end of the name.
- 3.
Inside build.gradle, change the plugins { } section to plugins { id 'ear' }.
- 4.
Inside build.gradle, use as the dependencies { } section:
- 5.
Create the war and ejb1 folders in the project root.
- 6.
Open the settings.gradle file and add the following:
- 7.
Invoke Gradle ➤ Refresh Gradle Project. Eclipse might throw an error message; you can ignore it for now.
- 8.
The two subprojects war and ejb1 show up in the Project Explorer. You may have to update the working set if you are using one.
- 9.
Convert both subprojects to a Faceted form (choose Configure ➤ Convert to Faceted Form...), and in the settings, add Java 1.8 capabilities.
This is for the Gradle dependencies. In order for Eclipse to recognize the dependency, you have to add the EJB project as a dependency in the Java Build Path (choose Project Settings ➤ Java Build Path ➤ Projects tab).
If you run the ear task, the subprojects and the EAR file will be built. The latter can be found in the build/libs folder.
Asynchronous EJB Invocation
EJB clients call EJB methods asynchronously. This means the client invokes an EJB method that was marked eligible for asynchronous invocation, immediately regains control of the program execution, and handles the result from the EJB invocation later, when it is available.
This example EJB uses the no-interface method, but asynchronous invocation works for local and remote interfaces as well. AsyncResult is a convenience class that allows for the easy creation of a Future. This Future object will not be exposed to the client; its main purpose is to obey the method signature. The Future returned to the client will instead be transparently created by the EJB container .
Timer EJBs
EJBs can be equipped with timer facilities, such as for delayed execution of some task or recurring automatic method invocations. You have two options: automatic timers and programmatic timers.
A delayed execution like “Do something once ten seconds after the server has started” is not possible with automatic timers.
This means every x:00:00 (x = 00 through 23), but also at 00:30:00. Unless you also give a persistent=false to the @Schedule annotation, a timer survives an application and a server restart.
The method annotated with @Timeout is called every time the timer fires. For this example, this will be 5000 milliseconds after EJB creation, because of the createSingleActionTimer() invocation . The timer service you get with context.getTimerService() enables various scheduling options; see the API documentation for details.
Exercises
Exercise 1
EJBs must have a local and a remote interface.
Not providing interfaces means EJBs are automatically assigned to local and remote interfaces by the EJB container (the part of the Jakarta EE server that handles EJBs).
A remote EJB means the EJB can be accessed from other applications on the same server. Access from other Jakarta EE servers is not possible.
EJBs cannot have a state.
If a client accesses an EJB, a new instance of the EJB is created on the server side.
To access any EJB from a client, you must use do a lookup in a JNDI context.
In order to use an EJB from a client, the EJB’s interfaces and its implementation must be imported into the client project.
Exercise 2
A JSE project (no Jakarta EE capabilities) with a single MyDateTime class and a method called date( String format ), which returns the LocalDateTime as a string, according to the format string specified as a parameter. Make it a Gradle project.
An EJB project with a single EJB MyDateTimeEjb and local and remote interfaces. Have it use the JAR file generated from the JRE project above. Hint: You can use something like implementation files( '../../- SimpleNoJEE/build/libs/SimpleNoJEE.jar' ) to specify a local dependency.
An EAR project that contains the EJB project and adds the necessary JAR dependency.
A simple no-Jakarta-EE EJB client project that tests the remote interface from the MyDateTimeEjb EJB. Hint: Include gf-client.jar from GlassFish’s lib folder as a library dependency.
Summary
Enterprise Java Beans (EJBs) are classes that encapsulate business functionalities, each of a certain kind. Unlike normal Java classes, however, EJBs run in a container environment, which means the server adds system-level services to them. These include lifecycle management (instantiating and destroying, when and how), transactionality (building logical, atomic, rollback-enabled units of work), and security (which users can invoke which methods). Because Java MVC runs inside such a container, namely Jakarta EE, EJBs are a good way for Java MVC applications to encapsulate their business functionalities.
The EJB technology includes session beans and message driven beans. Session EJBs can be accessed locally (in the same application), remotely (over the network, via method invocation), or via some web service interface (distributed applications across heterogeneous networks, HTML, XML or JSON data formats).
Concerning the creation and destruction of session EJBs, there are three types of session EJBs. Singleton EJBs, stateless EJBs, and stateful EJBS. To define any of them, you add the appropriate annotation—@Singleton, @Stateless, or @Stateful—to the EJB implementation.
Concerning their accessibility from client code, session EJBs can use one or a combination of three methods: no-interface access, local access, or remote access.
Accessing local EJBs from a Java MVC controller is easy: you just use the @EJB injection to let CDI assign instance access to an EJB: @EJB private SomeEjbInterface theEjb.
Addressing remote EJBs is considerably more complicated compared to local-access EJBs. You have to set up a JNDI context and then use it to do a lookup of a remote instance.
For this to work, the Java MVC application must have access to the compiled remote interfaces. That means, in the EJB server build, you must have somehow included a step to extract the interfaces from the generated classes.
Jakarta EE projects don’t have to be web projects; they can also just expose services to clients accessing their remote EJB interfaces. Web interfaces, like REST or web service interfaces, are your first choice for interoperability with web browsers and non-Jakarta EE servers. For faster communication among Jakarta EE participants in a larger system with different network nodes, using Component-to-EJB communication might be a better choice. Web projects also can expose remote EJBs to appropriate clients.
Once you need to add libraries to an EJB, the best way is to pack the EJB module into an Enterprise Archive (EAR). EARs are archives that bundle EJBs, web applications (WARs), and library JARs. Dealing with EARs instead of isolated EJBs somewhat increases the complexity of the administration activities. But once you’re finished, if you run the ear task, the subprojects and the EAR file will be built. The latter can be found in the build/libs folder.
EJB clients call EJB methods asynchronously. This means the client invokes an EJB method that was marked eligible for asynchronous invocation, immediately regains control of the program execution, and handles the result from the EJB invocation later, when it is available.
To mark an EJB method for asynchronous invocation, you add the @Asynchronous annotation from the javax.ejb package to the method.
EJBs can be equipped with timer facilities, such as for delayed execution of some task or reoccurring automatic method invocations. You have two options: automatic timers and programmatic timers.
With automatic timers, you add a @Schedule or @Schedules annotation (from the javax.ejb package) to any void method (the visibility doesn’t matter), either without a parameter or with a javax.ejb.Timer parameter. The parameters of the @Schedule annotation describe the frequency.
Timers can also be defined programmatically. It is also possible to define a one-time invocation.
In the next chapter, we learn how to connect Java MVC to databases.