Sunday, September 26, 2010

EJB Programmatic Lookup

Table of Contents


In our previous post we learnt about EJB references and EJB injection. Even though EJB injection is a powerful container facility that eases modular application development, sometimes performing a programmatic EJB lookup is instead desirable.

Let's suppose, for example, that a set of distinct EJBs implement a common strategy, defined by a common business interface. Depending on the result of some choice algorithm (such as a business rule), a different strategy is chosen and hence a different EJB will be executed in the scope of a business process. In such a scenario the target EJB cannot be chosen at injection time since annotation elements (such as @EJB's) are defined at compilation time and deployment descriptors are defined at deployment time. The only solution to this problem is a programmatic JNDI lookup.

The same mechanisms described in the previous posts will apply. EJB references will be declared and linked against a name in your application private namespace using the @EJB annotation or the corresponding elements of your Java EE module deployment descriptor.

Lookup in the Application Private Namespace

The portable way to establish an indirection level between the namespace used in your lookup code and the target EJBs is using your application private namespace. This kind of indirection level is quite common in the Java EE platform: not only it is used for EJB references but for all sorts of resource references such as JDBC data sources, JMS queues, JavaMail sessions, etc.

In the case of EJBs, as seen in our previous post, you simply define a private name used by lookup and injection code of your application. This is a private application-scoped name and is a subelement of the java:comp/env JNDI entry. With the aid of the @EJB annotation and of the deployment descriptors, you can establish a link between this name and a target EJB. The only difference is that instead of relying on the container to inject a reference into a component of yours, your application algorithms will choose the appropriate EJB and will look it up dynamically.

As we've seen in part 2 of this series, the @EJB annotation can be used at type, method and field level to declare a reference to an EJB and, optionally, to link it to the target bean without the need of writing any deployment descriptor code.

If the case of dynamic programmatic JNDI lookup, instead of annotating a field (or a property) as an injection target, you can annotate a class (such as a Servlet) to establish a reference to an EJB. In the following example we'll see how to do it with both the @EJB annotation and the deployment descriptor.

Declaring a Reference to an EJB

In the test servlet we've used throughout our previous posts, we can use the @EJB annotation at class level to declare a reference to an EJB in the private name ejb/ejbLocalRef:

@EJB(name = "ejb/ejbLocalRef",
     beanInterface = es.reacts.SessionTest0Local.class,
     beanName = "EJBServer1.jar#SessionTest1")
public class ServletTest1 extends HttpServlet {
  [...]
}

The annotation in the previous example is functionally equivalent to the following deployment descriptor (in this case, the web.xml file) fragment:

<ejb-local-ref>
  <ejb-ref-name>ejb/ejbLocalRef</ejb-ref-name>
  <ejb-ref-type>Session</ejb-ref-type>
  <local>es.reacts.SessionTest0Local</local>
  <ejb-link>EJBServer1.jar#SessionTest1</ejb-link>
</ejb-local-ref>

The most important difference between the @EJB semantics in this example and in the examples in the previous post is that in this case we're providing all of the information required to establish the reference and the link to the target EJB without injecting nor even relying on information coming from the injection target (such as the beanInterface).

Although the annotation is applied at a class level, it is effectively equivalent to adding the corresponding deployment descriptor elements and, therefore, the declared reference will be available throughout your Java EE module. In this case, any other servlet in your Java EE web module will be able to inject or lookup the very same EJB referenced by the ejb/ejbLocalRef name:

@EJB(name = "ejb/ejbLocalRef")
SessionTest0Local lc4;

Additional "plumbing" here is not necessary since the reference declaration contains all of the information that is required to resolve the target EJB.

EJB Programmatic Lookup

Since a reference has been declared and linked, our code is now able to make a JNDI lookup and retrieve a reference to the desired business interface of our target EJB. The JNDI lookup code is the good ole lookup code we're used to (with a little difference we'll point out later):

InitialContext ctx = new InitialContext();
Object obj = ctx.lookup("java:comp/env/ejb/ejbLocalRef");
if (obj instanceof SessionTest0Local) {
  SessionTest0Local lc = (SessionTest0Local) obj;
  [...]
}

(* Please note that the previous fragment has been stripped of the required exception handling code.)

The good news with EJB 3.0 is that you don't need to narrow the reference using the PortableRemoteObject.narrow() method as was required by the EJB v. 2.1 Specification. In the example code we can directly test the reference with the instanceof operator and use a Java native cast to set a SessionTest0Local reference.

There's absolutely no difference between looking up local and remote business interfaces. Only in the case you rely on the deployment descriptor, the declaration and the linking of the EJB references will be performed using an <ejb-ref/> or an <ejb-local-ref/> according to the EJB business interface type. As far as it concerns your application, the lookup code will be identical.

Patterns

In scenarios in which you don't use EJB injection and rely on lookup instead, there are both advantages and disadvantages in using annotations or the deployment descriptors to declare and link EJB references.

The advantages of annotations is that they're easier to write and use than the corresponding deployment descriptors elements. Moreover, as far as it concerns my experience, IDE support for code autocompletion might be a better tool than some deployment descriptor editors which are "obscure", at best (with notable exceptions such as Oracle JDeveloper's and NetBeans'.)

The advantage of the deployment descriptor is that it can centralize resource references declarations. If the same EJB reference is used throughout the code of your Java EE module and is not confined to a single class, a best option is using the deployment descriptor to declare and link the EJB (and other resource) reference and avoid using annotations at all. This is a design choice that has to be taken carefully. In the uses cases in which a lookup is desirable, chances are that EJB linking might be performed in the application assembly and deployment stages. Having references well documented and declared in a central repository might still be preferable instead of having @EJB annotation scattered throughout your code and the task of the deployer might get considerably eased.

Next Steps

In the following part we'll see how application server-specific mechanisms will help us build modular Java EE applications and link EJB references to EJB remote business interfaces outside our application. We'll explore the tools provided by a leading Java EE compliant application server: Oracle WebLogic.


No comments: