Table of Contents
Introduction
The Enterprise JavaBeans Specification v. 3.0 introduces a simplified, annotation based API for EJB injection and lookup. EJB 3.0 are now POJOs and can be injected in other components (such as EJBs and Servlets) using simple annotations. EJB 3.0 is one of many other POJO-based specifications that made it to Java EE 6, such as JSR-229 "Contexts and Dependency Injection for the Java EE Platform" (formerly known as "Web Beans"). Java EE has never been so easy!
Nevertheless, while EJB 3.0 annotations hide away from the programmer the complexities of assembling a Java EE application and wiring all of its components, there are some subtleties that you should be aware of. Some of them, such as "global JNDI names", were address by the Enterprise JavaBeans Specification v. 3.1. Meanwhile, until Java EE 6 application servers are widely deployed, you might be exposed to the risk of writing code that relies upon non-portable application server specific behaviour.
To make things worse, some of the problems you might encounter show up when using an EJB local interface, which supposedly is an application-scoped interface of a bean of your own. Since local business interfaces actually imply a local method call (they use a pass-by-reference semantics and are restricted to the same JVM instance) such interfaces are the preferred choice when accessing the business interfaces of the components in your application. Often, this use case will make up most of the EJB calls in your Java EE application.
Declaring an EJB
According to the EJB v. 3.0 Simplified API Specification, when declaring a session bean you may use two annotations, depending on the session bean type:- @Stateless
- @Stateful
Both annotations share two common annotation elements: name and mappedName.
name
The annotation element name defines the bean "name" and defaults to the unqualified name of the bean class. The bean name must be unique in the scope of the module containing the EJB.mappedName
The EJB 3.0 Specification defines mappedName as a "product-specific name that the session bean should be mapped to." Often, application server use mappedName to map a session bean to a global JNDI name. The EJB 3.1 specification sort of deprecates the mappedName element and introduces the concept of "portable global JNDI name".We'll soon see how and when to use such metadata.
EJB References
To establish a reference to an EJB, you may use the @EJB annotation or the standard deployment descriptors (via <ejb-ref/> and <ejb-local-ref/>). The @EJB annotation is defined as follows:@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface EJB {
String name() default "";
Class beanInterface() default Object.class;
String beanName() default "";
String mappedName() default "";
String description() default "";
}
name
The name element defines the injected EJB name "in the application environment". The EJB name, then, is the location of the injected object in the private namespace java:comp/env. Its default value it's the fully qualified name of the annotated class field or property. When the @EJB annotation is used at class level the name element is mandatory.The name element is the equivalent of the <ejb-ref-name/> element in the deployment descriptor:
<ejb-ref>
<ejb-ref-name>bean name</ejb-ref-name>
[...]
</ejb-ref>
beanInterface
The beanInterface is the business interface type of the injected component. By default, it's the type of the annotated field or property. When the @EJB annotation is used at class level the beanInterface element is mandatory.The beanInterface element is the equivalent of the <remote/> or <local/> elements in the deployment descriptor:
<ejb-ref>
<ejb-ref-name>bean name</ejb-ref-name>
<remote>bean interface</remote>
[...]
</ejb-ref>
beanName
The beanName element specifies the bean "name" as declared in the @Stateful and @Stateless annotations via the name element or in the deployment descriptor via the <ejb-name/> element. The beanName element is most useful when more than one EJB implement the same business interface in an application: the beanName lets the developer reference a specific EJB in a specific module. The syntax to use in this case is:<ejb module>#<ejb name>
The bean name is resolved automatically if there's only one EJB implementing the requested business interface in the application.
The beanName element is the equivalent of the <ejb-link/> element in the deployment descriptor:
<ejb-ref>
<ejb-ref-name>bean name</ejb-ref-name>
<remote>bean interface</remote>
<ejb-link>linked ejb</ejb-link>
[...]
</ejb-ref>
mappedName
As in the case of the mappedName element of the @Stateless and @Stateful annotations, the mappedName is a product-specific metadata whose use is not portable.The mappedName element is the equivalent of the <mapped-name/> element in the deployment descriptor:
<ejb-ref>
<ejb-ref-name>bean name</ejb-ref-name>
<remote>bean interface</remote>
<ejb-link>linked ejb</ejb-link>
<mapped-name>mapped name</mapped-name>
[...]
</ejb-ref>
What About Beans in Other Applications?
If you review the mechanisms described so far, you should notice that there's no (portable) way to declare a dependency (an EJB reference) to a bean outside the application. The EJB Specification v. 3.1 addresses this problem and defines portable global JNDI names. No changes will be required for a compliant EJB v. 3.0 to be assigned a portable global JNDI name in a compliant EJB v. 3.1 container.Meanwhile, to wire a reference to an EJB outside your application, you should rely on the mechanisms provided by your application server.
Do Not Rely on Non-Portable Global JNDI Names to Lookup an EJB
As outlined in the previous sections, before EJB v. 3.1 there was no portable way to lookup an EJB with a global JNDI name and the deployer has to rely on application server specific tools and metadata to establish a link between an EJB reference and an EJB global JNDI name. Such a link, moreover, is necessary when establishing a reference to a bean outside your application.Even if it's appealing to avoid using the @EJB annotation elements (or their corresponding deployment descriptor elements) and use global JNDI names instead, you should always rely on EJB references and lookup in your java:comp/env private namespace. Your private namespace and the mapping mechanism provide you the level of indirection that will isolate your code from configuration changes.
This also means that, if you're restricted to EJB v. 3.0, you should always use the beanName (<ejb-link/>) mechanism and performing lookups in your private java:comp/env namespace. Many times I've heard the story of some developer inspecting the global JNDI tree of an application server to deduce the application server global JNDI naming scheme for EJBs and then experiencing some NamingException here and there. For this reason, avoid using the @EJB mappedName element and rely on name and beanName instead.
This also means that, if you're restricted to EJB v. 3.0, you should always use the beanName (<ejb-link/>) mechanism and performing lookups in your private java:comp/env namespace. Many times I've heard the story of some developer inspecting the global JNDI tree of an application server to deduce the application server global JNDI naming scheme for EJBs and then experiencing some NamingException here and there. For this reason, avoid using the @EJB mappedName element and rely on name and beanName instead.
Some application servers, for example, publish both remote and local EJB business interfaces with global JNDI names. Others simply don't. Oracle WebLogic is one of the latter. The EJB v. 3.0 specification, indeed, does not require a local business interface to be available in the global JNDI tree.
Looking Up Remote and Local Business Interfaces
As far as it concerns the caller, the process of acquiring a reference to a remote and to a local business interface should be identical. Unfortunately, that's not always the case if you don't rely only on portable mechanisms.References to local interfaces of EJBs can always be resolved with the beanName mechanism. This is not always the case for references to remote interfaces since such EJB might live outside your application. Here's, then, another reason not to rely on global JNDI names in your code: not only such a code would be not portable, but it would require you to use different strategies to lookup remote and local business interfaces (depending on the application server.) The business interface type would not be transparent and a change in a business interface type could break your code.
An Easy Pattern to Establish EJB References
Although they might be necessary in some scenarios, avoid EJB lookups and rely on @EJB references "auto-wiring". Respecting some easy patterns guarantees that your EJB references will be declared and satisfied in a completely automatic way:- Try to reduce the number of EJB that share business interfaces: if just one EJB implement a specific business interface in an application, an EJB reference can be automatically declared and wired with a default @EJB annotation.
- If sharing business interfaces improves your application design, define a clear naming policy for beans of your application and avoid duplications: if there's only one bean with a specific name, an EJB reference can be automatically declared and wired with a @EJB(beanName="[name]") annotation. If there's more than one bean with the same name, you'll be forced to use the @EJB(beanName="[ejb-module-path]#[ejb-name]") syntax when declaring references.
- If you need to lookup EJB references programmatically, declare a location for your EJBs in your local namespace using the @EJB name element (or the <ejb-ref-name/> element) and link it to the target EJB with the @EJB beanName element (or the <ejb-link/> element.)
Next Steps
In the following blog post, we'll examine some example to clarify how to use the EJB v. 3.0 APIs to declare EJB references and use dependency injection and programmatic lookup to obtain a reference to an EJB business interface.
No comments:
Post a Comment