The introduction of dependency injection in JavaEE5 did reduce the need for doing a JNDI lookup. However, there are times when the users / clients had to rely on the good old JNDI lookup to acquire EJB references. Typical examples of such clients performing lookup of EJBs include (a) a JavaEE component from a different application and (b) a Java SE client.
The problem was such clients had to use “global” jndi name to lookup the target bean. All along the ejb specifications had been silent about portability of such global jndi names. This allowed each vendor to assign a global jndi names to EJBs in a vendor specific way. This meant that the client code that performed a lookup using global JNDI names were inherently non portable across appserver vendor implementations.
For example, the following code (part of a non JavaEE client) assumes that FooBean has been mapped to the global jndi name: "_app1_mod1_FooBean"
|
public static void main(String[] args) InitialContext ic =
new InitialContext();
//Use fooBean |
This code might break if the target bean was deployed to a different JavaEE server.
Portable Global JNDI name in EJB 3.1
EJB 3.1 solves the above problem by mandating that every container must assign (at least one) well defined global JNDI names to EJBs.The general syntax of a (portable) global JNDI name of an EJB is of the form:
java:global/[<application-name>]/<module-name>/<bean-name>!<fully-qualified-bean-interface-name>
In addition to the above name, if the EJB exposes just a single client view (that is it implements just one interface or the no interface view), the container is also mandated to map the bean to
java:global/[<application-name>]/<module-name>/<bean-name>
Where
- <aplication-name> defaults to the bundle name (.ear file name) without the bundle extension. This can be overridden in application.xml. Also, <application-name> is applicable only if the bean is packaged inside a .ear file.
- <module-name> defaults to bundle name (.war or .jar) without the bundle extension. Again, this can be overridden in ejb-jar.xml.
- <bean-name> defaults to the unqualified class name of the bean. However, if @Stateful or @Stateless or @Singleton uses the name attribute, then the value specified there will be used as the bean name.
Example 1:
Assuming
that the following classes and interfaces are packaged inside hello.jar
and
deployed as a stand alone module
| package
com.acme;
@Local public interface Hello { public String sayHello(); } package com.acme; @Local public interface GoodBye { public String sayBye(); } package com.acme; @Stateless public class MyEJB implements Hello, GoodBye { ... } package com.acme; @Singleton(name="HelloSingleton") public class HelloBean { ... } package com.acme; @Stateful public class ShoppingCart ... } package com.acme; public class Util { //Not a EJB or Servlet ... } |
The
following portable jndi names are then made available by the
container to the clients:
|
java:global/hello/MyEJB!com.acme.Hello java:global/hello/HelloSingleton java:global/hello/ShoppingCart |
Note:
- Since MyEJB implements more than one interface, the global jndi names must include the implemented interface
- For HelloBean, the value specified in the name() attribute is used as <bean-name>
- Since HelloBean bean exposes a no interface view, the qualified bean class name is used as <fully-qualified-bean-intf-name>
- Since ShoppingCart bean exposes a no interface view, the qualified bean class name is used as <fully-qualified-bean-intf-name>
- Since, the application is
deployed as a standalone module, <app-name>
is not used in the global jndi name.
Client code:
| InitialContext
ic = new InitialContext();
Hello = (Hello) ic.lookup("java:global/hello/MyEJB!com.acme.Hello"); GoodBye = (GoodBye) ic.lookup("java:global/hello/MyEJB!com.acme.GoodBye"); ShoppingCart = (ShoppingCart) ic.lookup("java:global/hello/ShoppingCart"); |
You
can also use the portable jndi names in injection too
|
@EJB(lookupName="java:global/hello/ShoppingCart") |
More name spaces
java:app and java:module
Why do we need these two? In the case of java:global, the name contains a hard coded <app-name> and (or) <module-name>. Assuming, that <app-name> and <module-name> are not specified in .xml, re-packaging the bean and client into a different module breaks the client. For example, if FooBean was originally packaged inside foo.jar, the client that looked up using ic.lookup("java:global/foo/FooBean") will break if FooBean is re-packaged inside module2.jar
Since, most of the time the beans and the clients are colocated in the same application or even within the same module, EJB 3.1 allows accessing these colocated beans in a easier way. The spec defines two more name spaces called java:app and java:module.
java:module allows a component executing within a Java EE application to access a namespace
rooted below the <module-name> portion of the namespace corresponding to its module.
You can think of java:module as the jndi sub-context that is rooted under the "current module" in which the client is located. java:module differs from java:global in the sense that the client can access only those beans that are packaged inside the same module as the client. If a component in module1 has to lookup a component in module2, then it has to use java:global (or java:app).
Similarly, can think of java:app as the jndi sub-context that is rooted under the "current app" in which the client is located.
The java:app and java:module names are of the form:
java:app/<module-name>/<bean-name>!<fully-qualified-intf-name>
and
java:module/<bean-name>!<fully-qualified-intf-name>
In our example, the Util class can access other components as
follows:
|
java:app/hello/MyEJB!com.acme.Hello java:app/hello/HelloSingleton java:app/hello/ShoppingCart |
java:module/MyEJB!com.acme.Hello java:module/HelloSingleton java:module/ShoppingCart |
Running the sample application
- Save and unzip portable-jndi-app.zip
- cd portable-jndi-name
- mvn install will build the application. The .war file will be under target directory
- <v3-install-dir>/bin/asadmin deploy --force=true target/portable-jndi-app.war
- Open a browser and access localhost:8080/webejb/Example1Servlet
Also, I have used mvn as the build / packing tool.
References
[1] EJB 3.1 Specification
[2] Ken Saks' note on portable jndi name