Portable Global JNDI Names

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 03: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 03: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 03: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 07:26 AM 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 02: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 12: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 02:45 AM EST #

[Trackback] EJB 3.1 (JSR 318) and Servlet 3.0 (JSR 315) are the two new JSRs in Java EE 6 (JSR 316). The EJB 3.1 specification provides multiple new features such as WAR packaging, Optional Local Business Interfaces, EJB.lite, Portable Global...

Posted by Arun Gupta's Blog on May 19, 2009 at 07:00 AM EDT #

[Trackback] EJB 3.1 (JSR 318) and Servlet 3.0 (JSR 315) are the two new JSRs in Java EE 6 (JSR 316). The EJB 3.1 specification provides multiple new features such as WAR packaging, Optional Local Business Interfaces, EJB.lite, Portable Global...

Posted by Arun Gupta's Blog on May 20, 2009 at 02:15 PM EDT #

Thank you very much, I have been in trouble with this issue for a long time

Posted by Xuewu Liu on September 26, 2009 at 12:58 AM EDT #

Hi Ken,
This really is a great feature which was needed especially for Local EJB's deployed in an EJB jar and web components deployed in another WAR but in the same applicationserver.
The problem was that even though practically we had separate EJB jars and WARS,both deployed in the same appserver, there was no way to inject an Local EJB reference in the web component.THe @EJB annotation didn't work for EJB and web compenets deployed in different jars and war although both run on the same appserver's JVM. Had to declare it as an EJB with Remote interface to make it work.
Anyways I tried it now in glassfish v3 and it is running great !!!
I have 1 question though. Let's say my Local EJB is packaged as TestEJB.jar and Servlet as TestWeb.jar.When I deployed the EJB jar, the global JNDI assigned during server deployment was java:global/TestEJB/MyLocalBean.
I have used the @EJB annotation in the servlet to get a reference to MyLocalBean in my servlet code but when I deploy the TestWeb.war, the global JNDI assigned is java:global/TestWeb/MyLocalBean. Why is this difference and if this is the JNDI reference to be used in the web components rather than java:global/TestEJB/MyLocalBean??? i.e won't JNDI lookup work if I look for "java:global/TestEJB/MyLocalBean" in my servlet??

Posted by Mrutyunjay Swain on January 14, 2010 at 06:14 PM EST #

Hi Mrutyunjay,

Glad to hear you're enjoying the new EJB 3.1 features. Regarding your question, the reason this happens is that the existence of any .class containing a EJB component-defining annotation (e.g. @Stateless) packaged within a .war or ejb-jar results in a new component being defined. So, if the bean class exists in both the .war and the ejb-jar, each will result in its own component. This is a result of the no-interface view since previously the bean class would not have been packaged in the .war. You can look up either JNDI name. However, it's probably easier to just package the ejb component directly in the .war.

Posted by Ken Saks on January 19, 2010 at 06:33 AM EST #

Dear Ken,
I am worked only on the EJB2 Api in Weblogic server and am new to the EJB3 API .Infact since it is not being used in our development frame work used at work I have to study it on the own .For the starters I am a bit confused with the terms Global JNDI names ,Portable and Non Portable JNDI names ,Local and Environment JNDI names(java:comp/env...).I tried to search many websites but there I didnot see any lucid explanation for these terminologies

Uptill now I have deployed EJB2 Session beans on Sun Application Server and Weblogic Server.
In the Sun Application Server the deployment took place via a deployment tool where we had the provision for entering the JNDI name which was used for accessing the EJB
In the Weblogic server the deployment JNDI name was through ejbjar.xml and weblogic.xml files and in both files whatever names we gave were used during lookup.
So I go confused with these terms .IF possible can you post an explanation for these terms which would be really helpful for newbies like me.Secondly I am also confused by the protocol used before the JNDI name i.e (java:comp/env) in this case java etc. What advantage does the IDE server get by using this protocol .I can understand the "/" helps in faster lookups as we can get an instance of that specific context
Regards

Posted by Abhijeet Kushe on February 12, 2010 at 08:20 PM EST #

Hi Abhijeet,

It's difficult to do justice to this topic in a comment so I'll quickly summarize and then point you to a presentation that covers it in more detail. The main distinction is to understand is the difference between component environment dependencies and global resources.

The Java EE (originally J2EE) platform defines a special context called the component environment. Each EJB component in an ejb-jar and each .war has its own private component environment namespace. This namespace is used to define logical dependencies on resources (e.g. data sources, ejbs, queues, etc.) used by the application. This provides a level of indirection that allows the code to be written without knowing the specific global JNDI names of the resources, since those are typically not known until \*deployment\* time. "java:comp/env" is special string that allows code to refer to these logical dependencies in the component environment.

Global JNDI names are globally unique names for resources. It's up to the application to decide whether to access resources via the level of indirection provided by the component environment or to bypass the component environment and directly lookup a resource by its global name. In the former case, there is typically a need to map the logical dependency to the global resource. As always, there are tradeoffs to each approach.

Take a look at this presentation for more detail. It's from the Java EE 5 time frame but it has more
on the distinction between component-environment and global JNDI namespaces :

https://glassfish.dev.java.net/javaee5/ejb/compdependencies_xmlforum_nov15.pdf

Posted by Ken Saks on February 15, 2010 at 04:29 AM EST #

Hello,

First: thanks for your explanantions.

But I want to know what happens if we use the @Stateless annotation with attribute name and mappedName ? Are these attributes completely ignored now when trying to access to an EJB ?

Posted by François on February 17, 2010 at 04:54 AM EST #

Hello again,

Just some remarks after my last post ...

I have read the spec and have made some tests.
So the attribute name of the @Stateless annotation could be used in the global naming space: OK.

But what happens to the attribute mappedName of the @Stateless annotation: is it completely unuseful now ?

Posted by François on February 17, 2010 at 05:20 AM EST #

Hi Francois,

The name() attribute is used to define the bean's ejb-name, also referred to as "bean name.", which is a critical part of the EJB portable JNDI name syntax. name() is optional. If not specified, bean name defaults to the unqualified bean class name.

The behavior of mappedName() is unfortunately not portable. It was an unsuccessful attempt in Java EE 5 to solve some of these issues around global JNDI names but it's not required to be supported. Any particular implementation that supported it in a Java EE 5 product is likely to continue supporting it going forward but now that the EJB 3.1 specification defines portable JNDI name syntax it's best to not use mappedName() at all.

Posted by Ken Saks on February 17, 2010 at 05:28 AM EST #

Very clear :-)

Thanks for your answer.

Posted by François on February 17, 2010 at 05:30 AM EST #

Hi I have the problem UnresolvableReferenceException
Could not resolve reference [EJB Reference: beanInterface 'br.com.FooBeanRemote', beanName '', mappedName 'null'] in AbstractVFSDeploymentContext@3607545{vfs:///C:/jboss-6.0.0.Final/server/default/deploy/Hello.war}

Posted by Douglas on February 09, 2011 at 04:35 AM EST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Ken Saks is the Spec Lead for Enterprise JavaBeans (EJB) 3.1 and a Senior Staff Engineer in the Java Platform, Enterprise Edition team at SUN.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today