Locating Resources Using JNDI (Java Naming and Directory Interface), Page 2
A popular protocol for accessing directory services is the Lightweight Directory and Access Protocol (LDAP). LDAP is a protocol that defines how client applications can manipulate data on a directory server, but says nothing about how the data should be stored. Generally speaking though, directory services usually allow you to store objects in a hierarchical fashion. LDAP servers, for example, arrange all objects in a tree known as the Directory Information Tree (DIT). The categorization of entries can simplify the search for particular objects. For example, a Yellow Pages directory might have categories for lawyers and carpet fitters. The categorized entries are a form of subcontext within the directory context of the Yellow Pages directory.
In essence, a directory service is really just a simple database that enables you to search for data, and to narrow that search by specifying search criteria. When you perform a search of a directory service, there are three pieces of information that you need to specify:
A search filter with the values of the attributes. For example, you could search for all employees whose gender attribute has the value male.
A search base that identifies the node in the tree that you want the search to begin from.
A scope that limits the depth of the tree that is searched.
Relational Database Management Systems (RDBMS) is another technology that might spring to mind while reading this discussion of directory services. An RDBMS enables you to create, update, retrieve, and remove entries that are stored within it, as does a directory service. One difference is that the internal data tends to be stored differently. This is because an RDBMS generally uses a relational information model that involves the use of tables. An RDBMS usually also supports transactions that allow a group of operations to be rolled back if a particular step fails for some reason. Directory services are designed more to be very quick at reading and searching for data, and use a hierarchical data model as mentioned earlier.
Note that the syntax for specifying names varies between naming services. For example, the Microsoft Windows operating system allows you to use the \ character to separate the components in a path such as c:\winnt\system32\drivers\etc\hosts. When you use the DNS naming convention, you specify a name, such as http://www.samspublishing.com, where each component in the path is separated by the . character.
LDAP servers use names that are based on the X.500 standard. Such names (known as distinguished names) have the following general form:
cn=Peter Szolkowski, ou=Bikers, o=MANX_RACERS, c=uk
This form might also be familiar to you if you have used Microsoft's Active Directory service, which also uses names based on the X.500 standard. The difference is that it uses the / character rather than commas to delimit the name components:
Both LDAP and Active Directory use hierarchical names. When the names are read from left to right, the most specific part of the name occurs first and the least specific part occurs last.
Tip - Some JNDI naming service implementations (also known as JNDI Service Providers) use case-sensitive names, and some do not. However, to maintain the portability of your applications you should avoid names that only differ by case. It is also a good idea to make sure that names are always spelled in a consistent fashion.
When you use JNDI, most of the time you simply specify a string that JNDI passes on to the underlying naming service with a minimum of interpretation. You should be aware that JNDI also provides support for creating and manipulating structured names.
Why Use a Naming Service?
One reason to use a naming service is that it enables you to decouple the provider of a service from its consumer. This is because the name that the supplier of the service uses to register the service is the only thing that the consumer needs to know.
Another reason to use a naming service is that it can provide an application with a single repository of information in which it can find all of its required resources.
When you use a naming service, you have a consistent way of publishing services that is independent of any particular platform and, therefore, is portable. In addition, you are free to migrate a service from one host to another. All that you would need to do is update the entry in the naming service to point to the new location of the service. The beauty of this is that the client needs to know nothing about the fact that the service has moved.
If you did not use JNDI to access a naming service, life would be a lot more difficult when you have to provide services such as those that are implemented using J2EE objects such as message queues, EJBs, and data sources. Every vendor would have to implement a proprietary mechanism that defined how client code gains access to J2EE objects. For example, one vendor might use TCP/IP broadcast network packets, whereas another could use textual configuration files.
What Is JNDI?
JNDI is a Java API that has been available since J2SE 1.2, and is also a part of J2EE 1.3.1. If you are using older versions of the SDKs, you should be aware that JNDI is also included in J2EE 1.2 and is available as a standard Java extension for JDK 1.2 and earlier releases.
The JNDI API defines an interface that Java programs can use to access a variety of naming and directory services in a uniform way. JNDI was designed specifically for the Java platform, and uses Java's object model. Therefore, you can use JNDI to store and retrieve Java objects of any type.
It is perhaps helpful to also state what JNDI is not: It is not a naming and directory service implementation, only an API. Thus, to use JNDI, you must also have available an implementation of a naming and directory service.
Without JNDI, it is necessary to learn the specific APIs that are implemented by the naming and directory service that you are using. This makes life a lot more difficult for application developers because they need to know all the APIs for the different naming and directory services used in their enterprise, thus leading to harder-to-maintain code. Figure 13.3 shows the architecture of a client and multiple services that each provides its own API.
The architecture of a system that does not use JNDI.
In fact, JNDI consists of both an Application Programmer's Interface (API) and a Service Provider's Interface (SPI). Figure 13.4 shows the architecture of how your application, the JNDI API, SPI, and the naming service implementations fit together. Because the JNDI API is defined in a way that is independent of any individual directory service implementation, it is possible to use additional naming services as long as they implement the SPI for JNDI. A service provider is basically a driver that your application can use to communicate with a directory service.
The JNDI architecture.
The JNDI architecture's layered design was constructed to help insulate client code from naming service provider code.
The JNDI classes and interfaces are divided into five main packages: javax.naming, javax.naming.directory, javax.naming.event, javax.naming.ldap, and javax.naming.spi. These packages are covered in the next five subsections of this chapter.
Note - You might be wondering why JNDI is so large. If all that you ever want to do is look up J2EE objects, Sun could have made JNDI as simple as a name to an object mapping service. Most of the time, this is exactly what you want. However, Sun designed JNDI to interoperate with many existing naming and directory services so that it would not be used as a proprietary product with J2EE servers.
The javax.naming Package
The javax.naming package contains the classes and interfaces that your application can use to access naming services. The Context and Name interfaces are part of this package, as well as the Reference and Binding classes.
The javax.naming.Binding Class
A binding is a set of information that contains an object's name, the name of the class used to instantiate the object as well as the actual object itself.
The javax.naming.Context Interface
Within a naming service, a set of bindings is referred to as a context. The javax. naming.Context interface is the principal interface in JNDI because it defines methods that enable you to
Bind objects to, and unbind objects from, names.
Retrieve objects with the lookup method.
You can also use the list and listBindings methods to retrieve an enumeration of name-to-object bindings. The listBindings method returns an enumeration of type javax.naming.NamingEnumeration, where each element in the enumeration is of type javax.naming.Binding, as described later.
The list method is more lightweight, in that it also returns an enumeration, but this time each element is of type javax.naming.NameClassPair. An instance of this class contains an object's name and the name of the class that was used to instantiate the object. The list method is useful when an application needs information about the object, but not the object itself. For example, you might be writing some kind of browser that displays a list of objects in the naming service.