EJB Core

The annotations described in this section are at the heart of EJBs. Just like other Java annotations, these are just pieces of metadata with which you decorate your POJO (Plain Old Java Objects) classes. But their real power is harnessed by the EJB container, which automatically lines up services for your components based on the type of EJB represented by the annotation.

Exploration of these implicit services provided by the EJB runtime and their corresponding annotation is what this book is all about - so you'll read more about these features as you progress. Some of these services include thread safety, concurrency, pooling, transactions, scheduling etc.

Some of the benefits of the annotation driven EJB components are

  • Clearly distinguishes different EJBs and the bean developer can make an appropriate choice depending upon the required business scenario.
  • Provides a simplified programming model based around POJOs, on top of which features are applied in a declarative fashion using annotations and the heavy lifting is delegated to the container
  • Promotes ease-of-use through Convention Over Configuration approach in (common throughout the Java EE platform) - enables sensible defaults which can be further refined by annotations or XML deployment descriptors

Without further ado, let's dive in....

@Stateless

It was EJB 3.0 (part of the Java EE 5 specification), which initiated its revival as a lightweight, POJO based programming tool and the @Stateless annotation was at the centre of it all.

It is just a simple annotation (metadata) which instructs the container to treat a class/interface as a Session Bean whose instances can be pooled. Apart from pooling, there are a host of generic services which a Stateless EJB is eligible for e.g. transactions, thread safety (to name a few)

package ejbap.core;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class StatelessBeanExample {

    @PersistenceContext
    private EntityManager em;

    public void deleteComment(String id){
        em.remove(id);
    }
}

What's critical to understand is that every time a method on the bean is invoked, a different (pooled) instance is chosen by the container (at random). Hence it goes without saying that, one should not store any state within the bean using instance variables. This is because there is no guarantee as to whether or not the bean which was commissioned to your application for executing the current request will be chosen for the subsequent invocations as well. This is the idea behind the name Stateless: all instances of a Stateless session bean are equivalent

Being unable to store state is not necessarily bad. It actually results in important characteristics such as

Scalability - The container can be tuned to pool instances of your EJBs (more on this later). This is only restricted by the amount of resources (memory and processing capacity) on the physical machinehosting the server.

Thread safety by default - No two threads can ever act on the same instance of the bean, since the container always spins up a new bean instance with each new request.

@Stateful

This annotation defines a session bean whose instances can hold state specific to a client and remain attached to the very same client throughout its life cycle. The container also renders other implicit services like thread safety, transactions etc.

package ejbap.core;

import java.util.UUID;
import javax.annotation.PostConstruct;
import javax.ejb.Stateful;

@Stateful
public class StatefulBeanExample {

    private String changeID;

    @PostConstruct //will be explained later
    public void init(){
        changeID = UUID.randomUUID().toString(); 
    }

    public String getChangeID(){
        //the client gets the same value on subsequent invocations
        return changeID;
    }

    public void updateProperty(String key, Object value){
        //business logic
        System.out.println("Changes made under ID: "+ changeID);
    }
}

Other key characteristics of Stateful beans

Passivation - Stateful session beans are capable of persisting their state to disk and de-serializing it from the passivated state, back into memory. This can be controlled using the @PrePassivate and @PostActivate annotations [discussed later]

From EJB 3.2 onwards, one can also configure a Stateful bean to not get passivated at all.

package ejbap.core;

import javax.ejb.Stateful;

@Stateful(passivationCapable = false)
public class StatefulNoPassivationBeanExample {
    //details omitted
}

Removal hooks - A Stateless bean does not provide explicit call back/hook (annotation) which can control its eviction/removal - it's entirely managed by the EJB runtime. In the case of Stateful beans, the bean developer can provide a method annotated with @Remove which and the trigger bean [discussed later]

JPA Extended Persistence scope - A JPA Entity Manager injected within a Stateful can be configured to leverage Extended Persistence scope which ensures that the JPA entities handled by the Entity Manager remain managed throughout the lifecycle of the EJB i.e. they are not limited to transaction boundaries as is the case with entity managers associates with Stateless EJBs

package ejbap.core;

import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;

@Stateful
public class StatefulBeanWithExtendedPersistenceContext {

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    EntityManager em;

    public void findUser(final String userID){
        //use EM to find user
        //User user = em.find(User.class, userID);
    }
}

@Singleton

Think of @Singleton as a shortcut for producing an implementation of the Singleton pattern (which is often hard to get right). Just a single annotation is enough to ensure that

  • The container creates a single instance of your bean (scoped to your container JVM of course)
  • The container provides implicit thread safety by default
package ejbap.core;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;

@Singleton
public class SingletonBeanExample {
    private Properties config;

    @PostConstruct //will be explained later
    public void init(){
        config = new Properties();
        try {
            //cached (singleton)
            config.load(new FileInputStream("appconfig.xml"));
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public String getPropertyValue(String key){
        return config.getProperty(key);
    }
}

Singleton beans thread safety

A Singleton EJB is a lone warrior. Because of this trait, its is important to configure them appropriately in terms of their concurrency characteristics.

As far as thread safety configuration is concerned, Singleton beans support sensible defaults

  • Container managed concurrency
  • Explicit write lock on each of the methods.

Some of the common use cases for using Singleton beans are bootstrapping global configuration, implementing custom caches, starting services on application loading etc.

The associated annotations such as @Startup, @DependsOn, @Lock and @ConcurrencyType have been explained in detail later.

@MessageDriven

This annotation is used to define a Message Driven Bean (a.k.a MDB). The @ActivationConfigProperty annotation works hand in hand with @MessageDriven and helps fine tune its usage [discussed later]

package ejbap.core;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(activationConfig = {
    //below mentioned annotation will be covered later
    @ActivationConfigProperty(propertyName = "destinationLookup",
            propertyValue = "java:comp/env/myQueue"),
    @ActivationConfigProperty(propertyName = "destinationType",
            propertyValue = "javax.jms.Queue")
})
public class MDBExampleBean implements MessageListener{

    @Override
    public void onMessage(Message msg) {
        try {
            print(msg);
        } catch (JMSException ex) {
            throw new RuntimeException(ex);
        }
    }

    private void print(Message msg) throws JMSException{
        System.out.println("Message ID "+ msg.getJMSMessageID());
        System.out.println("Message type "+ msg.getJMSType());
    }

}

Why MDBs ?

Message Driven beans are generally used in conjunction with JMS (Java Message Service) where JMS acts as a messaging provider/router. Sender (a JMS message producer) uses the JMS API to send messages to JMS specific destinations (queues/topics) and the MDB in turn is tuned to listen to these destinations.

In general, Message Driven Beans provide the following benefits

Ability to consume and process messages in an asynchronous manner

  • The message sender does not directly interact with a MDB. A MDB is invoked by the EJB container in response to a message sent to the component/destination which the MDB is listening to.

Sequence of events

  1. The sender sends the message and it's associated thread returns (frees up) as soon as the message sending process (i.e. the method call) completes
  2. The message consumption and business logic execution is carried out by the MDB in background by a completely different thread (managed by the EJB container)

Completely decouples the sender and receiver - the sender just knows where to send the message. It's not aware of the specifics of the MDB. Similarly, all the MDB knows about is the message it is supposed to process

Reilability - It offers various degrees of reliability by providing facilities for persistence and acknowledgement of messages which in turn ensures guaranteed message delivery with once-and-only-once semantics.

M for MDB, M for myth ...

MDBs are not part of the JMS specification or coupled with JMS by any means. This is a misconception. Just like Stateless session beans, MDBs are pooled components which can process messages in an asynchronous fashion and can listen to any end point (including a JMS queue or destination) for which there is a compliant JCA resource adapter. In fact, this has been the case since EJB 2.1 and is made possible by the JCA (Java Connector Architecture) specification.

JMS based MDBs in action

How does JCA enable the concept of Generic MDBs ?

JCA based MDB in action

  • JCA (Java Connector Architecture) defines MDB specific features
  • Just like in case of a JMS based MDB, a JCA based MDB also needs to implement an interface and define activation properties (both are specific to the JCA Resource Adapter implementation)
  • The external system sends a message which the Resource Adapter accepts via its implementation of the inbound JCA contract and this message is relayed to an internal endpoint (this is again specific to the JCA adapter implementation)
  • The MDB registered to this endpoint kicks in, and executes the business logic

What's next ?

The next chapter will dwell into the details of how the EJB container manages the lifecycle of different instances of stateless, stateful, singleton and message driven EJBs.

results matching ""

    No results matching ""