Portable Global JNDI names

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)
    throws Exception {

    InitialContext ic = new InitialContext();
    FooBean fooBean = (FooBean)
            ic.lookup("_app1_mod1_FooBean");

    //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

  1. <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.
  2. <module-name> defaults to bundle name (.war or .jar) without the bundle extension. Again, this can be overridden in ejb-jar.xml.
  3. <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/MyEJB!com.acme.GoodBye

java:global/hello/HelloSingleton
java:global/hello/HelloSingleton!com.acme.HelloBean

java:global/hello/ShoppingCart
java:global/hello/ShoppingCart!com.acme.ShoppingCart


Note:

  1. Since MyEJB implements more than one interface, the global jndi names must include the implemented interface
  2. For HelloBean, the value specified in the name() attribute is used as <bean-name>
  3. Since HelloBean bean exposes a no interface view, the qualified bean class name is used as <fully-qualified-bean-intf-name>
  4. Since ShoppingCart bean exposes a no interface view, the qualified bean class name is used as <fully-qualified-bean-intf-name>
  5. 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")

private ShoppingCart cart;

More name spaces


In addition to the java:global namespace, the container is also required to make the bean(s) available under two other 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/MyEJB!com.acme.GoodBye

java:app/hello/HelloSingleton
java:app/hello/HelloSingleton!com.acme.HelloBean

java:app/hello/ShoppingCart
java:app/hello/ShoppingCart!com.acme.ShoppingCart

java:module/MyEJB!com.acme.Hello
java:module/MyEJB!com.acme.GoodBye

java:module/HelloSingleton
java:module/HelloSingleton!com.acme.HelloBean

java:module/ShoppingCart
java:module/ShoppingCart!com.acme.ShoppingCart



Running the sample application

  1. Save and unzip  portable-jndi-app.zip
  2. cd portable-jndi-name
  3. mvn install will build the application. The .war file will be under target directory
  4. <v3-install-dir>/bin/asadmin deploy --force=true target/portable-jndi-app.war
  5. Open a browser and access localhost:8080/webejb/Example1Servlet
I will be updating this blog with one more sample app where the EJBs and Servlets are packaged inside a .ear file.

Also, I have used mvn as the build / packing tool.

References


[1] EJB 3.1 Specification

[2] Ken Saks' note on portable jndi name

Comments:

Hello everyone,
I'm trying a simple example of a stateless session bean with @remote interface and a client that connects to EJB application.
I deploy EJB application on GlassFish and everything is ok.
If I run the client in the client container 'appclient-client myapp'
everything works fine (both with the annotation EJB that with JNDI), but if I run the client from Eclipse IDE with JNDI, then I have always this error:

java.lang.NullPointerException
at com.sun.enterprise.naming.impl.SerialContext.getRemoteProvider(SerialContext.java:297)
at com.sun.enterprise.naming.impl.SerialContext.getProvider(SerialContext.java:271)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:430)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at com.webage.ejbs.TestClient.runTest(TestClient.java:19)
at com.webage.ejbs.TestClient.main(TestClient.java:27)
javax.naming.NamingException: Lookup failed for 'java:global/EX_SVR/antonio.ejb.SimpleBean' in SerialContext [Root exception is javax.naming.NamingException: Unable to acquire SerialContextProvider for SerialContext [Root exception is java.lang.NullPointerException]]
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:442)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at com.webage.ejbs.TestClient.runTest(TestClient.java:19)
at com.webage.ejbs.TestClient.main(TestClient.java:27)
Caused by: javax.naming.NamingException: Unable to acquire SerialContextProvider for SerialContext [Root exception is java.lang.NullPointerException]
at com.sun.enterprise.naming.impl.SerialContext.getProvider(SerialContext.java:276)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:430)
... 3 more
Caused by: java.lang.NullPointerException
at com.sun.enterprise.naming.impl.SerialContext.getRemoteProvider(SerialContext.java:297)
at com.sun.enterprise.naming.impl.SerialContext.getProvider(SerialContext.java:271)
... 4 more

I tried different examples but always this error.
Someone can help me?

Thanks
Antonio

Posted by manto69 on January 16, 2010 at 12:54 AM PST #

Hi, I have a similar issue that Antonio's :
I am using:
NetBeans 6.8
GlassFish V3
When I try to deploy the EAR I get the following error:
Server Log:

SEVERE: Class [ Lorg/apache/fop/apps/FopFactory; ] not found. Error while loading [ class gov.siup.fopwrapper.app.FopFactoryWrapper ]
WARNING: Error in annotation processing: java.lang.NoClassDefFoundError: Lorg/apache/fop/apps/FopFactory;
SEVERE: Exception while deploying the app
java.lang.IllegalArgumentException: Invalid ejb jar [WebFOPWrapper-ejb.jar]: it contains zero ejb.
Note:
1. A valid ejb jar requires at least one session, entity (1.x/2.x style), or message-driven bean.
2. EJB3+ entity beans (@Entity) are POJOs and please package them as library jar.
3. If the jar file contains valid EJBs which are annotated with EJB component level annotations (@Stateless, @Stateful, @MessageDriven, @Singleton), please check server.log to see whether the annotations were processed properly.
at com.sun.enterprise.deployment.util.EjbBundleValidator.accept(EjbBundleValidator.java:72)
at com.sun.enterprise.deployment.util.ApplicationValidator.accept(ApplicationValidator.java:124)

I can deploy the Ejb-tier as a stand alone project.
Here is the server output:
INFO: Portable JNDI names for EJB FopFactoryWrapper : [java:global/WebFOPWrapper-ejb/FopFactoryWrapper, java:global/WebFOPWrapper-ejb/FopFactoryWrapper!gov.siup.fopwrapper.app.FopFactoryWrapper]
INFO: Portable JNDI names for EJB DecretoRenderer : [java:global/WebFOPWrapper-ejb/DecretoRenderer!gov.siup.fopwrapper.app.DecretoRendererLocal, java:global/WebFOPWrapper-ejb/DecretoRenderer]
INFO: WebFOPWrapper-ejb was successfully deployed in 1,111 milliseconds.

When I try to Run the AppClient:
Server log:

INFO: ACDEPL104: Java Web Start services stopped for the app client webFOPWrapperPesado
INFO: ACDEPL103: Java Web Start services started for the app client webFOPWrapperPesado (contextRoot: /webFOPWrapperPesado)
INFO: webFOPWrapperPesado was successfully deployed in 200 milliseconds.

Client Side Log:

Jan 28, 2010 8:10:39 PM com.sun.enterprise.transaction.JavaEETransactionManagerSimplified initDelegates
INFO: Using com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate as the delegate
Jan 28, 2010 8:10:45 PM webfopwrapperpesado.test doCalls
SEVERE: null
javax.naming.NamingException: Lookup failed for 'java:global/WebFOPWrapper-ejb/DecretoRenderer' in SerialContext targetHost=localhost,targetPort=3700,orb'sInitialHost=localhost,orb'sInitialPort=3700 [Root exception is javax.naming.NameNotFoundException: DecretoRenderer not found]
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:442)

Need Help. Thanks

Posted by juan Manuel Fernandez on January 28, 2010 at 06:14 AM PST #

An excellent exposition and a really usefull information. Thank You.
I have a couple of questions and I hope you can help me on this.

I have a distributed architecture where servers are hosting the ejb services and clients are running on different computers. How can I modify the @EJB injection lookup in this case. How this can be done with regulare JNDI InitialContext ? Can you give me some examples ?
By the way I have tried the @EJB(lookupName="java:global/hello/ShoppingCart")
private ShoppingCart cart; but it seems that lookupName is not supported I have an error in compilation (or using Netbeans is just giving you an error right away in the ide as soon as you write lookupName), should beanName work the same because in this case there is no error.
Thank You,

Walter

Posted by Walter on February 04, 2010 at 04:12 AM PST #

hello everyone
For several day I'm trying to solve this problem

I have an application with ejb and web modules deployed on glassfish v2, and desk application using ejb
all work without problem

now I want to migrate to glassfish v3.1

// ebj
first steep deploy the ejb's on glassfish v3, no have problem

//web application
after when deploy web modules if ejb package checked to include on library the server say this error

GRAVE: Exception while loading the app : EJB Container initialization error
java.lang.RuntimeException: Error while binding JNDI name TestSessionBean for EJB : TestSessionBean
if ejb package not checked the web module is deployed but when run de application lookup not find the ejb

Caused by: javax.naming.NameAlreadyBoundException: Use rebind to override
at com.sun.enterprise.naming.impl.TransientContext.doBindOrRebind(TransientContext.java:333)

when deploy web application without ejb package the deploy works ok, but when run the application say this error

org.apache.jasper.JasperException: java.lang.RuntimeException: javax.naming.NamingException: Lookup failed for 'TestSessionBean' in SerialContext[myEnv={org.omg.CORBA.ORBInitialPort=3700, java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, org.omg.CORBA.ORBInitialHost=localhost, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NamingException: ejb ref resolution error for remote business interfacetest.TestSessionBeanRemote [Root exception is java.lang.ClassNotFoundException: test.TestSessionBeanRemote]]
root cause

java.lang.RuntimeException: javax.naming.NamingException: Lookup failed for 'TestSessionBean' in SerialContext[myEnv={org.omg.CORBA.ORBInitialPort=3700, java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, org.omg.CORBA.ORBInitialHost=localhost, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NamingException: ejb ref resolution error for remote business interfacetest.TestSessionBeanRemote [Root exception is java.lang.ClassNotFoundException: test.TestSessionBeanRemote]]

//desk application
when run this the lookup say

javax.naming.CommunicationException: Can't find SerialContextProvider [Root exception is java.lang.ClassCastException]

ejb source
//
@Remote
public interface TestSessionBeanRemote {

int add(int a, int b);

}

@Stateless(mappedName="TestSessionBean")
public class TestSessionBean implements TestSessionBeanRemote
{

@Override
public int add(int a, int b) {
return a + b;
}
}
//

web source on lookup
//
private TestSessionBeanRemote lookupTestSessionBeanRemote() {
try {
Context c = new InitialContext();
return (TestSessionBeanRemote) c.lookup("TestSessionBean");
} catch (NamingException ne) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", ne);
throw new RuntimeException(ne);
}
}
//

desk application lookup
//
Properties props = new Properties();

props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");

props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");

props.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");

props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");

props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");

InitialContext ic = new InitialContext(props);

TestSessionBeanRemote bean = (TestSessionBeanRemote) ic.lookup("TestSessionBean");
//

you have any suggestion on the subject, or which may be my error

from already very grateful

juan alzugaray

Posted by juan alzugaray on March 10, 2011 at 10:20 PM PST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Mahesh Kannan

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