EJB(s) rarely exist in isolation. Their implementations often need to call other EJB(s) and require a way to look up these EJB(s). The EJB 3.0 specification allows an EJB to declare references to other EJB(s), either in its deployment descriptor, or by using the @EJB annotation. This avoids the need to hard code the global JNDI names or locations of the referenced beans in the EJB implementation code. The references can be customized by modifying the deployment descriptor or using a deployment plan. This makes the EJB a more reusable component.
Until EJb 2.x, we would have used <ejb-ref> element in the ejb-jar.xml descriptor to map references to other EJB(s) into the local java:comp/env environment. The application code then uses logical JNDI names in the java:comp/env namespace, and the deployment descriptor specifies how the container should resolve these names.
Setting up an EJB reference in deployment descriptors requires coordinated changes to the code, ejb-jar.xml descriptor, and sometimes weblogic-ejb-jar.xml descriptor as well. The annotation and dependency injection features introduced in EJB 3.0 are much more convenient. The @EJB annotation keeps the declaration of an EJB reference within the code, but the mapping can still be overridden if necessary in a deployment descriptor.
Referring to EJB Components in the Same Application
Client can obtain a reference to the SLSB in any of three options here:
- First, the reference can be injected into the client via an @EJB annotation.
- Second, the reference can be bound into the client’s local java:comp/env environment, using the @EJB annotation at class-level or a deployment descriptor entry, and the client can retrieve the reference with a JNDI lookup.
- Third, the client can look up the SLSB in the server’s global JNDI tree. The client can also use the EJB 2.1 client view to look up the SLSB Home interface, using any of the three methods, and call create(). (The EJB 2.1 client view allows EJB 2.x clients to call EJB 3.0 beans, providing backward compatibility, as required by the specification).
EJBs can, of course, look up each other directly using known global JNDI names.
Lookup using Global JNDI
Context ctx = new InitialContext();
MyOtherEJB myOtherEJB = (MyOtherEJB)ctx.lookup("myejbs.MyOtherEJB");
This technique increases the coupling between EJB components because the global JNDI name of the referenced EJB is hard coded in multiple places. When a global JNDI lookup is used directly, the container knows nothing of the reference.
Alternatively, an EJB reference can be declared to the container. The container resolves the target EJB during deployment, and makes it available in the local java:comp/env environment.
EJB Reference in Container
MyOtherEJB myOtherEJB = (MyOtherEJB)ctx.lookup("java:comp/env/ejb/MyEJB");
EJB 3.0 has added a lookup() convenience method to EJBContext that provides direct access to an EJB’s environment without the need to use JNDI APIs, so the following will also work.
Lookup using EJBContext
MyOtherEJB myOtherEJB = (MyOtherEJB)context.lookup("ejb/MyEJB");
With the understanding on different ways of lookup() defined above, it is necessary to understand the three ways to map the environment entry to the target EJB.
In the first approach, the referring component declares the reference by including an <ejb-ref> element (for remote business interfaces) or an <ejb-local-ref> element (for local business interfaces) in the ejb-jar.xmldescriptor.
Using ejb-ref in ejb-jar.xml
The <ejb-local-reference-description> element in the weblogic-ejb-jar.xml descriptor is then used to map this reference to a particular global JNDI name.
Using ejb-ref in weblogic-ejb-jar.xml
ejb/MyOtherEJB <!-- Matches name in ejb-jar.xml -->
In the second approach, the referring component includes an <ejb-link> element in the <ejb-local-ref> element in ejb-jar.xml, specifying the name of the other EJB component.
,h5>Using ejb-link in ejb-jar.xml
With this approach there is no need to bind the referenced EJB into the global JNDI tree. No elements are required in the weblogic-ejb-jar.xml descriptor, which means the code is portable between containers. The container automatically maps the ejb/MyOtherEJB reference to the example.MyOtherEJB business interface and binds it to the referring EJB’s environment at java:comp/env/ejb/MyOtherEJB. The nameMyOtherEJBImpl is the logical name of the EJB set using the name element of the @Stateless or @Stateful annotations or, by default, the unqualified name of the bean class. The <ejb-link> element may also provide the name of the EJB archive file hosting the desired component.
Referring ejb in another jar
This is necessary only if two EJBs are in two different archive files and use the same logical name.
The third approach uses the @EJB annotation on the referring EJB class.
Using @EJB annotation
public class MyEJBImpl implements MyEJB
. . .
This approach is similar to using an <ejb-local-ref>, but has the benefit of requiring no deployment descriptor entries. A JNDI lookup of java:comp/env/ejb/MyOtherEJB is still required.
The @EJB annotation also supports a beanName element, which is interpreted in the same way as the value of an <ejb-link> element. In this case we have left it out, so the container will attempt to resolve the reference based on the type of the business interface. This will work, so long as a single EJB implements that business interface. The container will perform similar auto-wiring if we declare an <ejb-ref> or an <ejb-local-ref> without an <ejb-link> or a binding in weblogic-ejb-jar.xml.
Finally, EJB 3.0 dependency injection can be used to remove the need for an explicit lookup. The @EJB annotation is used again, but applied to a field of the referring EJB, rather than at class level.
DI without explicit lookup
public class MyEJBImpl implements MyEJB
private MyOtherEJB myOtherEJB;
It doesn’t get much simpler than this. When MyEJBImpl is deployed, the container will inject a reference to an EJB with the appropriate business interface. This is portable and requires no deployment descriptor entries. The EJB will be bound to java:comp/env/example.MyEJBImpl/myOtherEJB, but because the container has set the reference, there is no need for a JNDI lookup. The beanInterface and beanName elements can be used for disambiguation if the reference type is a superclass of the business interface, or if it is implemented by multiple EJBs in the application.
A further benefit of using dependency injection is that the implementation class can be constructed easily in a unit test context, where the value of the @EJB field is set directly by the unit test code. As we noted earlier in this chapter, it is a best practice to add access methods to your EJBs so that unit tests can simulate the container dependency injection and populate annotated fields.
Use @EJB annotations, or <ejb-ref>/<ejb-local-ref> deployment descriptor elements, rather than direct lookup using global JNDI names, to reduce coupling between components.
In most applications, using dependency injection with the @EJB annotation will be the simplest approach and will allow your EJB code to be unit tested easily.
Referring to External EJB Components
EJB components located in different enterprise application archive (.ear) files or other EJB jar files not part of the current application, are considered external components whether or not they run in the same WebLogic Server instance. These components are not part of the same deployment, so the <ejb-link> mechanism for referring to other components is not available. Components must use either global JNDI names or include appropriate <ejb-ref> elements in ejb-jar.xml and <ejb-reference-description> elements in the weblogic-ejb-jar.xml descriptor to look up external components. Note that local business interfaces can only be used within an application, so external components must be looked up and invoked through their remote business interfaces.
Because the components are external to the current application and more likely to change their global JNDI names, we suggest you use <ejb-ref> and <ejb-reference-description> elements instead of directly using global JNDI names.
The <ejb-reference-description> allows lookup of components in the host WebLogic Server’s global JNDI tree. If the location of the referenced EJB is truly remote, use WebLogic Server’s Foreign JNDI Provider feature to create a binding in the global JNDI tree.
Use <ejb-ref> and <ejb-reference-description> elements to access EJB components in other applications.
If the referenced EJB is truly remote, use WebLogic Server’s Foreign JNDI Provider feature to map its location into the local server’s global JNDI tree.
Calling Components by Reference
The Java EE specification requires that EJB components invoked through their remote interfaces must use pass-by-value semantics, meaning that method parameters are copied during the invocation. Changes made to a parameter in the bean method are not reflected in the caller’s version of the object. Copying method parameters is required in the case of a true remote invocation, of course, because the parameters are serialized by the underlying RMI infrastructure before being provided to the bean method. Pass-by-value semantics are also required between components located in different enterprise applications in the same Java virtual machine due to classloader constraints.
EJB components located in the same enterprise application archive (.ear) file are loaded by the same classloader and have the option of using pass-by-reference semantics for all invocations, eliminating the unnecessary copying of parameters passed during the invocation and improving performance. Set the <enable-call-by-reference> parameter to true in the weblogic-ejb-jar.xml descriptor file to enable this feature for each bean in your application. Local references always use pass-by-reference semantics and are unaffected by the <enable-call-by-reference> setting.
When you deploy an EJB with a remote interface and do not enable call by reference, WebLogic Server will issue a warning of the performance cost – BEA-010202.
|Note: Call By Reference
The default value of <enable-call-by-reference> is false in WebLogic Server 8.1 and later to comply with Sun’s Java EE licensing policy changes that require all Java EE compatible servers to support the specification with their out-of-the-box configuration. Be sure to set <enable-call-by-reference> to true for all beans in your application that have remote business interfaces to avoid parameter copying unless your application requires copying for functional correctness.