Dealing with session bean global JNDI names is a common source of frustration for EJB component developers. Global JNDI names have always been outside the scope of the specs. Even though most Java EE implementations take the same high-level approach to their use, the actual syntax is typically different for each product. This leads to developer confusion and requires additional configuration steps for each deployment environment. Portability issues are also common as developers mistakenly embed the vendor-specific global JNDI names in the source code.

We're planning to solve these problems by defining portable global JNDI names for session beans.

Let's take a look at a simple example. Here's a basic stateless session bean exposing a remote business interface :

   1:  @Stateless
   2:  public class FooBean implements FooRemote { ... }

The two most common client scenarios are :

a) Remote EJB dependency from a Java EE component in a different application

   1:  @EJB 
   2:  FooRemote foo;

Since the target bean resides outside the application, there is no standard way to tell the deployment environment how to resolve the @EJB dependency.

b) Remote access from a "stand-alone" (non Java EE) component

   1:  FooRemote foo = (FooRemote) 
   2:    new InitialContext().lookup("<global_jndi_name>");

In this case there is no standard string to use in the context lookup.

Our proposed solution is to define a standard global namespace that has a well-defined name for each session bean. For example, if FooBean were deployed as fooejb.jar, its standard global JNDI name would be "java:global/fooejb/FooBean".

The revised, now portable, client examples become :

a) Remote EJB dependency from a Java EE component in a different application

   1:  @EJB(mappedName="java:global/fooejb/FooBean")  
   2:  FooRemote foo;

Note that the existence of portable global JNDI names does not mean that the Java EE component environment should be bypassed. There are still many advantages to using @EJB or ejb-ref as a way to declare the caller's dependency on an enterprise bean. An important one is the option to move the global JNDI name mapping information to a descriptor so that it's not hard-coded.

b) Remote access from a "stand-alone" (non Java EE) component

   1:  FooRemote foo = (FooRemote) 
   2:    new InitialContext().lookup("java:global/fooejb/FooBean");

Non Java EE clients do not have access to a component environment so they must perform an explicit global lookup. The spec-defined global name at least ensures that the lookup string will be uniform across Java EE products.

The previous examples deals with the remote view, but the portable global JNDI names apply to session beans exposing Local and no-interface views as well. This is especially useful in conjunction with the new EJB 3.1 embeddable API.

More generally, the syntax for determining a session bean's portable global JNDI name is :

 java:global[/<app-name>]/<module-name>/<bean-name>

Application name and module name both default to the unqualified name of their respective bundle, minus the file extension. Each name will also be configurable via a standard deployment descriptor element. Application name only applies when the application is deployed in a .ear file. Bean name defaults to the unqualified name of the bean class, unless set as the name() attribute on the component-defining annotation or in the ejb-name element of ejb-jar.xml.

The spec will also define a variation of the global name syntax for identifying a particular client view of a session bean. This is useful for the case that a single session bean exposes multiple client views. The only difference is an additional field for the fully-qualified interface name (or bean-class type in the case of the no-interface view) :

 java:global[/<app-name>]/<module-name>/<bean-name>/<intf-name>
Comments:

Great work!

Man, that would be perfect! Actually, my primary JNDI problem with Glassfish V2 is that if I deploy the same EJB in multiple different EARs, I cannot tell from one another without changing every single deployment descriptor.

The proposed solution would kill two issues at the same time: portability and "virtual hosting".

Do you guys have any idea about when it will be available in Glassfish?

Posted by Fabio Gomes on November 10, 2008 at 11:21 AM EST #

Uh oh. Sorry. I didn't notice you're a spec guy, not a Glassfish guy. :-)

Posted by Fabio Gomes on November 10, 2008 at 11:22 AM EST #

Hi Fabio,

Actually, I wear many hats :-) Thanks for the feedback. We have an early implementation of this available now within the EJB module in the update center. You can install it on top of the GlassFish v3 Prelude that
was just released. See my latest blog post for pointers. GlassFish v3 Prelude only supports enterprise
beans in .war files. We're still working on the full Java EE 6 release, which will support ejb-jars and .ears
as well.

Posted by Ken Saks on November 10, 2008 at 11:34 AM EST #

Isn't encoding deployment/development constructs into a global name a bit fragile?

If I've understood correctly, if someone takes my EJB and packages it up into an EAR file, anyone who wants to use it needs to know this, and know the name of the EAR file (or at least the JAR file).

If someone else takes my EJB and puts it into a different EAR file, the global name is different and any client I wrote for person A is no good for person B, unless I parameterized the JDNI name. And isn't that where we started?

Even if all that is true, having some naming standard is definitely an improvement :)

Posted by Matt on December 10, 2008 at 03:26 PM EST #

Hi Matt,

Thanks for the feedback. You're right that there's still some unique locator information that has to be conveyed from the deployer to the consumer. However, that's true of many public resources in the appserver, most notably web resources. The same servlet can be deployed in many .wars but the deployer assigns a context-root and relative uri and makes each known to clients. It's partly a function of the ability of the appserver to support many independently running applications.

About your example, many people would consider that behavior a feature :-) It's a good thing that
the same implementation can be deployed multiple times and each have its own unique identity. We
could solve this problem before. The real issue is that each vendor solved it in a different way.

Posted by Ken Saks on December 11, 2008 at 10:34 AM EST #

Please, extend this to allow creation of local session beans dynamically to use with Seam and other frameworks.

JBoss has a non standard solution. To create local sessions inside my framework in Glassfish I had to create this hack.

private static Map<Class<?>,Long> ejbIDs = new ConcurrentHashMap<Class<?>,Long>();

public Object getLocalInstanceFromHugeGlassfishHack(
Class<?> ejbClass,Class<?> ejbLocalInterface) {

ContainerFactory containerFactory = Switch.getSwitch().getContainerFactory();

Long id = ejbIDs.get(ejbClass);

if(id != null){
BaseContainer container = (BaseContainer)containerFactory.getContainer(id);
if(container != null){
return container.getEJBLocalBusinessHome()
.create(ejbLocalInterface.getName());
}
}

Enumeration containers =
Switch.getSwitch().getContainerFactory().listContainers();

while (containers.hasMoreElements()) {
BaseContainer container = (BaseContainer) containers.nextElement();

EjbDescriptor ejbDescriptor = container.getEjbDescriptor();

if (ejbClass.getName().equals(ejbDescriptor.getEjbClassName()) &&
ejbClass.isAssignableFrom(container.getEJBClass())) {

ejbIDs.put(ejbClass,ejbDescriptor.getUniqueId());

return container.getEJBLocalBusinessHome()
.create(ejbLocalInterface.getName());
}

}
return null;

}

Posted by Ezio Anselmo Mazarim Fernandes on December 16, 2008 at 08:54 AM EST #

Portable global JNDI names will be defined for the Local view. The main goal is to benefit
application code, although it should also make it easier for frameworks to acquire EJB references.

Posted by Ken Saks on December 16, 2008 at 10:45 AM EST #

Post a Comment:
  • HTML Syntax: NOT allowed

This blog copyright 2009 by Ken Saks