This week I have been writing a simple J2EE application client. I haven't written an app client in a while so I decided to try it out using my freshly downloaded Eclipse 3.2 Callisto with J2EE Tools. While I was working on this project I ran into a couple problems with javax.naming.InitialContext. These were not problems with the class itself but rather my inability to use it as intended. To illustrate these Callisto context-newbie mistakes I'll walk through the code for a small sample app that runs on JBoss 4.
For this project I decided to use XDoclet to help create the EJB classes and interfaces rather than manually code them all by hand. Before I could use it I had to download it from since Eclipse 3.2 does not come with it. Eclipse needs to know where XDoclet is installed so I set it in Window -> Preferences -> XDoclet.
After coding the EJB the J2EE Tools make it trivially easy to create a client jar that contains the classes required for a client to use the bean. Just right-click the EJB project in the Project Explorer view and go to EJB Client Jar -> Create EJB Client Jar. When a client is created the J2EE Tools creates a Util.java file that contains some helper methods used to locate and create an instance of an EJB. This should be a decent time saver and keep people from doing boiler plate code that leads up to PortableRemoteObject.narrow(…). One of the methods is getHome() which gets the home interface used to create the bean.
With this util class making the client should be a breeze. The hard work is reduced to:
FibonacciHome fibHome = FibonacciUtil.getHome(); Fibonacci fib = fibHome.create(); fib.getFibonacciNumber(12);
Unfortunately running this code produces a nasty stack trace.
Exception in thread "main" javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial at javax.naming.spi.NamingManager.getInitialContext(Unknown Source) at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source) at javax.naming.InitialContext.getURLOrDefaultInitCtx(Unknown Source) at javax.naming.InitialContext.lookup(Unknown Source) at net.anthonychaves.ejb.FibonacciUtil.lookupHome(FibonacciUtil.java:22) at net.anthonychaves.ejb.FibonacciUtil.getHome(FibonacciUtil.java:42) at net.anthonychaves.ejb.MyClient.main(MyClient.java:24)
A closer look at the getHome() method shows that it calls lookupHome(…)
public static net.anthonychaves.ejb.FibonacciHome getHome() throws javax.naming.NamingException
{
if (cachedRemoteHome == null) {
cachedRemoteHome = (net.anthonychaves.ejb.FibonacciHome) lookupHome(null,
net.anthonychaves.ejb.FibonacciHome.JNDI_NAME,
net.anthonychaves.ejb.FibonacciHome.class);
}
return cachedRemoteHome;
}
And lookupHome(…) takes as its first argument a Hashtable of JNDI environment values keyed by javax.naming.Context constants. The problem was that getHome() was passing null to lookupHome(…). When creating an InitialContext the environment values to use come from one of two places: the environment Hashtable passed in to the InitialContext constructor and a JNDI properties file, in that order. That NoInitialContextException was caused because my project did not pass an environment Hashtable and I did not have a JNDI properties file in my project. I decided to fix this problem by looking in the JNDI Tutorial to find the options required for getting the InitialContext of my EJB from the JBoss naming server.
I was fairly certain I only needed two environment properties set in order to gain access to my EJB. The Naming Operations page told me I needed to set values for Context.INITIAL_CONTEXT_FACTORY and Context.PROVIDER_URL. According to the Environment Overview the Context.INITIAL_CONTEXT_FACTORY value should be a class that implements the InitialContextFactory interface. Because I'm using JBoss I thought it would be worthwhile to head over to the JBoss Application Server Guide and look at the Naming on JBoss section. That sections told me the environment Hashtable should look like the following:
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); env.put(Context.PROVIDER_URL, "localhost:1099");
The rest of the client doesn't change other than using the getHome(Hashtable environment) method in the *Util class instead of the no-arg getHome() method. And with that, the client can make a connection to the JBoss server and use the EJB. I really appreciate all the hard work the guys at the JST and WST projects do to create such a robust and flexible IDE that makes writing app clients so much easier than it has in the past. Using the tools is no substitute for creating a few EJBs and clients without tools, but they make life easier once you have a few under your belt. I'm really happy with Eclipse 3.2 as my IDE at this point.
I'm not an expert on the JNDI libraries so please check out the links above to get a better feel for all that it can do.
In other news, LinkieWinkie has found me. Here's a wink right back.




