12/16/2009

Portable JNDI names in EJB 3.1

Java EE 6 made some efforts to standardize JNDI names at global,application, and module levels. Now EJB deployed to EJB-3.1-capable server will have various standard JNDI names registered. EJB 3.1 spec defines the following 3 levels of JNDI names for EJB:

Global JNDI name:

java:global[/<app-name>]/<module-name>/<bean-name>[!<fully-quali-fied-interface-name>]
Application-scope JNDI name:
java:app/<module-name>/<bean-name>[!<fully-qualified-interface-name>]
Module-scope JNDI name:
java:module/<bean-name>[!<fully-qualified-interface-name>]
The following exampels shows various EJB JNDI names, depending upon how the EJB is packaged, its business interface(s), and bean class:

Bean class: test.TestBean
business interface: none






















How the EJB is packaged

Global JNDI names


Application-scope JND names

Module-scope JNDI names

in testEJB.jar inside testApp.ear

java:global/testApp/testEJB/TestBean

java:global/testApp/testEJB/TestBean!test.TestBean


java:app/testEJB/TestBean

java:app/testEJB/TestBean!test.TestBean



java:module/TestBean


java:module/TestBean!test.TestBean


in testWeb.war



java:global/testWeb/TestBean

java:global/testWeb/TestBean!test.TestBean


java:app/testWeb/TestBean

java:app/testWeb/TestBean!test.TestBean


java:module/TestBean


java:module/TestBean!test.TestBean



in testEJB.jar



java:global/testEJB/TestBean

java:global/testEJB/TestBean!test.TestBean



java:app/testEJB/TestBean

java:app/testEJB/TestBean!test.TestBean



java:module/TestBean


java:module/TestBean!test.TestBean


12/10/2009

EJB Lite testing with JUnit and embeddable container


In EJB 3.1, one can unit-test EJB using standadard EJB API, JUnit, and outside any application server. This is how I created an Eclipse java project to unit-test a simple EJB with GlassFish v3.

1. inside Eclipse, create a new java project, named ejblite.


Build path: $GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar $GLASSFISH_HOME/modules/javax.ejb.jar

Output dir: testApp (it is used in global jndi name in the test)

2. create a new java class named TestBean:

package test;

import javax.ejb.Stateless;

@Stateless
public class TestBean {
public int add(int a, int b) {
return a + b;
}

public int multiply(int a, int b) {
return a * b;
}
}
3. select TestBean.java in the package panel, create a new JUnit Test Case (File -> New -> JUnit Test Case). Choose TestBean as the test target, and the test method for both business methods (add and multiply) will be generated, along with setUp and tearDown.

4. modify the test case:
package test;

import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;

import junit.framework.TestCase;

public class TestBeanTest extends TestCase {
private EJBContainer container;
private Context namingContext;
private TestBean testBean;

@Override
protected void setUp() throws Exception {
super.setUp();
container = EJBContainer.createEJBContainer();
namingContext = container.getContext();
testBean = (TestBean) namingContext.lookup("java:global/testApp/TestBean");
}

@Override
protected void tearDown() throws Exception {
super.tearDown();
namingContext.close();
container.close();
}

/**
* Test method for {@link test.TestBean#add(int, int)}.
*/
public final void testAdd() {
int a = 1, b = 2, expected = a + b;
assertEquals(expected, testBean.add(a, b));
}

/**
* Test method for {@link test.TestBean#multiply(int, int)}.
*/
public final void testMultiply() {
int a = 1, b = 2, expected = a * b;
assertEquals(expected, testBean.multiply(a, b));
}

}
5. select TestBeanTest class, and run it as JUnit test.

Our EJB classes all reside under testApp directory (the output dir of the current project), and testApp is also used as the module-name for the current EJB app. If you choose to use a different output dir value, you will also need to change the global jndi-name accordingly.

If you are interested in using DataSource and JPA in EJB embeddable, check out EJB lite, JPA, DataSource embedded in java application.

12/08/2009

A sample EJB 3.1 Lite client

EJB Lite is a lightweight subset of EJB 3.1, including the support for running EJB container embedded in the current java application. So one can run EJB without appserver. The following sample contains a no-interface stateless EJB, and a java application that looks up and invokes the EJB.

1. project structure:

mkdir -p ejblite/src/test
mkdir -p ejblite/testApp

2. go to ejblite/src/test, and edit 2 java files:
-------------
TestBean.java
-------------

package test;
import javax.ejb.Stateless;

@Stateless
public class TestBean {
public String hello(String name) {
return "Hello, " + name;
}
}

-----------
Client.java
-----------

package test;

import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import javax.naming.NamingException;

public class Client {
public static void main(String args[]) throws NamingException {
EJBContainer container = null;
try {
container = EJBContainer.createEJBContainer();
Context namingContext = container.getContext();
TestBean testBean = (TestBean) namingContext.lookup(
"java:global/testApp/TestBean");
String hi = testBean.hello("client");
System.out.println("testBean.hello method returned: " + hi);
} finally {
if(container != null) {
container.close();
}
}
}
}
3. compile and run from src/test directory, using GlassFish v3:
javac -d ../../testApp/ -cp $GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar:../../classes Client.java TestBean.java

java -cp $GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar:../../testApp test.Client
You will see the following output, when running with GlassFish v3:
Dec 8, 2009 3:47:45 PM com.sun.enterprise.v3.server.AppServerStartup run
INFO: GlassFish v3 (74.2) startup time : Embedded(1246ms) startup services(511ms) total(1757ms)
Dec 8, 2009 3:47:45 PM org.glassfish.admin.mbeanserver.JMXStartupService$JMXConnectorsStarterThread run
INFO: JMXStartupService: JMXConnector system is disabled, skipping.
Dec 8, 2009 3:47:46 PM com.sun.enterprise.transaction.JavaEETransactionManagerSimplified initDelegates
INFO: Using com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate as the delegate
Dec 8, 2009 3:47:46 PM AppServerStartup run
INFO: [Thread[GlassFish Kernel Main Thread,5,main]] started
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.SecurityLifecycle <init>
INFO: security.secmgroff
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.SecurityLifecycle onInitialization
INFO: Security startup service called
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.PolicyLoader loadPolicy
INFO: policy.loading
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm admin-realm of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm file of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm certificate of classtype com.sun.enterprise.security.auth.realm.certificate.CertificateRealm successfully created.
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.SecurityLifecycle onInitialization
INFO: Security service(s) started successfully....
Dec 8, 2009 3:47:48 PM com.sun.ejb.containers.BaseContainer initializeHome
INFO: Portable JNDI names for EJB TestBean : [java:global/testApp/TestBean!test.TestBean, java:global/testApp/TestBean]
testBean.hello method returned: Hello, client
Dec 8, 2009 3:47:48 PM org.glassfish.admin.mbeanserver.JMXStartupService shutdown
INFO: JMXStartupService and JMXConnectors have been shut down.
Dec 8, 2009 3:47:48 PM com.sun.enterprise.v3.server.AppServerStartup stop
INFO: Shutdown procedure finished
Dec 8, 2009 3:47:48 PM AppServerStartup run
INFO: [Thread[GlassFish Kernel Main Thread,5,main]] exiting
It's a good practice to close the EJB container before program exists, to avoid any resource leaking. It's more easily handled in JUnit tearDown method. See my follow-up post EJB Lite testing with JUnit and embeddable container

How to get module name and app name

Java EE 6 brings an easy and portable way to get the module name and application name of the current app, using @Resource field injection or lookup:

    @Resource(lookup="java:module/ModuleName")
private String moduleName;

@Resource(lookup="java:app/AppName")
private String appName;
The above code can be in any Java EE component classes, such as servlet, servlet filter, web listener class, JSP tag handler class, JSF managed bean, EJB bean class, interceptor class, application client main class, and their super classes as well.

You can also look up the module name and app name by their reserved jndi names:
//look up inside a method
String mname = (String) initialContext.lookup("java:module/ModuleName");
String anmae = (String) initialContext.lookup("java:app/AppName");
If you only need moduleName or appName in a particular method, I feel looking them up on-demand is slightly better.

From Java EE 6 forward, the default module name is the base name of a WAR, ejb-jar, or application client jar, that is, the jar name without the .war and .jar extension. One can customize it in descriptors (web.xml, ejb-jar.xml, application-client.xml), though this is rarely needed.

The default app name is the basename of a EAR, i.e., the EAR name without the .ear extension. One can customize it in application.xml.