Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Developing JNDI-based Applications

 
 
Articles Index



The Java Naming and Directory Interface (JNDI) is a set of APIs for accessing naming and directory services from within Java applications. A naming service is just that: it associates names with objects so that objects can be accessed by those names. And a directory service is a naming service whose objects have attributes as well as names.

A naming or directory service allows you to centralize the storage of shared information, and this is important in network applications, since it allows such applications to be more coherent and manageable. For example, printer configuration can be stored in the directory service so that it can be used by all printer-related applications.

This article provides a fast-track, code-intensive tutorial to get you started with JNDI. It:

  • provides an overview of JNDI
  • describes the features of JNDI
  • offers a taste of the effort involved in developing applications using JNDI
  • shows how to use JNDI to access an LDAP server, such as the Sun ONE Directory Server
  • shows how to use JNDI to access J2EE services
  • provides sample code that you can adapt for your own applications

Overview of the Java Naming and Directory Interface (JNDI)

We all use naming services every day without knowing it. For example, when you enter a URL such as http://java.sun.com in a web browser, the Domain Name System (DNS) converts this symbolic URL name into a communication identifier (IP address). Objects in a naming system can range from names located in DNS records, to Enterprise JavaBeans Components (EJBs) in an application server, to user profiles in a Lightweight Directory Access Protocol (LDAP).

A directory service is a natural extension of a naming service. The key difference between the two is that a directory service allows the association of attributes (such as the email address of a user) with objects, and a naming service doesn't. So with a directory service, you can search for objects based on their attributes. JNDI allows you to access files in the file system, locate objects in a remote RMI registry, access directory services such as LDAP, and locate EJBs on the network.

JNDI is a good candidate for such applications as an LDAP client, application launcher, class browser, network management utility, or even an address book.

The JNDI Architecture

The JNDI architecture provides a standard naming system-independent API that is built on top of a naming system-specific driver. This layer helps insulate the application from the actual data source, and therefore it doesn't matter whether the application is accessing an LDAP, RMI, DNS, or other directory service. In other words, JNDI is independent of any specific directory service implementation, and any directory can be used as long as you have the service provider interface (or driver) for it as shown in Figure 1.


Figure 1: The JNDI Architecture

What is important to note about JNDI is that it provides both an application programming interface (API) and a service provider interface (SPI). What this really means is that -- for your application to interact with a naming or a directory service -- there must be a JNDI service provider for that service, and that's where the JNDI SPI comes in to play. A service provider is basically a set of classes that implement various JNDI interfaces for a specific naming and directory service -- much like a JDBC driver implements various JDBC interfaces for a particular database system. As an application developer, you don't need to worry about the JNDI SPI. You just need to make sure that you have a service provider for each naming or directory service you want to use.

J2SE and JNDI

The JNDI is included in the Java 2 SDK 1.3 and later releases. It is also available as a a standard extension for JDK 1.1 and 1.2. The latest release of the Java 2 SDK 1.4.x includes several enhancements, and the following naming/directory service providers:

  • The Lightweight Directory Access Protocol (LDAP) Service Provider
  • The Common Object Request Broker Architecture (CORBA) Common Object Services (COS) Naming Service Provider
  • The Java Remote Method Invocation (RMI) Registry Service Provider
  • The Domain Name System (DNS) Service Provider

More on Service Providers

A list of service providers available for download can be found here. Of particular interest might be the Windows registry JNDI Service Providers from cogentlogic.com, which allows you to access the windows registry on Windows XP/2000/NT/Me/9x.

Also, the JNDI/LDAP Booster Pack is available for download. The booster pack contains support for popular LDAP controls and extensions. It replaces the booster pack that was bundled with the LDAP 1.2.1 service provider. See Controls and Extensions for more information.

Another interesting service provider to check out is Sun's service provider for the Directory Services Markup Language (DSML) v2.0. The aim of DSML is to bridge directory services with XML.

The JNDI APIs

The JNDI APIs consist of five packages:

  • javax.naming: Contains classes and interfaces for accessing naming services. For example, it defines the Context interface, which is the entry point to the naming service for performing a lookup.
  • javax.naming.directory: Extends the naming package to provide classes and interfaces for accessing directory services. For example, it adds new classes for attributes and provides the DirContext interface which represents a directory context, and defines methods for examining and updating attributes associated with a directory object.
  • javax.naming.event: Provides support for event notification when accessing naming and directory services. As an example, it defines a NamingEvent class, which is used to represent an event that is generated by a naming/directory service, and a NamingListener interface that listens to NamingEvents.
  • javax.naming.ldap: This package provides support specific to LDAP version 3 extended operations and controls, that are not covered by the generic javax.naming.directory package.
  • javax.naming.spi: This package provides the means for dynamically plugging in support for accessing naming and directory services through the javax.naming and related packages. This package should be of interest to only those developers who are interested in creating service providers.

JNDI Contexts

As I mentioned earlier, a naming service associates names with objects. Such an association is called a binding. A set of such bindings is called a context, which provides a resolution or lookup operation that returns the object. Other operations may include binding and unbinding names, and listing bound names. Note that a name in one context object can be bound to another context object that has the same naming convention. This is called a subcontext. As an example, if the UNIX directory /home is a context, then a directory named relative to it is a subcontext -- for example, /home/guests. Here, guests is a subcontext of home.

In JNDI, a context is represented using the javax.naming.Context interface, which is the key interface for interacting with a naming service. Each naming method in the Context (or DirContext, which is discussed later) interfaces has two overloaded forms:

  • lookup(String name): accepts a string name
  • lookup(javax.naming.Name): accepts a structured name such as CompositeName (a name that spans multiple naming systems) or CompondName (a name in a single naming system); both implement the Name interface. Here is an example of a compound name: cn=mydir,cn=Q Mahmoud,ou=People, and here is an example of a composite name: cn=mydir,cn=Q Mahmoud,ou=People/myfiles/max.txt (here myfiles/max.txt is a filename that represents the second part)

The javax.naming.InitialContext is a class that implements the Context interface. Use this class to serve as your entry point to the naming service. To create an InitialContext object, the constructor takes a set of properties in the form of java.util.Hashtable or one of its subclasses, such as Properties. Here is an example:

Hashtable env = new Hashtable();
// select a service provider factory
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContext");
// create the initial context
Context contxt = new InitialContext(env);

The INITIAL_CONTEXT_FACTORY specifies the name of a factory class in a JNDI service provider. The factory is responsible for creating a suitable InitialContext object for its service. In the above snippet of code, a factory class for the file system service provider is specified. Table 1 shows the factory classes for supported service providers. Note that the factory class for the file system service provider is available as a separate download from Sun Microsystems and doesn't come with J2SE 1.4.x.

Table 1: Values of Context.INITIAL_CONTEXT_FACTORY
Name Service Provider Factory
File System com.sun.jndi.fscontext.RefFSContextFactory
LDAP com.sun.jndi.ldap.LdapCtxFactory
RMI com.sun.jndi.rmi.registry.RegistryContextFactory
CORBA com.sun.jndi.cosnaming.CNCtxFactory
DNS com.sun.jndi.dns.DnsContextFactory


To retrieve or resolve (look up) an object by name from a naming service or a directory, use the lookup method of Context: Object obj = contxt.lookup(name). The lookup method returns an object that represents the child of the context you want to find.

A Naming Example

Now, let's look at an example that uses a naming service. In this example, we write a simple program that looks up an object whose name is passed in as a command line argument. Here we will use a service provider for the file system, and therefore the name we provide as an argument must be a filename. Code is provided in Code Sample 1.

Code Sample 1: Resolve.java

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

public class Resolve {
   public static void main(String argv[]) {
      // The user should provide a file to lookup
      if (argv.length != 1) {
         System.err.println("Usage: java Resolve ");
         System.exit(-1);
      }

      String name = argv[0];

      // Here we use the file system service provider
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");

      try {
         // Create the initial context
         Context ctx = new InitialContext(env);

         // Look up an object
         Object obj = ctx.lookup(name);

         // Print it out
         System.out.println(name + " is bound to: " + obj);

         // Close the context
         ctx.close();
      } catch (NamingException e) {
         System.err.println("Problem looking up " + name + ": " + e);
      }
   }
}

Here I will assume you are using the Java 2SDK 1.4.x, which comes with several service providers (listed above). This application uses the file system service provider which is not installed by default. Therefore, you will need to download and install it. If, on the other hand, you run the program and the service provider is not installed, you will get a NoInitialContextException, meaning that the service provider factory class couldn't be found and therefore wasn't able to instantiate it. You'll then need to include fscontext.jar and providerutil.jar in your classpath -- or if you are like me, you may want to simply copy these two files to JAVA_HOME\jre\lib\ext, where JAVA_HOME is the root directory of your Java 2SDK installation.

To Experiment with this Application:

  1. Make sure you have downloaded and installed the file system service provider (as mentioned in the above paragraph), since this service provider doesn't come with the J2SE 1.4.x.
  2. Copy and paste the code into the file, and call it Resolve.java.
  3. Compile Resolve.java using javac.
  4. Run the application using the java interpreter.

Here is a sample run:

prompt> java Resolve \classes

\classes is bound to: com.sun.jndi.fscontext.FSContext@f62373

If the name you provide is a filename, you'll see something like this:

prompt> java Resolve \classes\Resolve.java

\classes\Resolve.java is bound to: C:\classes\Resolve.java

Listing the Contents of a File Directory

Now, let's see how to use other JNDI APIs to list the contents of a file directory. Let's assume you want the user to be able to specify the command line argument using a URL such as file:///. In that case, you would set a new property PROVIDER_URL as shown in Code Sample 2. The listBindings method of Context returns a NamingEnumeration, which can be iterated through using a while loop as shown in Code Sample 2.

Code Sample 2 Resolve2.java

import javax.naming.Binding;
import javax.naming.NamingEnumeration;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

public class Resolve2 {
   public static void main(String argv[]) {
      // The user should provide a file to lookup
      if (argv.length != 1) {
         System.err.println("Usage: java Resolve2 ");
         System.exit(-1);
      }

      // Here we use the file system service provider
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.FSContextFactory");
      env.put(Context.PROVIDER_URL, argv[0]);

      try {
         // Create the initial context
         Context ctx = new InitialContext(env);
         
         NamingEnumeration ne = ctx.listBindings("");
         while(ne.hasMore()) {
           Binding b = (Binding) ne.next();
           System.out.println(b.getName() + " " + b.getObject());
         }
         
         // close the context
         ctx.close();
      } catch (NamingException e) {
         System.err.println("Problem looking up " + argv[0] + ": " + e);
      }
   }
}

To experiment with this application, follow the same compilation and running steps for the example above. Here is a sample run:

prompt>: java Resolve2 file:///uddi

fig1.gif C:\uddi\fig1.gif
fig2.gif C:\uddi\fig2.gif
fig3.gif C:\uddi\fig3.gif
fig4.gif C:\uddi\fig4.gif
fig5.gif C:\uddi\fig5.gif
impl.txt C:\uddi\impl.txt

Directory Services

As stated earlier, a directory service is simply a naming service whose objects have attributes as well as names. An object with attributes and names is called a directory entry. Applications can use a directory service to store and retrieve attributes of directory objects. It can even be used as an object store.

LDAP

The Lightweight Directory Access Protocol (LDAP) -- which traces its roots to the X.500 protocol developed at the University of Michigan at Ann Arbor -- is a protocol for accessing and managing directory services; it defines how clients should access data stored on the server, but not how data is stored. An LDAP directory consists of entries with descriptive information that describe people (for example, name, phone numbers, email address, and so on) or network resources (such as printers, fax machines, and the like). Such descriptive information is stored in the attributes of an entry, where each attribute describes a specific type of information. Here's an example showing attributes describing a person:

cn: Qusay H. Mahmoud
mail: qmahmoud@javacourses.com
telephoneNumber: 123-4567

An LDAP directory service can be used to look up someone's phone number or email address based on attributes. Table 2 shows some common LDAP attributes:

Table 2: Some common LDAP attributes
Attribute Meaning
o Organization
cn Common Name
sn Surname
uid Userid
mail Email address
c Country


LDAP names are a sequence of (name, value) pairs, such as name, organization, country:

cn=Qusay Mahmoud, o=javacourses.com, c=Canada

The javax.naming.directory.DirContext, which extends javax.naming.Context, is JNDI's directory services interface. It provides methods such as:

  • search: Searches the directory for matching directory entries, and to compare a directory entry to a set of attributes
  • bind and createSubcontext: Adds a new directory entry
  • modifyAttributes: Modifies specific attributes of a directory entry. The rename method can be used to modify the entry name itself.
  • unbind and destroySubcontext: Deletes a specific directory entry
  • close: Ends a session with an LDAP server

LDAP Programming with JNDI

To manipulate objects in an LDAP server (such as the Sun ONE Directory Server), you must first connect to the server; you may also need to authenticate yourself to the server. To connect to the server, you obtain a reference to an object from the DirContext interface. You do this with the InitialDirContext class, which takes a Hashtable.

The following snippet of code can be used to authenticate and connect the user to an LDAP server. Note that simple authentication is used here. Simple authentication consists of sending the LDAP server the fully qualified DN of the user and the user's clear-text password. To avoid exposing the clear-text password, use this mechanism with an encrypted channel (SSL) if it is supported by your LDAP server. For more information about authentication schemes, please see the JNDI Tutorial.

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
// specify where the ldap server is running
env.put(Context.PROVIDER_URL, "ldap://GH308C-N-MAHMOUD.humber.org:61596");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");
env.put(Context.SECURITY_CREDENTIALS, "password");
// Create the initial directory context
DirContext ctx = new InitialDirContext(env);

Once you're connected to the LDAP server, you can add new entries to the LDAP server, modify an entry, delete an entry, or search for an entry. The following snippet of code shows how to add, or store, a new entry. Note: To store an object, you need to load it with Java Schema, which doesn't come pre-configured with the directory server. For more information about this, please see Java Objects and the Directory in the JNDI tutorial.

SomeObject Obj = new SomeObjct("param1", "param2", "param3");
ctx.bind("cn=myobject", obj);

You can look up an object using the lookup method, as follows:

SomeObject obj = (SomeObject) ctx.lookup("cn=myobject");

Code Sample 3 shows an example of how to retrieve an attribute of a named object. As you can see, the code for selecting the factory class is the same as before. We use the InitialDirContext class to create a directory context, DirContext, the getAttributes method is used to return the attributes for an object, and finally the get method finds the surname and prints it. Straightforward, eh?

Code Sample 3: GetAttrib.java

import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attributes;
import javax.naming.NamingException;
import java.util.Hashtable;

class GetAttrib {
   public static void main(String[] argv) {
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
      // specify where the ldap server is running
      env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=javacourses.com,c=Canada");
      // use simple authenticate to authenticate the user
      env.put(Context.SECURITY_AUTHENTICATION, "simple");
      env.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");
      env.put(Context.SECURITY_CREDENTIALS, "password");

      try {
         // Create the initial directory context
         DirContext ctx = new InitialDirContext(env);

         // Ask for all attributes of the object
         Attributes attrs = ctx.getAttributes("cn=Qusay Mahmoud");

         // Find the surname ("sn") attribute of this object and print it
         System.out.println("Last Name: " + attrs.get("sn").get());

         // Close the context
         ctx.close();
      } catch (NamingException e) {
         System.err.println("Problem getting attribute: " + e);
      }
   }
}

JNDI provides APIs for basic as well as advanced (using filters) searching. As an example, a simple search can be performed using a set of attributes that an entry must have, and the name of the target context in which to perform the search. The following snippet of code shows how to search on a subtree for an entry that has the uid=qmahmoud attribute. Advanced searching using filters is beyond the scope of this article.

// ignore attribute name case
Attributes matchattribs = new BasicAttributes(true);
matchattribs.put(new BasicAttribute("uid", "qmahmoud"));
// search for objects with those matching attributes
NamingEnumeration answer = ctx.search("ou=People,o=javacourses.com", matchattribs);
while (answer.hasMore()) {
  SearchResult sr = (SearchResult)answer.next();
  // print the results you need
}

For more information on using JNDI for writing LDAP clients, please see Tips for LDAP Users.

CORBA COS Naming Service Provider for JNDI

The CORBA Common Object Services (COS) name server is used to store CORBA object references. You access it from a CORBA application using the COS Naming package (org.omg.CORBA.CosNaming).

The JNDI COS naming service provider implements the javax.naming.Context interface on top of the COS naming package so that CORBA applications can use JNDI to access the COS name server. Therefore, a CORBA application that uses JNDI would have one interface for accessing all naming and directory services. This enables CORBA applications to use a distributed enterprise-level service like LDAP for storing object references.

To select the COS naming service provider, use:

env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");

To convert your CORBA applications to use JNDI, consider AddServer.java and AddClient.java, described in greater detail in another article.

  1. Use javax.naming: In both the client and server, replace:

    import org.omg.CosNaming.*;
    import org.omg.CosNaming.NamingContextPackage.*;
    

    with:

    import javax.naming.*;
    
  2. Replace NameService with InitialContext in both client and server:

    replace:

    org.omg.CORBA.Object objRef =
            orb.resolve_initial_references("NameService");
    NamingContextExt ncRef =
            NamingContextExtHelper.narrow(objRef);
    

    with

    Hashtable env = new Hashtable();
    env.put("java.naming.corba.orb", orb);
    Context ctx = new InitialContext(env);
    
  3. Use lookup in place of resolve:

    replace:

    String name = "Add";
    Add href = AddHelper.narrow(ncRef.resolve_str(name));
    

    with:

    Add href = AddHelper.narrow((org.omg.CORBA.Object)ctx.lookup("Add"));
    

RMI Registry Service Provider for JNDI

The RMI registry service provider allows JNDI applications to access remote objects registered with the RMI registry. Once the location of the registry is known, the provider creates a naming context with bindings for the objects registered in the registry. This context may then be bound into another JNDI accessible namespace such as LDAP. This new functionality subsumes the functionality offered by the java.rmi.Naming class.

The key benefit of using RMI like this is that clients would no longer need to know the hostname and port number of where the RMI registry is running; it would be location-independent.

The following snippet of code shows how JNDI can be used with RMI:

// select the registry service provider as the initial context
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");

// specify where the registry is running
env.put(Context.PROVIDER_URL, "rmi://server:1099");

// create an initial context that accesses the registry
Context ctx = new InitialContext(env);

// now, the names stored in registry can be listed
NamingEnumeration enum = ctx.list("");

// bind the registry context into LDAP directory
Context ldapctx = (Context)ctx.lookup("ldap://server:port/o=comp,c=ca");
ldapctx.bind("cn=rmi", ctx);

DNS Service Provider for JNDI

The DNS service provider enables JNDI-based applications to access information stored in the DNS. The DNS service provider presents the DNS namespace as a tree of JNDI directory contexts, and the DNS resource records as JNDI attributes.

Code Sample 4 demonstrates how to work with the DNS service provider to retrieve the information of the environment and the IP address (the A records).

Code Sample 4: TestDNS.java

import javax.naming.*;
import com.sun.jndi.dns.*;
import java.util.Hashtable;

public class TestDNS {
   public static void main(String[] argv) {
      Name cn = null;
      String name = argv[0];
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY,
		    "com.sun.jndi.dns.DnsContextFactory");
      env.put(Context.PROVIDER_URL, "dns://IP for DNS Server/");

      try {
         // Create the initial context
         Context ctx = new InitialContext(env);
         // print the fully qualified name (.)
         System.out.println("Name in namespace: "+ctx.getNameInNamespace());
         // retrieve the parser associated with the named context
         NameParser np = ctx.getNameParser(ctx.getNameInNamespace());
	 if (argv.length != 1) {
            System.out.println("Usage: java TestDNS ");
            System.exit(-1);
         }
         // parse the name into its components and print them
         cn = np.parse(name);
         System.out.println("Name is: "+cn.toString());
         System.out.println("The parsed name has "+cn.size()+" components:");
         for (int i=0; i<cn.size(); i++){
            System.out.println(cn.get(i));
         }
         System.out.print("Trying to lookup ");
         // get the prefix (domain) and suffix (hostname)
         Name domain = cn.getPrefix(cn.size()-1);
         Name host = cn.getSuffix(cn.size()-1);
         System.out.println("DNS Host: "+host+" Domain: "+domain);
         // retrieve the named object
         Object obj = ctx.lookup(domain);
         System.out.println(domain.toString()+" is bound to: "+obj);
         // retrieve and print the environment in effect
         System.out.println("Domain properties: "+ ((Context)obj).getEnvironment());
         // retrieve and print the IP address (the DNS A records)
         System.out.println("IP for: "+cn+ " is: "+
                            ((DnsContext)obj).getAttributes(host, new String[]{"A"}));

         // we're done so close the context
         ctx.close();
      } catch (NamingException e) {
         System.err.println("Problem looking up " + cn + ": " + e);
      }
   }
}

Before you run this application, make sure you specify the IP address for the DNS server.

Here is a sample run:

prompt> java TestDNS prep.ai.mit.edu

Name in namespace: .
Name is: prep.ai.mit.edu
The parsed name has 4 components:
edu
mit
ai
prep
Trying to lookup DNS Host: prep Domain: ai.mit.edu
ai.mit.edu is bound to: com.sun.jndi.dns.DnsContext@b89838
Domain properties: {java.naming.provider.url=dns://IP for DNS Server/, java.namin
g.factory.initial=com.sun.jndi.dns.DnsContextFactory}
IP for: prep.ai.mit.edu is: {a=A: 199.232.41.9}

JNDI and J2EE

JNDI is one of the standard service APIs of the J2EE platform. It is included to give application components a standard API for referring to resources and other application components. J2EE also defines a standard naming policy (logical versus real names) to be used with JNDI, so that applications can be written in a deployment environment-independent manner. You can reference J2EE services by looking them up according to a logical name in a directory. For this to work, each J2EE-compliant system provides a JNDI service called an environment that contains:

  • environment variables
  • EJB references
  • resource factory references

Note: Here I will only discuss environment variables. For information on EJB references and resource factory references, please see this article.

Environment Variables

The application component's naming environment allows you to customize the application component, without the need to access or change the application component's source code. Each application component defines its own set of environment entries. All instances of an application component within the same container share the same entries. Note that application component instances are not allowed to modify the environment at runtime.

Declaring Environment Variables

The application component provider must declare all the environment entries accessed from the application's component code. They are declared using the <env-entry> tag in the deployment descriptor (web.xml in Tomcat, for example). The elements of the <env-entry> tag are:

  • <description>: an optional description of the environment entry.
  • <env-entry-name>: the environment entry name.
  • <env-entry-type>: the expected environment variable type. It can be one of the following Java types: Boolean, Byte, Double, Character, Float, Integer, Long, Short, String.
  • <env-entry-value>: a value for the environment entry that must match the type supplied within the
  • <env-entry-type>. This value can be changed later, but if it is not set, a value one must be specified during deployment.

The example in Code Sample 5 shows a declaration of two environment entries. To specify a declaration for a new environment, you simply add it to your web application descriptor (web.xml).

Code Sample 5: Declaring environment variables

<env-entry>
<description>welcome message</description>
<env-entry-name>greetings</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>Welcome to the Inventory Control
                           System</env-entry-value>
</env-entry>

<env-entry>
<description>maximum number of products</descriptor>
<env-entry-name>inventory/max</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>27</env-entry-value>
</env-entry>

Each <env-entry> tag describes a single environment entry. So, in this example, two environment entries have been defined. The first is named greetings, which is of type String and has an initial default value of: Welcome to the Inventory Control System. The second entry is named inventory/max, which is of type Integer and has an initial default value of 27.

Now, an application component instance can locate the environment entry using JNDI. It creates a javax.naming.InitialContext object using the constructor with no arguments. It then looks up the naming environment through the InitialContext, using the JNDI URL which starts with java:comp/env. Code Sample 6 shows how an application component accesses its environment entries.

Code Sample 6: Accessing environment entries

// obtain the application component's environment
// naming context
javax.naming.Context ctx =
new javax.naming.InitialContext();
javax.naming.Context env =
ctx.lookup("java:comp/env");

// obtain the greetings message
//configured by the deployer
String str = (String) env.lookup("greetings");

// use the greetings message
System.out.println(greetings);

// obtain the maximum number of products
//configured by the deployer
Integer maximum = (Integer) env.lookup(
"inventory/max");
//use the entry to customize business logic

Note that the application component may also look up environment entries using the full path names as follows:

javax.naming.Context ctx =
new javax.naming.InitialContext();
String str = (String) ctx.lookup(
"java:comp/env/greetings");

This snippet of code can be used in a JSP page as shown in Code Sample 7:

Code Sample 7: Accessing environment entries from a JSP page

<HTML>
<HEAD>
<TITLE>JSP Example</TITLE>
</HEAD>
<BODY BGCOLOR="#ffffcc">
<CENTER>
<H2>Inventory System</H2>
<%
javax.naming.Context ctx =
new javax.naming.InitialContext();
javax.naming.Context myenv =
   (javax.naming.Context) t.lookup("java:comp/env");
java.lang.String s = (java.lang.String)
    myenv.lookup("greetings");
out.println("The value is: "+greetings);
%>
</CENTER>

</BODY>
</HTML>

Conclusion

The JNDI is a set of APIs that can enhance your network applications with naming/directory services. The examples shown throughout the article demonstrate how easy it is to get started with JNDI to develop directory-based applications. The examples also demonstrate how you can use the same API to access different naming/directory services. Developers do not have to learn different APIs. In some cases, such as in RMI and CORBA applications, JNDI allows you to defer your choice of naming services until deployment time.

Some of the things to expect in the future from JNDI include: the incorporation of standard Java SASL API (JSR-28), support for internationalized domain names, and support for secure DNS.

To get started with JNDI and LDAP, download the trial version of the Sun ONE Directory Server, which is available for many platforms in difference languages.

For more information

LDAP v3
JNDI
The JNDI Tutorial
JNDI Service Providers
Sun ONE Directory Server
JNDI-INTEREST Mailing List (for discussing JNDI)

Acknowledgments

Special thanks to Jaya Hangal and Rosanna Lee of Sun Microsystems, whose feedback helped me improve this article.

Reader Feedback
Excellent   Good   Fair   Poor  

If you have other comments or ideas for future technical tips, please type them here:

Comments:
If you would like a reply to your comment, please submit your email address:
Note: We may not respond to all submitted comments.

Have a question about Java programming? Use Java Online Support.