The Enterprise Java Bean 3.0 (EJB 3) specification marked a very important way-point in the long march of Java in the enterprise. The specification was built very apparently with input from the community. It represented a much more consistent services paradigm, one that was more POJO-friendly and generally less complicated. The level of indirection afforded by Java 5's annotations made the paradigm more powerful while requiring less of the developer. The willingness to forsake bad legacy decisions for different, new solutions, made the framework interesting to people who might have previously shunned EJB. EJB Entity Beans disappeared, replaced with JPA Entities. The handful (or more) of Java classes and interfaces required for your average bean in EJB 2.1 or before became 2+ Java classes or interfaces. Convention-over-configuration based defaults were put into place to make it more straightforward to get up and running. EJB 3.0 was a revolution.
If EJB 3.0 was a revolution, EJB 3.1 is a very capable, welcome evolution. EJB 3.1 features a raft of features that feel like they should have been available in EJB 3.0. One can forgive the cautious pace of the specification - better to get 80% right than to freeze an imperfect model into the glacial evolution of a specification. Its arrival isn't unwelcome, of course. It is also telling that - even with all these new features - the EJB 3.1 specification and all the backwards compatibility and additions that if offers - is 626 pages, 14 pages shorter than the EJB 2.1 specification from almost a decade ago!
We will review some of these features in this post, and address their use.
New Features in EJB 3.1
Some of the biggest changes in EJB 3.1 are not additions to the platform, but reductions in the required ceremony in using the platform. Some increase the surface area of the user-facing APIs to introduce more flexibility. Some simply bring more flexibility.
Singletons
A singleton is a new type of session bean that provides one extra
guarantee: the bean will only be created once per running JVM. There
are many use cases that are better served with this feature: caching,
for example. Another is the ability to guarantee a shared view on a
resource that the application server doesn't already provide. Simple
storage for valuable data that needn't be persisted but that might be
too expensive to recreate is another option. Let's look at a
(contrived) example. We'll assume that the User
entity
has already been created elsewhere (perhaps using the JPA 2.0).
@javax.ejb.Singleton
public class ChatRoom {
private java.util.Map<User,Collection<String>> userComments;
@PostConstruct
public void setup (){
userComments = new java.util.concurrent.ConcurrentHashMap<User,Collection<String>>();
/* ...*/
}
public void join(User usr){ /* ...*/ }
public void disconnect(User usr){ /* ...*/ }
public void say(String msg){ /* ...*/ }
}
All clients can update the same mutable state by simple acquiring a
reference to the ChatRoom
instance. Clients are
guaranteed access to the same instance upon acquisition. As
this is a session bean, it offers the same guarantees as any other
session bean: this bean is fully thread safe.
@javax.ejb.EJB
private ChatRoom chatRoom ;
/* ... */
chatRoom.join(myUser) ;
chatRoom.say( "Hello, world!");
chatRoom.disconnect();
Singletons are designed for concurrent access. The specification
gives the developer sophisticated control over concurrency control.
You may use the container to force certain types of access in a
declarative way. This behavior is the default. You may explicityly
declare as using container-managed concurrency by annotating it with
@javax.ejb.ConcurrencyManagement(CONTAINER)
. If you want
to exert more control over the bean, use
@javax.ejb.ConcurrencyManagement(BEAN)
.
Container-managed concurrency lets you stipulate the type of access
at the method-level or class-level. You might by default stipulate
that all business methods are to be serialized by using
@javax.ejb.Lock(WRITE)
at the class level and then
optimize for the case where a method is effectively "read-only,"
with no state-modifying side-effects. You would annotate that
read-only method with @Lock(READ)
. All access on a
method annotated with @Lock(WRITE)
is serialized, and
blocks client access until completion, or a timeout occurs. You may
exercise control over the length of a timeout using the
@AccessTimeout
annotation, which takes a value of
java.util.concurrent.TimeUnit
. Thus, we might be able to
rework the code in the first implementation of the ChatRoom
to take advantage of this concurrency control.
@javax.ejb.Singleton
@
javax.ejb.
Lock(WRITE)
public class ChatRoom {
private java.util.Map<User,Collection<String>> userComments;
@PostConstruct
public void setup (){
userComments = new java.util.concurrent.ConcurrentHashMap<User,Collection<String>>();
/* ...*/
}
public void join(User usr){ /* ...*/ }
public void disconnect(User usr){ /* ...*/ }
public void say(String msg){ /* ...*/ }
@javax.ejb.Lock(READ)
public int getCountOfActiveUsers(){ /* ... run through the map and count ... */ }
}
Naturally, such a ChatRoom
will ultimately die a memory
starved death as the number of users and posts overwhelms the
application server's memory. Some sort of expiration mechanism is in
order. For the sake of demonstration, let's imagine a periodic
garbage collector that runs through the ChatRoom
posts
and LRU destroys (or, perhaps using JPA and the EntityManager
.
persists and then destroys) old chat data.
The EJB Timer
EJB has had a Timer mechanism since EJB 2.1. However, it's always
worked in terms of millisecond intervals and been fairly tedious to
set up. The situation was a bit improved with EJB 3.0, but the fact
remained that timers were still essentially procedurally declared and
interval-based. If you want to set something up at the beginning of
the week, for example, then you faced challenges. EJB 3.1 improves
things by providing a declarative, flexible timer service that pulls
the rug out from under various other schedulers like Quartz or Flux.
For most simple scheduling needs, including CRON-like scheduling, EJB
3.1 is a fine solution. Let's revisit our ChatRoom
. We
want to schedule a bean to go through and garbage collect old data.
@javax.ejb.Singleton
public class ChatRoom {
private java.util.Map<User,Collection<String>> userComments;
@javax.ejb.Schedule(minute="1", hour="*")
public void cleanOutOldDataFromChatLogs() {
/** ... not reprinting all the existing code ... */
}
}
We've written a method that will go through and run a quick check on the data and - as necessary - expunge old /irrelevant chat logs so that we can keep things manageable. Here, we're using a declarative model to ensure that the method runs every hour. We could, of course, still inject the TimerService from EJB 3.0.
No-Interface Views
In EJB 3.0 beans were required to support at least one interface (a local or remote business interface) that would be used as the view of the bean to the clients of that bean. While indirection via interfaces is a very powerful technique, it sometimes just complicates things. In EJB 3.1 you may write a bean that has no interfaces. The client view is then the public methods as exposed by the class.
@javax.ejb.Stateless
public class Calculator {
public float add ( float a , float b) {
return a + b;
}
public float subtract (float a , float b){
return a - b ;
}
}
Clients of this bean may simply accquire as usual with injection and invoke it:
@javax.ejb.EJB
private Calculator calculator;
...
float result = calculator.add( 10 , 10 ) ;
...
Asynchronous Services
The simple way to handle scaling issues is... to simply not handle them (until capacity is available)! This approach is most famously characterized by the SEDA (staged event driven architecture) pattern, wherein bottlenecks are avoided by queuing work. This lets submissions of work being queued and the client proceed. If a component downstream takes a long time, and the system is under load, this pattern ensures that the slow component doesn't bring the system to its knees.
Another approach to scaling is to not block client invocations on
one-way message exchanges. Another approach still is to simply work
asynchronously, letting execution on the client proceed until a
result comes back. All of these approaches are embodied in the new
asynchronous support for services in EJB 3.1. A bean class, or
individual methods, may be annotated with the @javax.ejb.Asynchronous
annotation to tell the container that clients should not block on the
result of the invocation. This lets the client proceed
instantaneously, and - in theory - it would enable a container to
buffer the work until it's better able to perform it.
If a bean class, or a business interfaces, is annotated with
@Asynchronous,
then all methods on the bean are
deferred. Otherwise, only methods that are annotated with
@Asynchronous
are deferred. Asynchronous methods may
return void, or an instance of java.util.concurrent.Future<V>.
The client may consult the instance of Future<V>
for the result at any time later, but the result of the invocation
proceeds instantly in the client thread, and doesn't block. This way,
if the EJB takes an hour or two, then it doesn't matter - the client
is unaffected. Conceptually this is the same as calling a service
with the sole job of sending the request to a JMS queue.
The instance of Future<V>
may be used to cancel
the job, or wait for the result. A client's transactional context is
not propagated to the asynchronous method. REQUIRED
,
then, is effectively REQUIRES_NEW
for the asynchronous
method.
Let's take a look at an example: we want to build a service that talks to several other web services and aggregates the results. We want the results, but we can't leave the client request (a web page, perhaps?) hanging. Such a service might look as follows:
@javax.ejb.Stateless
public CarHotelAndAirLineBookingServiceBean implements CarHotelAndAirLineBookingService {
@javax.ejb.Asynchronous
public Future<BookingConfirmation> bookCarHotelAndAirLine( Car rental, Hotel hotel, AirLine airLine) {
/** ... */
}
}
In the client - a JSF action - we might invoke such a service as follows:
@Named
public BookingAction {
@javax.ejb.EJB private CarHotelAndAirLineBookingServiceBean bookingService;
private Future<BookingConfirmation> confirmation;
public String makeRequest(){
Hotel hotelSelection = ... ;
Car rentalSelection = ... ;
AirLine airLineSelection = ... ;
confirmation = bookingService.bookCarHotelAndAirLine(
rentalSelection, hotelSelection, airLineSelection ) ;
return "showConfirmationPending";
}
public Future<BookingConfirmation> getConfirmation(){ /* ... */ }
/* ... */
}
Simplified Deployment
EJB 3.1 also marks the first version of the specification to
provide a dramatically simpler deployment paradigm with support for
deployment inside a in .WAR
file. A class with a
component-defining annotation becomes an enterprise bean component
when packaged within WEB-INF/classes
or as a .jar in
WEB-INF/lib.
Enterprise beans may also be defined using
a WEB-INF/ejb-jar.xml
file. Beans packaged in a .WAR
share a single namespace, and become part of the .WAR
's
environment. Packaging a .jar in WEB-INF/lib
is thus
semantically equivalent to putting the classes in WEB-INF/classes
.
Another novel feature of the new specification is EJB Lite. For many applications, EJB technology is more than is required. EJB Lite provides a more streamlined subset centered around the use of session-bean components. It provides a way to use EJB components in an embeddable fashion, too, which simplifies unit tests. EJB Lite supports stateless, stateful and singleton session beans. Beans may have a local-, or no-interface view. They may work with interceptors, and use container services like transactions and security.
EJB 3.1 is a powerful tool in the developers toolkit, and is clearly being evolved into something that'll meet the 80% use cases of most applications. The future looks good for the specification. This is also the first release of the specification to target certain specification features for future removal using the Java SE pruning mechanism. Aspects of the specification that have been marked for possible future removal include pre-3.0 support for older forms of container-managed and bean-managed persistence, the EJB 2.1 client view of entity beans, EJB QL (the query language from EJB 2.1), and JAX-RPC based web service support (both endpoints and client views; they were added in J2EE 1.4)
Clearly, EJB 3.1 is a compelling, backwards-compatible upgrade, and represents a fine next step from the work started in JSR 220 (EJB 3.0) more than 5 years ago.