Locating Resources Using JNDI (Java Naming and Directory Interface), Page 4
Obtaining an Initial Context
The first thing you must do when you use a JNDI naming service is to obtain a context in which you can add and find names. The context that represents the entire namespace is known as the initial context. You need to have an initial context, since all of the operations that you can perform on naming and directory services are performed relative to a context.
In Java code, you represent the initial context with an instance of the javax. naming.InitialContext class. As mentioned earlier in this chapter, this class implements the javax.naming.Context interface that defines methods for examining and updating bindings within a naming service.
The way that you retrieve a reference to an initial context is very simple and can be performed with this line of code:
Context initialContext = new InitialContext();
However, there are several things that can go wrong when this code is executed. In any of these cases, an instance of the javax.naming.NamingException class is thrown. The four most common errors and their reasons are as follows.
First of all, you get the following exception if the JNDI server is not running or if the JNDI properties for the server are not set correctly:
javax.naming.CommunicationException: Can't find SerialContextProvider
Second, if the InitialContext class has neither default properties for the JNDI service provider nor explicitly configured server properties, you will see the following exception:
javax.naming.NoInitialContextException:Need to specify class name in environment or system property,
or as an appletparameter, or in an application resource file: java.naming.factory.initial
Third, if the classpath for the JNDI program does not contain the JNDI server classes, you see this exception:
javax.naming.NoInitialContextException: Cannot instantiate class: XXX [Root exception is java.lang.ClassNotFoundException: XXX]
Fourth, if the JNDI properties for the program do not match the JNDI Service Provider, you see this exception:
javax.naming.ServiceUnavailableException: Connection refused: no further information [Root exception is java.net.ConnectionException: Connection refused: no further information]
The next section, "Configuring the JNDI Service Provider," details how to avoid all four of these exceptions.
Configuring the JNDI Service Provider
Although you are developing an application on your personal development machine, it is perfectly reasonable for you to use a JNDI service that is running on your local machine. For example, you could use the default service provider shipped with your J2EE server. However, when you deploy the application you need to use the naming service used by your organization. This means that you must configure your application to use a specific naming service rather than the one that is running on your personal development J2EE server.
Some implementations from vendors might require additional parameters, but the core information that you need to provide to define a JNDI service is
The server's DNS host name
The socket port number on the server
The JNDI service class name
There are several ways to provide this information to an application, but all you need to do is choose one of these options:
Add the properties to a JNDI Properties file in the Java runtime home directory.
Provide an application resource file for the program.
Set command-line parameters that are passed to the application. You can do this using the Java interpreter's -D option. The downside to using this approach is that the command lines tend to become unwieldy, although you could always use a script that contains the properties that you set.
In the case of an applet, you can specify parameters that are passed to it by using the <param> tags that can be nested in <applet> tags.
Hard-code the values into the application. This is not a preferred approach because it restricts the application to the naming service on the host that you specify.
The use of hard-coded properties is the least desirable because you have to edit, recompile, and redeploy an application if you change the service provider or if the naming server moves to a different host. However, if you want to try it out, perhaps in a test environment, you simply set the properties using a hashtable, with code like this:
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.SerialInitContextFactory"); env.put(Context.PROVIDER_URL, "localhost:1099"); Context initialContext = new InitialContext(env);
Notice that the code does not use the property names such as java.naming. factory.initial, but instead uses Context.INITIAL_CONTEXT_FACTORY and Context.PROVIDER_URL. This is because the javax.naming.Context interface defines a set of constants for the names of the properties that you need to set. Thus, you do not have to remember strings such as java.naming.factory.initial. This also makes your code more flexible because it is independent of any changes that might be made to the property names in future versions of JNDI. You will see more on the different properties and their names shortly.
Although it is possible to hard code the JNDI properties, it is the first two approaches that are the most suitable for production environments. For both, all that you need to do is distribute a text file with the application.
When you create an InitialContext, JNDI searches for any application resource files called jndi.properties on the classpath. JNDI also looks in the Java runtime home directory (which is the jre subdirectory in the Java JDK home directory) for a file called lib\jndi.properties. All the properties that you define in these files are placed into the environment that belongs to the initial context.
For example, the j2ee.jar file in the lib directory of the J2EE RI contains these lines::
These are a set of properties, which are simply name/value pairs. In practice, as long as the j2ee.jar file is on the classpath, you should be all set.The first of these two properties, java.naming.factory.initial, enables you to set the fully qualified class name of the Initial Context Factory for the JNDI Service Provider. That is, you use this property to specify which JNDI Service Provider you want to use.
If you want to use the default naming service supplied with the J2EE RI (and the j2ee.jar file is not on your classpath), you would use the following line in your jndi.properties file:
Sun Microsystems provides several free reference implementations that are mentioned in the Table 14.1. You can specify the values from the table for the Context.INITIAL_CONTEXT_FACTORY environment property. Sun Microsystems maintains a list of service providers for JNDI on its Web site at http://java.sun.com/products/jndi/serviceproviders.html.
Table 13.1 Values of Context.INITIAL_CONTEXT_FACTORY (java.naming.factory.initial)
CORBA Naming Service (COS)
You can find more information on these properties in the documentation for the javax.naming.Context and Sun's JNDI Tutorial (http://java.sun.com/products/jndi/tutorial/index.html).
How about if you need to access a JNDI service on a remote machine? If you needed to reference a JNDI service on a machine called nameserver.samspublishing.com on port 4242, you would set this property in the jndi.properties file:
The java.naming.provider.url property specifies the DNS host name and service port number of the machine that is running the JNDI service. This is the only property that the network administrator needs to modify, but JNDI uses port 1099 on the localhost by default, and most sites do not need to change this value.