Accessing Objects in the Directory

«« Previous
Next »»

Traditionally, directories have been used to store data. Users and programmers think of the directory as a hierarchy of directory entries, each containing a set of attributes. You look up an entry from the directory and extract the attribute(s) of interest.

For applications written in the Java programming language, Java objects may sometimes be shared across applications. For such applications, it makes sense to be able to use the directory as a repository for Java objects. The directory provides a centrally administered, and possibly replicated, service for use by Java applications distributed across the network. For example, an application server might use the directory for registering objects that represent the services that it manages so that a client can later search the directory to locate those services as needed. An example of JNDI used as a directory of services is Apache DS.

The JNDI provides an object-oriented view of the directory, thereby allowing Java objects to be added to and retrieved from the directory without requiring the client to manage data representation issues. This lesson discusses the use of the directory for storing and retrieving Java objects at a basic level. The JNDI provides what are known as object and state factories for creating and storing the objects accessed from the directory.

Object Factory

An object factory is a producer of objects. It accepts some information about how to create an object, such as a reference, and then returns an instance of that object.

State Factory

A state factory transforms an object into another object. The input is the object and optional attributes, supplied to Context.bind() and the output is another object and optional attributes, to be stored in the underlying naming service or directory.

Storing and Reading Objects


Applications and services can use the directory in different ways to store and locate objects:

◈ Store (a copy of) the object itself.
◈ Store a reference to an object.
◈ Store attributes that describe the object.

In general terms, a Java object's serialized form contains the object's state and an object's reference in a compact representation of addressing information that can be used to contact the object. Some examples are given in the Lookup an Object lesson. An object's attributes are properties that are used to describe the object; attributes might include addressing and/or state information.

Which of these three ways to use depends on the application/system that is being built and how it needs to interoperate with other applications and systems that will share the objects stored in the directory. Another factor is the support provided by the service provider and the underlying directory service.

Programmatically, all applications use one of the following methods when storing objects in the directory:

◈ Context.bind()
◈ DirContext.bind()
◈ Context.rebind()
◈ DirContext.rebind()

The application passes the object that it wants to store to one of these methods. Then, depending on the types of objects that the service provider supports, the object will be transformed into a representation acceptable to the underlying directory service.

This lesson shows how to store serializable objects in the directory once the object is stored, you can simply use lookup()to get a copy of the object back from the directory, regardless of what type of information was actually stored.

You can get the object back not only by using lookup(), but also when you list a context and when you search a context or its subtree. In all of these cases, object factories might be involved.

Before you go on: To run these examples successfully, you must either turn off schema-checking in the server or add the Java schema that accompany this tutorial to the server. This task is typically performed by the directory server's administrator.

Windows Active Directory: Context.rebind() and DirContext.rebind() do not work against Active Directory because these methods work by reading the attributes of the entry to be updated, removing the entry, and then adding a new entry that contains the modified attributes. Active Directory returns some attributes that cannot be set by the user, causing the final addition step to fail. The workaround for this problem is to use DirContext.getAttributes() to obtain and save the attributes that you want to keep. Then, remove the entry and add it back with the saved attributes (and any others that you want to add) using DirContext.bind().

Serializable Objects


To serialize an object means to convert its state to a byte stream so that the byte stream can be reverted back into a copy of the object. A Java object is serializable if its class or any of its superclasses implements either the java.io.Serializable interface or its subinterface, java.io.Externalizable. Deserialization is the process of converting the serialized form of an object back into a copy of the object.

For example, the java.awt.Button class implements the Serializable interface, so you can serialize a java.awt.Button object and store that serialized state in a file. Later, you can read back the serialized state and deserialize into a java.awt.Button object.

The Java platform specifies a default way by which serializable objects are serialized. A (Java) class can override this default serialization and define its own way of serializing objects of that class.

When an object is serialized, information that identifies its class is recorded in the serialized stream. However, the class's definition ("class file") itself is not recorded. It is the responsibility of the system that is deserializing the object to determine how to locate and load the necessary class files. For example, a Java application might include in its classpath a JAR file that contains the class files of the serialized object(s) or load the class definitions by using information stored in the directory, as explained later in this lesson.

Binding a Serializable Object

You can store a serializable object in the directory if the underlying service provider supports that action, as does Oracle's LDAP service provider.

The following example invokes Context.bind to bind an AWT button to the name "cn=Button". To associate attributes with the new binding, you use DirContext.bind. To overwrite an existing binding, use Context.rebind and DirContext.rebind.

// Create the object to be bound
Button b = new Button("Push me");

// Perform the bind
ctx.bind("cn=Button", b);

You can then read the object back using Context.lookup, as follows.

// Check that it is bound
Button b2 = (Button)ctx.lookup("cn=Button");
System.out.println(b2);

Running this example produces the following output.

# java SerObj
java.awt.Button[button0,0,0,0x0,invalid,label=Push me]

Specifying a Codebase

Note: The procedures described here are for binding a serializable object in a directory service that follows the schema defined in RFC 2713. These procedures might not be generally applicable to other naming and directory services that support binding a serializable object with a specified codebase.

When a serialized object is bound in the directory as shown in the previous example, applications that read the serialized object from the directory must have access to the class definitions necessary to deserialize the object.

Alternatively, you can record a codebase with the serialized object in the directory, either when you bind the object or subsequently by adding an attribute by using DirContext.modifyAttributes. You can use any attribute to record this codebase and have your application read that attribute from the directory and use it appropriately. Or you can use the "javaCodebase" attribute specified in . In the latter case, Oracle's LDAP service provider will automatically use the attribute to load the class definitions as needed. "javaCodebase" should contain the URL of a codebase directory or a JAR file. If the codebase contains more than one URL, then each URL must be separated by a space character.

The following example resembles the one for binding a java.awt.Button. It differs in that it uses a user-defined Serializable class, Flower, and supplies a "javaCodebase" attribute that contains the location of Flower's class definition. Here's the code that does the binding.

String codebase = ...;

// Create the object to be bound
Flower f = new Flower("rose", "pink");

// Perform the bind and specify the codebase
ctx.bind("cn=Flower", f, new BasicAttributes("javaCodebase", codebase));

When you run this example, you must supply the URL of the location at which the class file Flower.class was installed. For example, if Flower.class was installed at the Web server web1, in the directory example/classes, then you would run this example as follows.

# java SerObjWithCodebase http://web1/example/classes/
pink rose

Afterward, you may remove Flower.class from your classpath and run any program that looks up or lists this object without directly referencing the Flower class. If your program references Flower directly, then you must make its class file available for compilation and execution.

«« Previous
Next »»