Enterprise JavaBeans 3.1 with Contexts and Dependency Injection: The Perfect Synergy

By Adam Bien
Published October 2010

The enhanced simplification of Enterprise JavaBeans (EJB) 3.1 provides a perfect synergy with the power and flexibility of Contexts and Dependency Injection (CDI).

Downloads:

Introduction

Version 3.1 of the Enterprise JavaBeans (EJB) specification greatly simplifies the EJB specification and provides declarative aspects, such as transactions, security, threading, and asynchronous processing. Contexts and Dependency Injection (CDI) provides Dependency Injection (DI) power and flexibility. Both are part of the Java Platform, Enterprise Edition (Java EE) 6 specification and ready to use. This article describes why there is perfect synergy between EJB 3.1 and CDI.

Enterprise JavaBeans is Lightweight

The EJB 3.1 specification is a lightweight Plain Old Java Object (POJO) programming model. The only requirement is the existence of the @Stateless, the @Stateful, or the (less common) @Singleton annotation.

The simplest possible bean looks like this:



@Stateless
public class LightweightPojo {

    public String hello(){
        return "I'm very lightweight !";
    }
}


An EJB 3.1 bean is an annotated POJO. In addition, it can be deployed in a WAR file, side by side with your servlets, JavaServer Faces (JSF) 2 technology, and other Web components.

EJB 3.1 is especially lightweight, because all the run-time code already resides on the server. There is no need to deploy the container or any injection framework with your application, which makes the deployment and turnaround cycles really fast (usually less than 0.5 seconds) and the WAR file small.

A ready-to-deploy WAR file with the EJB 3.1 code above takes exactly 4 KB on the hard drive. Ultimately, the EJB 3.1 container (runtime environment) is surprisingly small as well. The installation size of a typical EJB 3.1 container takes about 1 MB on the hard drive.

In the case of GlassFish 3.0.1, these components are around 831 KB (ejb-container.jar), 12 KB (ejb-internal-api.jar), and 86 KB (ejb.security.jar)—all OSGi bundles. These OSGi modules are, of course, not self-contained; rather, they contain references to other infrastructural modules, such as transactions, concurrency, or deployment. But even a size of 50 MB would be vastly smaller than the common perception of the actual container size.

Common myths about "bloated J2EE" are, like the Java 2 Platform, Enterprise Edition (J2EE) name itself: several years (more than 5) old. With the advent of Java EE 5, the programming model was drastically simplified. In Java EE 5, an EJB 3 session bean was just an interface with implementation. In the EJB 3.1 specification, even the interface became superfluous.

Other Simplifications Provided by EJB 3.1

The true benefit of EJB 3.1 is declarative cross-cutting aspects, such as the single-threaded execution model with declarative transactions. And you don't even need to configure the beans. EJB 3.X beans come with reasonable defaults.

Java EE 6 follows the Convention over Configuration model, sometimes also called "Configuration by Exception." The Java EE 6 APIs, such as JSF 2, EJB 3.1, CDI, Java Persistence API (JPA) 2, and even J2EE Connector Architecture (JCA), follow this principle as well.

The method hello in the LightweightPojo bean gets executed in a transaction. The configuration for this behavior was inherited from the class-level defaults. Every EJB 3.X bean comes with the configuration: @TransactionAttribute(TransactionAttributeType.REQUIRED).

You can, of course, override this behavior on the class level or the method level using annotations. And you can overwrite the annotations with XML. But you don't need to. The provided defaults are good enough for the first iterations and sometimes even for the whole project.

The setting TransactionAttributeType.REQUIRED itself is interesting. If there is no transaction during the invocation of the method, the container will start one for you. If there already is one, it will just be reused. This behavior is particularly useful for chaining bean invocations together. The first bean will start a transaction for you, which gets propagated to all other beans. The method checkout() starts a transaction that gets "reused" by the OrderSystem and CustomerNotification session beans:



@Stateless
public class Cart {

    @EJB
    OrderSystem ordering;

    @EJB
    CustomerNotification notifier;

    public void checkout(){
        ordering.placeOrder();
        notifier.sendNotification();
    }
}


More about Transactions

Transactions are getting really interesting together with Java Message Service (JMS), JCA, JPA, or CDI event interactions. Because everything gets executed in the context of the Cart bean's transaction, the all-or-nothing principle applies. Either all changes made in OrderSystem and CustomerNotification are captured persistently, or none are.

Imagine that the OrderSystem creates and stores a JPA entity in the database and CustomerNotification notifies an external system via JMS. After the successful completion (no unchecked exceptions and no explicit rollback) of the Cart#checkout method, all changes are persistent. Either the Order entity gets persisted in the database and the notification is sent, or all changes are automatically rolled back for you.

The object identity is also preserved in a transaction. All beans participating in a transaction see the same entity instance. A modification-managed entity is visible to all other participants in the same transaction. This behavior can be achieved without EJB beans, but you get it absolutely for free with EJB beans. Also, this behavior comes without any additional configuration or effort.

Declarative transactions and their propagation are harder to explain than code. In practice, you can solve 80% of all use cases with the defaults, without any additional configuration. For the remaining edge cases, you can either override the behavior with annotations or XML configuration, or you can use Bean Managed Transactions (BMT). BMT allows you far finer control, but it requires you also to write some infrastructure code. In the real world, you will rarely need the BMT option.

Concurrency management makes the use of EJB 3 even more interesting. The container manages threads for you and ensures that only one thread (the request) gets access to a bean instance at a given time. Nonactive instances are pooled and reused when needed. The amount of threads and bean instances is configurable, so you can prevent "denial of service" attacks just by limiting the amount of threads or instances.

Developers don't really bother with monitoring and management in the first iterations. For debugging and stress testing, however, Java Management Extensions (JMX) is an invaluable resource. Since J2EE 1.4 and the introduction of the JSR-77 specification (J2EE Management) in 2004, all Java EE resources, and thus enterprise beans, must be exposed by the application server to JMX. After connecting with standard Java Development Kit (JDK) tools, such as VisualVM or JConsole, you can monitor the number of beans created, the number of successful transactions, the slowest methods, the fastest methods, average time, and so on. You get this monitoring "for free" without any configuration or coding.

Transparent concurrency, declarative transactions, and monitoring with the POJO programming model greatly simplify the development not only of enterprise applications. Java EE 6 is actually now so lean and simple that it became interesting for really small projects and "situational" software. You can easily write a full-stack "proof of concept" application in less than an hour.

Injecting EJB beans is not very powerful. Furthermore, EJB beans cannot be directly exposed to JSF or JSP without a little help from CDI. In these areas, CDI (JSR-299) together with Dependency Injection for Java (JSR-330, lead by SpringSource and Bob Lea of Guice fame) really shine. JSR-299 with JSR-330 should be considered as single unit; in the real world, CDI just relies on the JSR-330 annotations. Compared to JPA, JSR-330 could be considered as the Java Database Connectivity (JDBC) API, whereas JPA is comparable with CDI. In the real world, the distinction between both specifications doesn't really matter, and for developers, the distinction is almost transparent.

On the other hand, CDI doesn't provide any transactional, monitoring, or concurrency aspect out of the box. For transactions, JMX could be easily implemented with interceptors or decorators, but Java EE 6 does not provide them out of the box. CDI beans are not "active," as EJB 3 beans are. Managed CDI beans are usually executed in the context of JSF, Java API for RESTful Web Services (JAX-RS), or EJB 3.

CDI: The Natural Complement of EJB 3.1

In Java EE 6, CDI is the natural complement of EJB 3.1. For service-driven architectures, a stateless EJB 3.1 bean as boundary (Facade) with injected managed beans (controls) results in the simplest possible architecture.

The OrderSystem and CustomerNotification beans can be transformed easily to CDI managed beans. The @EJB annotation is removed and @Inject is used instead.



@Named
@Stateless
public class Cart {

    @Inject
    OrderSystem ordering;

    @Inject
    CustomerNotification notifier;

    public void checkout(){
        ordering.placeOrder();
        notifier.sendNotification();
    }
}


After the EJB 3-to-CDI migration, all the additional CDI power, such as extended dependency injection, events, and stereotypes, can be used with EJB beans.

Annotating the boundary (Cart) with the @Named annotation makes the Cart immediately visible for expression language (EL) expressions in JSP and JSF. This @Named annotation takes the simple name of the annotated class, puts the first character in lowercase, and exposes it directly to the JSF pages (or JSP). The Cart bean can be accessed directly, without any backed or managed beans, by the JSF pages:

<h:commandButton value="Check out!" action="#{cart.checkout}" />

As with EJB 3, CDI relies on the Convention over Configuration principle and doesn't require any further configuration. If there is only one possibility, it will be injected.

Any ambiguity results in meaningful deployment errors, such as WELD-001408 Injection point has unsatisfied dependencies. Injection point:... So, there are no more NullPointerException messages at run time on a bogus injection.

CDI also doesn't care whether the injected artifact is an interface or a class—as long as the type matches, the instance gets injected.

So you can start "small" just by directly injecting a concrete class. If there is a need for abstraction, the class can be turned into an interface (or abstract class). Each implementation of the desired functionality can be pushed into an independent interface realization. The CustomerNotification class can be converted easily into an interface:



public interface CustomerNotification {
   public void sendNotification();
}

Here is a remote implementation:
public class RemoteCustomerNotification implements CustomerNotification{

    //JMS resources injection

    @Override
    public void sendNotification() {
       //sending via JMS
        System.out.println("Remote event distribution!");
    }
}


And here is a local implementation (with CDI events discussed later in this article):



public class LocalCustomerNotification implements CustomerNotification{

    @Inject
    Event<String> event;

    @Override
    public void sendNotification() {
        event.fire("Order proceeded!");
      }
}


The injection of the CustomerNotification interface and the introduction of two implementations (and, therefore, injection opportunities) breaks the Convention over Configuration approach. You must now configure which implementation is going to be injected. In EJB 3.X, you could qualify the injection point, for example, @EJB(beanName="remote"), with the bean name as a String. Although it works well, Strings are brittle and can be misspelled easily.

In CDI, you could also use Strings for the qualification with the @Named annotation. A better choice is the use of custom annotations:



@Qualifier
@Retention(RUNTIME)
@Target({FIELD,TYPE})
public @interface Notification {
    enum Delivery{
     LOCAL, REMOTE
    }
    Delivery value();
}


The qualifier annotation is simple. The only requirement is the existence of the @Qualifier meta-annotation and @Retention(RUNTIME). The definition of attributes inside the annotation is optional, but it provides even more power.

Now it is sufficient to annotate the injection point:



@Named
@Stateless
public class Cart {

    @Inject @Notification(Notification.Delivery.LOCAL)
    CustomerNotification notifier;
}


Plus, annotate the implementation with the same qualifier annotation:



@Notification(Notification.Delivery.LOCAL)
public class LocalCustomerNotification implements CustomerNotification


If both match, the container injects the implementation. If there is no match, or there are too many possibilities, an exception is thrown. The annotation-driven approach is type-safe; misspelling results in compiler, not deployment, errors.

Annotations are "just" Java Platform, Standard Edition (Java SE), so you get optimal IDE support, such as auto-completion, refactoring, and so on. To configure the injection, you need to annotate the implementation of your choice as well as the injection point.

To change the injected class, however, you need to recompile your code. This requirement is more of a benefit than a disadvantage, but for a few situations (such as operational or political requirements), external XML configuration might be more appropriate.

XML configuration is also possible with CDI, although the solution is somewhat interesting. Instead of configuring the injection point, you deactivate all implementation of the interface with the @Alternative annotation. Doing this breaks the injection, because there is no implementation available for injection. With a few lines of XML in beans.xml, you can reactivate the bean of your choice.



<beans>
    <alternatives>
        <class>com.abien.ejbandcdi.control.RemoteCustomerNotification</class>
    </alternatives>
</beans>


The activation of the managed beans in beans.xml fixes the problem: A single implementation of the interface becomes available for injection again.

The injected instance of javax.enterprise.event.Event belongs to the CDI-implementation. The class Event can be considered to be a lightweight alternative to the java.beans.PropertyChangeSupport class. The event can be distributed with the invocation of the fire method. More precisely, there is no real event, just the payload:



   @Inject
    Event<String> event;

    @Override
    public void sendNotification() {
        event.fire("Order proceeded!");
      }


The event can be received by any managed bean and also by EJB beans. You need only provide a method with a single @Observes annotated parameter.



@Stateless
public class OrderListener {

    public void onOrder(@Observes String event){
        System.out.println("--: " + event);
    }
}


If the type of the fired payload matches with the annotated parameter, the event gets delivered; otherwise, it is ignored. Additional qualification with annotations is possible and works exactly as described for dependency injection.

The during attribute in the @Observes annotation allows you to select in which transactional phase the event gets delivered. The default setting is IN_PROGRESS, which causes an immediate event delivery regardless of the transaction outcome. The AFTER_SUCCESS configuration causes the delivery to occur only after successful transaction completion:

public void onOrder(@Observes(during=TransactionPhase.AFTER_SUCCESS) String event)

With the transaction-dependent event delivery, you can easily log successful or rolled-back transactions, or you can implement a batch-processing monitor. CDI events are a publish-subscribe implementation with transaction support, so they are a full-featured JMS replacement for local events. Although CDI events work only inside a single process (in the default case, CDI is extensible), they are perfectly suitable for decoupling packages from modules. The only dependency between the publisher and subscriber is the event itself and, optionally, the qualifier annotation.

Conclusion

In this article, we've introduced only excerpts of dependency injection, and events are a small portion of the whole CDI specification. Thankfully, you don't need to learn the whole specification to use basic injection. CDI is scalable—you can start small and, if needed, even swap the implementation using the official Service Provider Interface (SPI). Then, the sky is the limit.

The EJB 3.1 specification provides you with cron-like timer services, support for asynchronous processing (@Asynchronous annotation), JMX monitoring, declarative transactions, and a thread-safe programming model. These cross-cutting concerns are not covered by CDI and are free, without any programming effort, with EJB 3.1. On the other hand, type-safe dependency injection, events, and support for custom interceptors and decorators are covered much better with CDI.

In Java EE 6, the "EJB 3.1 with CDI" combination is the perfect strategy. EJB 3.1 provides the built-in cross-cutting aspects and CDI provides the additional DI power. With both specifications, your code becomes so simple that it is hardly possible to remove anything. You could implement all aspects without CDI, but then you would need to maintain them yourself, because they are not part of the Java EE specification. This approach would be hard to justify in real-world projects; both CDI and EJB 3.1 are part of the Web profile.

However, this situation could change in future Java EE releases. Transactions, monitoring, asynchronous processing, and so on could be factored out of the EJB 3.1 specification and pushed into portable CDI extensions.

In fact, CDI plus EJB 3 are even lighter than POJOs—just try to implement the same functionality with POJOs or any other framework. You will end up having either more code or more XML. In our case, only a single beans.xml file with the content <beans></beans> is required.

Because CDI and EJB 3.1 are part of Java EE 6, your WAR file contains only the business logic without any additional library or framework. The size of the deployable WAR file containing the sample introduced here is 16 KB. The whole project (source files, binaries, build scripts, and so on) is 242 KB . An average deployment takes approximately 500 ms.

See Also

About the Author

Consultant and author Adam Bien is an Expert Group member for the Java EE 6, EJB 3.1, and JPA 2.0 JSRs. He has worked with Java technology since JDK 1.0 and with servlets/EJB 1.0 in several large-scale projects, and he is now an architect and developer for Java SE, Java EE, and JavaFX projects. He has edited several books about JavaFX, J2EE, and Java EE, and he is the author of Real World Java EE Patterns—Rethinking Best Practices. Adam is also a Java Champion and JavaOne 2009 Rock Star.