The Context Interface The Java Naming and Directory Interface JNDI

This chapter has been something of a mixed bag. We first discussed the general idea of a naming service. This led to a discussion of the idea of federating, and from there we implemented our own naming service. Implementing our own naming service served two purposes: it helped to make the idea that there are many different possible naming services, each with their own advantages and disadvantages, more concrete. It also served as a fully worked example of a highly threaded server; our naming service is significantly more complex that any version of Account_Impl that will appear in this book. The final topic for this chapter is the Java Naming and Directory Interface JNDI. JNDI consists of three things: • A set of fairly generic interfaces that encapsulate the common operations performed on naming services. These interfaces are defined in the javax.naming and javax.naming.directory packages and include such interfaces as Context , Attribute , and Name . • A generic way for client code to initialize a connection to a naming service that implements the JNDI interfaces. JNDI provides the InitialContext class, defined in the javax.naming package, to enable code to connect to a naming service. InitialContext works by using various system properties to create an instance of Context for client code. • A device-driver model for naming service providers. Client code uses the interfaces and connects to a context using InitialContext . The last remaining piece involves connecting a naming service to JNDI. This requires two things: a set of classes that implement the JNDI interfaces and code that enables InitialContext and its supporting classes to create and configure the naming service.

15.8.1 The Context Interface

From the point of view of an application developer, the most important part of JNDI is the Context interface. At first glance, Context is intimidating: it defines 29 methods as opposed to the RMI registrys 5 methods or the 15 methods we defined in our naming service. However, when you take a closer look at them, Context becomes a much friendlier interface. Here are the methods defined in Context : public NamingEnumeration listName name throws NamingException public NamingEnumeration listString name throws NamingException public NamingEnumeration listBindingsName name throws Naming Exception public NamingEnumeration listBindingsString name throws NamingException public Object lookupName name throws NamingException public Object lookupString name throws NamingException public Object lookupLinkName name throws Naming Exception public Object lookupLinkString name throws NamingException public void bindName name, Object obj throws NamingException public void bindString name, Object obj throws NamingException public void rebindName name, Object obj thr ows NamingException public void rebindString name, Object obj throws NamingException public void unbindName name throws NamingException public void unbindString name throws NamingException public void renameName oldName, Name newName throws NamingE xception public void renameString oldName, String newName throws NamingException public Context createSubcontextName name throws NamingException public Context createSubcontextString name throws NamingException public void destroySubcontextName na me throws NamingException public void destroySubcontextString name throws NamingException public void close throws NamingException public String getNameInNamespace throws NamingException public Object addToEnvironmentString propName, Object p ropVal throws NamingException public Hashtable getEnvironment throws NamingException public Object removeFromEnvironmentString propName throws NamingException public Name composeNameName name, Name prefix throws NamingException public String composeNameString name, String prefix throws NamingException public NameParser getNameParserName name throws NamingException public NameParser getNameParserString name throws NamingException Twenty-one of the 29 methods defined in Context are variations on methods weve already seen. There are minor changes, of course. For example, list returns an instance of NamingEnumeration rather than an array of objects. And the process of unbinding a server and rebinding it under another name has been promoted to a full-fledged method called rename . However, all in all, most of this is fairly straightforward. NamingEnumeration NamingEnumeration is an interface that extends the Enumeration interface defined in the java.util package. Enumeration defines the following two methods: public boolean hasMoreElements public Object nextElement And NamingEnumeration adds three additional methods: public void close throws NamingException public boolean hasMore throws NamingException public Object next throws NamingException NamingEnumeration extends Enumeration for convenience™one piece of code can get NamingEnumeration and then pass it around to other objects as an instance of Enumeration which is a more familiar interface. The implementations of hasMore and hasMoreElements are functionally equivalent™its just that one throws a more detailed type of exception. You may wonder why NamingEnumeration doesnt extend Remote . As it is defined, the instances of NamingEnumeration have to be serialized and passed by value over the wire. Why not instead use the following interface: public interface NamingEnumeration extends Remote { public void close throws NamingException, RemoteException public boolean hasMore throws NamingException, RemoteException public Object next throws NamingException, RemoteException } This lets us use NamingEnumeration as a remote iterator one of our design guidelines from Chapt er 7 . But it also leads to two significant problems: • It makes JNDI RMI-centric. Other naming and directory services might not use RMI. Making the NamingEnumeration interface extend Remote , and declaring the methods to throw RemoteException , partially defeats the goal of a generic interface. • It forces instances of NamingEnumeration to be servers. If there are only five return values for a list... query, the query probably shouldnt cause the creation of a short -lived remote iterator. Instead, if we want to use a remote iterator for a particular response, we can create a subclass of NamingEnumeration that uses a stub on the remote iterator and forwards method calls. In this case, the instance of the subclass of NamingEnumeration becomes a client-side proxy for the remote iterator. The genuinely new functionality is contained in seven methods. The first three methods enable Context to store environment variables in an instance of Hashtable for easy retrieval: public Object addToEnvironmentString prop Name, Object propVal throws NamingException public Hashtable getEnvironment throws NamingException public Object removeFromEnvironmentString propName throws NamingException There are two main differences between a name-value pair bound into a context and a name- value pair that defines an environment variable: • When you use bind , theres an implicit understanding that what youre binding is a complex structure of some type. It doesnt necessarily have to be a server, [ 8] but its usually an object with methods and structure. [ 8] In the case of RMI, the object being bound has to be a server. But with other JNDI contexts, the LDAP JNDI implementation for example, there are no restrictions at all. • When you use bind , youre explicitly allowing for paths, complex names, and more general queries. For example, if the context you bind into is a subcontext of another context, the object that has been bound may be accessible via a path-based lookup. Environment variables are explicitly local and can only be retrieved by name. Its not clear when environment variables are a good idea. Theyre often convenient theyre the conceptual equivalent of attaching a sticky note to the context, but they dont really add any functionality. And using such a weakly typed interface to store important information can only cause headaches. The final four methods are: public Name composeNameName name, Name prefix throws NamingException public String composeNameString name, String prefix throws NamingException public NameParser getNameParserName name throws NamingException public NameParser getNameParserString name throws NamingException These are an attempt to get around the fact that different naming services use different formatting conventions for their names. For example, the RMI registry uses strings with the following format: rmi: host:port humanReadableName The naming service we implemented, on the other hand, doesnt use a URL format. Instead, it just uses a path-based notation to choose between contexts. The NamingParser interface lets client code parse, and deal with components of names, without having to know the exact details of the format. JNDI Service Providers A JNDI Service Provider Implementation is an implementation of the JNDI interfaces, including the code that enables Initial Context to find the naming service so specified. As of this writing, JDK 1.3 ships with two JNDI Service Provider implementations: one that wraps the RMI registry, and one that wraps the CORBA naming service. In addition, you can download several additional service providers from Javasofts web site. Currently, the Javasoft web site contains JNDI wrappers for DNS, LDAP, and the filesystem. Of course, third-party software companies such as Novell provide JNDI wrappers for their directory-based products.

15.8.2 Using JNDI with the Bank Example