EJB 3 Advanced Concepts

In this topic we discuss how the container works and the concept of managed services, I also will be discussing dependency injection, JNDI lookups, EJB interceptors and the EJB timer service.

EJB centers on the idea of managed objects, beans are just annotated POJOs themselves. When a client invokes an EJB method using the bean interface it does not work directly on the bean instance, the container makes beans special by acting as a proxy between the client and the actual bean instance. This enables the container to provide EJB services to the client on behalf of the bean instance. For each bean instance the container automatically generates a proxy called an EJB Object. The EJB object has access to all the functionality of the container, including the JNDI registry, security, transaction management, thread pools, session management and pretty much anything else that is necessary to provide EJB services, you can see this in a diagram below

For session beans the client interacts with the EJB object through the business interface, for MDBs the EJB object or message endpoint sits between the message provider and the bean instance.

Services like transaction management, security, dependency injection, etc are meant to be overlaid on the bean through configuration, However there might be times when the bean needs to access the container directly, this is when you can use the EJB context. The javax.ejb.EJBContext interface is the backdoor to the container. The EJBContext interface is below

EJBContext interface

public interface EJBContext {

  public Principle getCallerPrincipal();
  public boolean isCallerInRole(String roleName);
  public EJBHome getEJBHome();

  public EJBLocalHome getEJBLocalHome();
  public boolean getRollbackOnly();
  public UserTransaction getUserTransaction();
  public void setRollbackOnly();
  public TimerService getTimerService();

  public Object lookup(String name);
}

EJBContext Methods
getCallerPrincipal
isCallerInRole
These methods are used in security
getEJBHome
getEJBLocalHome
These methods are used to obtain the remote home and local home interfaces, they are used mainly for backward compatibility
getRollbackOnly
setRollbackOnly
These are used for container-managed transactions
getUserTransaction Used for bean-managed transactions
getTimerService Used to get access to the EJB timer service
lookup This method is used to get references to objects stored in the JNDI registry, due to dependency injection (DI) this is hardy used

Both session and MDBs have their own subclass of the javax.ejb.EJBContext as shown below

To access the appropriate Context you can use dependency injection as seen below

SessionContext

@Stateless
public class PlaceBidBean implements PlaceBid {

  @Resource
  SessionContext context;

  ...
}

Note: this adds a number of methods that are session specific
getBusinessObject
getEJBLocalObject - used for EJB 2 beans
getEJBObject - used for EJB 2 beans
getInvokedBusinessInterface
getMessageContext

MessageDrivenContext

@MessageDriven
public class OrderBillingMDB {

  @Resource
  MessageDrivenContext context;

  ...
}

Note: no additional methods but throws exceptions if the following methods are called isCallerRole, getEJBHome or getEJBLocalHome, it makes no sense calling these methods

DI and JNDI

The @Resource annotation is the most versatile mechanism for DI in EJB 3, as you are already aware it can be used for injecting JDBC data sources, JMS server resources and EJB contexts. It can also be used for e-mail server resources, environment entries, ORB reference, or even EJB references.

Normally you would setup a data source as follows (using JBoss), then access that data source via the @Resource annotation

web.xml <resource-ref>
  <res-ref-name>OracleDS</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
</resource-ref>
jboss-web.xml <resource-ref>
  <res-ref-name>OracleDS</res-ref-name>
  <jndi-name>java:OracleDS</jndi-name>
</resource-ref>
@Resource annotation @Stateless
public class PlaceBidBean implements PlaceBid {

  @Resource(name="OracleDS")
  private javax.sql.Datasource datasource;

The @Resource annotation definition is

@Resource definition @Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface Resource {
  public enum AuthenticationType {
    CONTAINER,
    APPLICATION
  }
  String name default "";
  Class type() default Object.class;
  AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
  boolean shareable() default true;
  String mappedName() default "";
  description() default "";
}
@Resource Parameters
AuthenticationType Defines the type of authentication for accessing the resource, container means that the container's security context is used for the resource. Application means that authentication for the resource must be provided by the application.
shareable specifies if the resource can be shared
description A description of the resource
mappedName A Vendor-specific name that the resource may be mapped to as opposed to the JNDI name

You can also use setter/getter injection instead of field injection

setter/getter injection @Stateless
public class PlaceBidBean implements PlaceBid {
  ...
  private Datasource dataSource;
  ...
  @Resource(name="OracleDS")
  public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
  }

  public DataSource getDataSource() {
    return dataSource;
  }

The two advantages for using setter/getter injection is

Here a number of examples using the @Resource annotation

JMS Resources @Resource(name="jms/actionBazaarQueue")
private Queue queue;
EJBContext @Resource EJBContext context;
@Resource SessionContext context;
@Resource MessageDrivenContext context;
Environment entries @Resource
private boolean censorship;

# The deployment descriptor would be, make sure the data types are compatible otherwise you will
# get a runtime exception
<env-entry>
  <env-entry-name>censorship</env-entry-name>
  <env-entry-type>java.lang.Boolean</env-entry-type>
  <env-entry-value>true</env-entry-value>
</env-entry>
E-Mail Resources @Resource(name="mail/ActionBazaar")
private javax.mail.Session mailSession;
Timer Service @Resource
javax.ejb.TimerService timerService;

If the superclass defines any dependencies on resources using the @Resource annotation, they are inherited by the subclass.

There are two ways to programmatically perform lookups

EJB Context (inside the container) @EJB(name="ejb.BidderAccountCreator", beanInterface = BidderAccountCreator.class)
@Stateless
public class GoldBidderManagerBean implements GoldBidderManager {
  @Resource SessionContext sessionContext;
  ...
  BidderAccountCreator = accountCreator = (BidderAccountCreator) sessionContext.lookup(
                                                                    "ejb/BidderAccountCreator");

  ...
}
JNDI initial context (outside the container)

Context context = new InitialContext();
BidderAccountCreator accountCreator = (BidderAccountCreator) context.lookup(
                                                                "java:comp/env/ejb/BidderAcountCreator");

Interceptors

Before I talk about interceptors, i want to talk about Aspect-Oriented Programming (AOP), most applications, common application code repeated across modules not necessary for solving core business problems are considered as infrastructure concerns. There are a number of common examples that would be coded in to each module logging, auditing, profiling and statistics. The common term used to describe these cases is crosscutting concerns - concerns that cut across application logic. An AOP system allows the separation of crosscutting concerns into their own modules, these modules are then applied across the relevant cross section of application code. EJB 3 supports AOP-like functionality by providing the ability to intercept business methods and lifecycle callbacks.

Interceptors are objects that are automatically triggered when an EJB method is invoked. EJB 3 interceptors are triggered at the beginning of a method and are around when the method returns, they can inspect the return value or any exceptions thrown by the method. They can be applied to both Session and Message-Drive beans.

Here is an simple interceptor example

Interceptor class public class ActionBazaarLogger {

  @AroundInvoke
  public Object logMethodEntry ( InvocationContext invocationContext ) throws Exception {

    System.out.println("Entering Method: " + invocationContext.getMethod().getName() );
    
    // Tell the container it can proceed normally
    return invocationContext.proceed();
  }
}
Using the interceptor @Stateless
Public class PlaceBidBean implements PlaceBid {
  ...
  // Interceptor will be nvoke when this method is called
  @Interceptors(ActionBazaarLogger.class)

  public void addBid(Bid bid) {
    ...
  }
}

The @Interceptor annotation can accept multiple interceptor classes, they are called in the order they are specified, you can also attach the interceptor to the whole class which means any method invoke will invoke the interceptor. To attach a interceptor to all EJB modules you need to specify it in a deployment descriptor. An @Interceptor class should have only one method that is designated as the around invoke method.

Attach interceptor to all EJB modules <assembly-descriptor>
  <interceptor-binding>
    <ejb-name>*</ejb-name>
    <interceptor-class>action.bazaar.buslogic.ActionBazaarLogger</interceptor-class>
  </interceptor-binding>
</assembly-descriptor>

By default interceptors are called from the larger scope to the smaller scope: Default->Class->Method, there ia a parameter to specify the order called interceptor-order. You can also exclude interceptors by using the annotation @ExcludeDefaultInterceptors or @ExcludeClassInterceptors.

The InvocationContext interface passed in as the single parameter to the method provides a number of features that makes the AOP mechanism extremely flexible. The above example uses the two of the methods included in the InvocationContext interface, the getMethod().getName() and the proceed() method which tells the container that it should proceed to the next interceptor in the execution chain or call the intercepted business method, not calling the method will bring the processing to a halt and avoid the business method from being called, you could use this for a security validation for an example the interceptor method prevents the intercepted business method from being executed if the security validation fails.

The InvocationContext interface definition highlights more useful methods

InvocationContext interface

public interface InvocationContext {
  public Object getTarget();
  public Method getMethod();
  public Object[] getParameters();
  public void setParameters(Object[]);
  public java.util.Map<String,Object> getContextData();
  public Object proceed() throws Exception;
}

Notes:

getMethod - returns the method of the bean class for which the interceptor was invoked
getParameters - returns the parameters passed to the intercepted method as an array of objects.
setParameters - allows us to change these values at runtime before they are passed to the method
getContextData - allows data to be used to communicate between interceptors chains (name and value pair)

Lifecycle callbacks are a form of interceptor as well, where interceptors are triggered when a business method is invoked a lifecycle is triggered when a bean moves from one lifecycle state to another, you can incorporate lifecycle events into interceptors.

Lifecycle callbacks methods in the interceptors class

public class ActionBazaarResourceLogger {

  @PostConstruct
  public void intialize (InvocationContext context) {
    System.out.println ("Allocating resources for bean: " + context.getTarget());
    context.proceed();
  }

  @PreDestroy
  public void cleanup (InvocationContext context) {
    System.out.println ("Releasing resources for bean: " + context.getTarget());
    context.proceed();
  }
}

Here is a summary table of both business methods interceptors and lifecycle callbacks

Supported Features LifecycleCallback methods Business Method Interceptor
Invocation Gets invoked when a certain lifecycle event occurs Gets invoked when a business method is called by a client
Location In a separate Interceptor class or in the bean class In the class or an interceptor class
Method Signature

# in a separate interceptor class.
  void <METHOD> (InvocationContext)

# In the bean class
  void <METHOD> ()

Object <METHOD> (InterceptorContext) throws Exception
Annotation @PreDestroy
@PostContruct
@PrePassivate
@PostActive
@AroundInvoke
Exception Handling May throw runtime exceptions but must not throw check exceptions, may catch and swallow exceptions. No other lifecycle callback methods are called if an exception is thrown May throw application or runtime exception
May catch and swallow runtime exceptions
No other business interceptor methods or the business method itself are called if an exception is thrown before calling the proceed method
Transaction and security context No security and transaction context. Share the same secruity and transaction context within which the original buisness method was invoked

EJB 3 Timer Service

The Unix cron utility is probably the most people recognize, in the Java Quartz is a good open source implementation. The EJB 3 timer service is based on the idea of a time-delayed callback. The container will automatically invoke the method on your behalf when the time interval you specified elapses, you can even call the method at regular intervals. Timers are only used with session beans and MDBs, because of there asynchronous stateless nature. Timers are persistent (can survive crashes and restarts), they are also transactional, which means that if a failure occurs in a timer method it rolls back the transaction.

Here is an example

Timer example public class PlaceBidBean implements PlaceBid {
  ...
  @Resource TimerService timerService;

  public void addBid(Bid Bid) {
    .. Code to add bid ...
    timerService.createTimer(15*60*1000, 15*60*1000, bid);
    ...
  }

  @Timeout
  public void monitorBid(Timer timer) {
    Bid bid = (Bid) timer.getInfo();
    ... Code to monitor the bid ...
  }
}

We use resource injection to get a timer service, we then schedule a timer service callback to occur every 15 minutes, by attaching the newly create Bid, at regular intervals the monitorBid method is called by the timer service which is designed with the @Timeout annotation. The monitorBid method retrieves the Bid instance attached as timer information and monitors the bid.

You can also get a timer service via the EJB context

Obtaining timer service @Resource SessionContext context;

TimerService timerService = context.getTimerService();

The timerService has four overloaded methods to add timers, the one above should initially trigger in 15 minutes (15*60*1000 milliseconds) and repeat every 15 minutes and added a Bid instance as Timer information. The Timer definition is below, I will leave you to investigate further

Timer definition public interface javax.ejb.TimerService {
  public Timer createTimer(long duration, java.io.Serializable info);
  public Timer createTimer(long initialDuration, long intervalDuration, java.io.Serializable info);
  public Timer createTimer(java.util.Date expiration, java.io.Serializable info);
  public Timer createTimer(java.util.Date initialExpiration, long intervalDuration, java.io.Serializable info);
  public Collection getTimers();
}

A bean can have at most only one @Timeout method, which can either be specified by annotation or deployment descriptor (timeout-method), the timeout interface is below

timeout interface public interface javax.ejb.Timer {

  public void cancel();
  public long getTimeRemaining();
  public java.util.Date getNextTimeout();
  public javax.ejb.TimerHandle getHandle();
  public java.io.Serializable getInfo();
}

The above timer does not offer a fully feature rich scheduling like Quartz or Flux but can be use in some situations, the below is what it can offer and cannot offer