EJB 3.1 - A Significant Step Towards Maturity

Enterprise Java Beans (EJB) is a server-side component architecture for the Java Enterprise Edition (Java EE) platform, aiming to enable rapid and simplified development for distributed, transactional, secure and portable applications.

Introduction

Enterprise Java Beans (EJB) is a server-side component architecture for the Java Enterprise Edition (Java EE) platform, aiming to enable rapid and simplified development for distributed, transactional, secure and portable applications.

EJB experienced wide adoption in its version 2, reaching a level of maturity which made it capable to embrace the requirements of many enterprise applications. Despite its relative success, there were many critical voices accusing it of being overcomplicated. Lack of a good persistence strategy, long and tedious deployment descriptors, or limited capacity for testing, were some of the many times used arguments, causing a considerable number of developers to look for alternative technologies.

Sun reacted slowly, but it was able to come up with a greatly revised version of the specification, which has considerably enhanced its potential. EJB 3 dealt with most of the existing drawbacks, presenting solutions which got consensual acceptance among the community. EJB became a viable solution again, and many teams which had once put it aside are now using it.

Even though its success, EJB 3 did not go as far as it could. Going back to EJB 2.1, the spec was facing two main challenges: 1) to go through a massive restructuring in order to change existing features, like a powerful persistence framework as a substitute for Entity Beans, support for annotations as a substitute for deployment descriptors, drop of home interfaces, etc; 2) to introduce new solutions for problems not being dealt by the spec like support for Singletons, support for method interception or asynchronous calls to session beans, and to improve existing features like enhancing the Timer Service. Priority was correctly given to the restructuring- "we fist clean and rearrange the house, and only then we bring the new furniture"- even though there were some new features as well, like the support for interceptors. Now that the cleaning is done, it is time to benefit from it and to go one step beyond.

EJB 3.1 introduces a new set of features which intends to leverage the potential of the technology. It is, in my opinion, a release of major importance, which brings capabilities which were most wanted since long ago, making it more able to fulfill the needs of modern enterprise applications, and contributing for an increased adoption.

The EJB 3.1 Proposed Final Draft has been recently released and we are now close to its final version. This article will go through most of the new features, providing a detailed overview about each one of them.

No-Interface View

EJB 3.1 introduces the concept of a no-interface view, consisting of a variation of the Local view, which exposes all public methods of a bean class. Session Beans are not obliged to implement any interface anymore. The EJB container provides an implementation of a reference to a no-interface view, which allows the client to invoke any public method on the bean, and ensuring that transaction, security and interception behave as defined.

All beans' public methods (including the ones defined on the superclasses) are made available through the non-interface view. A client can obtain a reference to this view by dependency injection or by JNDI lookup, just like it happens with local or remote views.

Unlike local and remote views, where the reference consists of the respective local/remote business interface, the reference to a no-interface view is typed as the bean class.

The following code sample demonstrates how a servlet can easily make use of a no-interface view. The client references the no-interface view as a ByeEJB, that is, using the bean class. The EJB does not implement any interface (assume no deployment descriptor is provided). Last but not least, the reference to the EJB is obtained by simple dependency injection.

ByeServlet

(...)
@EJB
private ByeEJB byeEJB;
    
public String sayBye() {
    StringBuilder sb = new StringBuilder();
    sb.append("<html><head>");
    sb.append("<title>ByeBye</title>");  
    sb.append("</head><body>");
    sb.append("<h1>" + byeEJB.sayBye() + "</h1>");
    sb.append("</body></html>");
    return sb.toString();                       
} 
(...)

ByeEJB

@Stateless
public class ByeEJB {

    public String sayBye() {
        return "Bye!";
    }   
}

The fact that the reference's type is the bean class imposes limitations:

  • The client can never use the new operator to acquire the reference.
  • An EJBException will be thrown if any method other than the public method is invoked.
  • No assumption can be made about the internal implementation of the no-interface view. Even though the reference corresponds to the type of the bean class, there is no correspondence between the reference implementation and the bean implementation.

A reference to this view can be passed as parameter/return value of any local business interface or other no-interface view methods.

If the bean does not expose any local or remote views then the container must make a no-interface view available. If the bean exposes at least one local or remote view then the container does not provide a no-interface view unless explicitly requested, using the annotation LocalBean.

All public methods of the bean, and its superclasses, are exposed through the no-interface view. That means any public callback methods will be exposed as well, and therefore caution must be exercised at this level.

This feature will allow to avoid writing interfaces, simplifying development. It might be that the future will bring remote no-interface views.

Singleton

Most of the applications out there have experienced the need of having a singleton bean, that is, to have a bean that is instantiated once per application. A big number of vendors has provided support for such need, allowing the maximum number of a bean's instances to be specified in the deployment descriptor. It goes without saying that these workarounds break the "write once deploy everywhere" principle, and therefore it was up to the spec to standardize the support for such feature. EJB 3.1 is finally introducing singleton session beans.

There are now three types of session beans- stateless, stateful and singleton. Singleton session beans are identified by the Singleton annotation and are instantiated once per application. The existing instance is shared by clients and supports concurrent access.

The life of a singleton bean starts whenever the container performs its instantiation (by instantiation it is meant instantiation, dependency injection and execution of PostConstruct callbacks). By default the container is responsible for deciding whenever a singleton bean is created, but the developer can instruct the container to initialize the bean during application startup, using the Startup annotation. Moreover, the Startup annotation permits defining dependencies on other singleton beans. The container must initialize all startup singletons before starting to deliver any client requests.

The following example provides an overview about how dependencies work. The instantiation of singleton A is decided by the container as it does not contain the Startup annotation and no other singleton depends on it. Singleton B is instantiated during application startup and before singleton D and singleton E (E depends on D which depends on B). Even though singleton B does not have the Startup annotation, there are other singletons which have it and that depend on it. Singleton C is instantiated during application startup, before singleton E, and so does singleton D. Finally, singleton E will be the last one to be instantiated during the application startup.

@Singleton
public class A { (...) }


@Singleton
public class B { (...) }

@Startup
public class C { (...) }


@Startup(DependsOn="B")
@Singleton
public class D { (...) }


@Startup(DependsOn=({"C", "D"})
@Singleton
public class E { (...) }

Note that the order defined on a multiple dependency is not considered at runtime. For example, the fact that E depends on C and D does not mean that C will be instantiated before D. Should that be the case then D must have a dependency on C.

A singleton can define a startup dependency on a singleton that exists on another module.

The container is responsible for destroying all singletons during application shutdown by executing their PreDestroy callbacks. The startup dependencies are considered during this process, that is, if A depends on B then B will still be available whenever A is destroyed.

A singleton bean maintains state between client invocations, but this state does not survive application shutdown or container crashes. In order to deal with concurrent client invocations the developer must define a concurrency strategy. The spec defines two approaches:

  • Container-managed concurrency (CMC)- the container manages the access to the bean instance. This is the default strategy.
  • Bean-managed concurrency (BMC)- the container takes no intervention in managing concurrency, allowing concurrent access to the bean, and deferring client invocation synchronization to the developer. With BMCs it is legal to use synchronization primitives like synchronized and volatile in order to coordinate multiple threads from different clients.

Most of the times CMC will be the choice. The container manages concurrency using locking metadata. Each method is associated to a read or a write lock. A read lock indicates that the method can be accessed by many concurrent invocations. A write lock indicates that the method can only be invoked by one client at a time.

By default the value of the locking attribute is write. This can be changed by using the Lock annotation, which can be applied to a class, a business interface, or to a method. As usual, the value defined at the class level applies to all relevant methods unless a method has its own annotation.

Whenever a method with write locking has concurrent accesses, the container allows one of them to execute it and holds the other ones until the method becomes available. The clients on hold will wait indefinitely, unless the AccessTimeout annotation is used, which allows to define a maximum time (in milliseconds) that a client will wait, throwing a ConcurrentAccessTimeoutException as soon as the timeout is reached.

Following is presented a number of examples, illustrating the use of CMC singleton beans. Singleton A is explicitly defined as a CMC, even though it is not necessary because that is the default concurrency strategy. Singleton B does not define any concurrency strategy, which means that it is a CMC, and defines that the exposed methods make use of write locks. Like it happens with singleton A, singleton B would not need the use of the Lock annotation because by default all CMCs use a write lock. Singleton C uses read locks for all methods. The same for singleton D, with the difference that the method sayBye will have a write lock. Finally, singleton E makes use of write locks to all relevant methods, and any blocked client will get a ConcurrentAccessTimeoutException after being on hold for 10 seconds.

@Singleton
@ConcurrencyManagement(CONTAINER) 
public class A { (...) }


@Singleton
@Lock(WRITE)
public class B { (...) }


@Singleton
@Lock(READ)
public class C { (...) }


@Singleton
@Lock(READ)
public class D { 
(...)
@Lock(WRITE)
public String sayBye() { (...) }
(...)
 }


@Singleton
@AccessTimeout(10000)
public class E { (...) }

In case of clustering, there will be an instance of the singleton per every JVM where the application gets deployed.

Up to EJB 3, any system exception thrown by an EJB would cause the respective instance to be discarded. That does not apply to singleton beans, because they must remain active until the shutdown of the application. Therefore any system exception thrown on a business method or on a callback does not cause the instance to to be destroyed.

Like it happens with stateless beans, singletons can be exposed as web services.

Asynchronous Invocations

Asynchronous invocations of session beans methods is one of the most important new features. It can be used with all types of session beans and from all bean views. The spec defines that with asynchronous invocations the control must return to the client before the container dispatches the invocation to the bean instance. This takes the use of session beans into a whole new level, permitting the developer to take benefit from the potential of having asynchronous invocations to session beans, allowing a client to trigger parallel processing flows.

An asynchronous method is signaled through the Asynchronous annotation, which can be applied to a method or to a class. The following examples illustrate different use cases of the annotation. Bean A makes all its business methods asynchronous. Singleton B defines the method flushBye as asynchronous. For stateless C all methods invoked by its local interface Clocal are asynchronously invoked, but if invoked through the remote interface then they are synchronously invoked. Therefore the invocation of the same method behaves differently depending on the used interface. Last, for bean D the method flushBye is invoked asynchronously whenever invoked through the bean's local interface.

@Stateless
@Asynchronous
public class A { (...) }


@Singleton
public class B { 
(...) 
    @Asynchronous
    public void flushBye() { (...) }
(...)
}


@Stateless
public class C implements CLocal, CRemote { 

    public void flushBye() { (...) }

}
@Local
@Asynchronous
public interface CLocal { 
    public void flushBye();
} 
@Remote
public interface CRemote { 
    public void flushBye();
} 


@Stateless
public class D implements DLocal { (...) }

@Local
public interface DLocal {
(...) 
    @Asynchronous
    public void flushBye();
(...)
}

The return type of an asynchronous method must be void or Future<V>, being V the result value type. If void is used then the method cannot declare any application exception.

The interface Future was introduced with Java 5 and provides four methods:

  • cancel(boolean mayInterruptIfRunning)- attempts to cancel the execution of the asynchronous method. The container will attempt to cancel the invocation if it has not yet been dispatched. Should the cancellation be successful the method returns true, false otherwise.ThemayInterruptIfRunningflagcontrolswhether, inthecasethattheasynchronousinvocation cannotbecanceled, the target bean should have visibility to the client's cancel attempt.
  • get- returns the method's result whenever it gets complete. This method has two overloaded versions, one that blocks until the method completes, another which takes a timeout as parameter.
  • isCancelled- indicates if the method was cancelled.
  • isDone- indicates if the method was completed successfully.

The spec mandates that the container provides the class AsyncResult<V>, consisting of an implementation of the interface Future<V> which takes the result value as constructor's parameter.

@Asynchronous
public Future<String> sayBye() {
    String bye = executeLongQuery();
    return new AsyncResult<String>(bye);
}

The use of the Future<V> return type has only to be visible from the point of view of the client. So if method m is only defined as asynchronous on an interface then only the method declaration in the interface must return Future<V>, the one in the bean class (and in any other business interface) can define V as return type.

@Stateless
public class ByeEJB implements ByeLocal, ByeRemote { 

    public String sayBye() { (...) }

}

@Local
@Asynchronous
public interface sayBye { 
    public Future<String> flushBye();
} 

@Remote
public interface ByeRemote { 
    public String sayBye();
} 

The SessionContext interface has been enhanced with the method wasCancelCalled, which returns true if the method Future.cancel was invoked by t