CONTENTS | PREV | NEXT JNDI API


5. Overview of the Interface

The JNDI API is contained in four packages:

The JNDI service provider interface is contained one package:

The following sections provide an overview of the JNDI API. For more details on the API, see the corresponding javadoc.

5.1 The Naming Package -- javax.naming 1

The javax.naming package. The information in this graphic is available in the <a href=API documentation." WIDTH="591" HEIGHT="405" ALIGN="BOTTOM" BORDER="0" NATURALSIZEFLAG="3">

(exception classes are not shown)

5.1.1 Contexts

Context is the core interface that specifies a naming context. It defines basic operations such as adding a name-to-object binding, looking up the object bound to a specified name, listing the bindings, removing a name-to-object binding, creating and destroying subcontexts of the same type, etc.

public interface Context {
    public Object lookup(Name name) throws NamingException;
    public void bind(Name name, Object obj) throws NamingException;
    public void rebind(Name name, Object obj) throws NamingException;
    public void unbind(Name name) throws NamingException;
    public void rename(Name old, Name new) throws NamingException;
    public NamingEnumeration listBindings(Name name) throws NamingException;
    ...
    public Context createSubcontext(Name name) throws NamingException;
    public void destroySubcontext(Name name) throws NamingException;
    ...
};

Every naming method in Context takes a name as an argument. The operation defined by the method is performed on the Context object that is obtained by implicitly resolving the name. If the name is empty ("") the operation is performed directly on the context itself. The name of an object can be a composite name reflecting the arrangement of the namespaces used to refer to the object. Of course, the client is not exposed to any naming service implementation. In fact, a new type of naming service can be introduced without requiring the application to be modified or even disrupted if it is running.

 

5.1.2 The Initial Context

In JNDI, every name is relative to a context. There is no notion of "absolute names." An application can bootstrap by obtaining its first context of class InitialContext :

public class InitialContext implements Context {
    public InitialContext()...;
    ...
}

The initial context contains a variety of bindings that hook up the client to useful and shared contexts from one or more naming systems, such as the namespace of URLs or the root of DNS.

 

5.1.3 Names

The Name interface represents a generic name--an ordered sequence of components. Each Context method that takes a Name argument has a counterpart that takes the name as a String instead. The versions using Name are useful for applications that need to manipulate names: composing them, comparing components, and so on. The versions using String are likely to be more useful for simple applications, such as those that simply read in a name and look up the corresponding object. The String name parameter represents a composite name. The Name parameter can represent a composite name or a compound name .

The CompositeName class represents a sequence of names (atomic or compound) from multiple namespaces. If the Name parameter supplied to a method of the Context class is an instance of CompositeName , the name represents a composite name.

If the Name parameter supplied to a method of the Context class is not an instance of CompositeName , the name represents a compound name, which can be represented by the CompoundName class or some other implementation class. The CompoundName class represents hierarchical names from a single namespace. A context's name parser can be used to manipulate compound names in the syntax associated with that particular context:

public interface Context {
    ...
    public NameParser getNameParser(Name name) throws NamingException;
    ...
}

A namespace browser is an example of the kind of application that might need to manipulate names syntactically at this level. Most other applications will work with strings or composite names.

 

5.1.4 Bindings

Context.lookup() is the most commonly used operation. The context implementation can return an object of whatever class is required by the Java application. For example, a client might use the name of a printer to look up the corresponding Printer object, and then print to it directly:

Printer printer = (Printer) ctx.lookup("treekiller");
printer.print(report);

Context.listBindings() returns an enumeration of name-to-object bindings, each binding represented by an object of class Binding . A binding is a tuple containing the name of the bound object, the name of the object's class, and the object itself.

The Context.list() method is similar to listBindings() , except that it returns an enumeration of NameClassPair objects. Each NameClassPair contains an object's name and the name of the object's class. The list() method is useful for applications such as browsers that wish to discover information about the objects bound within a context, but don't need all of the actual objects. Although listBindings() provides all of the same information, it is potentially a much more expensive operation.

public class NameClassPair ... {
    public String getName() ...;
    public String getClassName() ...;
    ...
}
public class Binding extends NameClassPair {
    public Object getObject() ...;
    ...
}

5.1.5 References

Different Context implementations are able to bind different kinds of objects natively. A particularly useful object that should be supported by any general-purpose context implementation is the Reference class. A reference represents an object that exists outside of the directory. References are used to give JNDI clients the illusion that objects of arbitrary classes are able to be bound in naming or directory services--such as X.500--that do not have native support for objects in the Java programming language.

When the result of an operation such as Context.lookup() or Binding.getObject() is a Reference object, JNDI attempts to convert the reference into the object that it represents before returning it to the client. A particularly significant instance of this occurs when a reference representing a Context of one naming system is bound to a name in a different naming system. This is how multiple independent naming systems are joined together into the JNDI composite namespace. Details of how this mechanism operates are provided in the JNDI SPI document.

Objects that are able to be represented by a reference should implement the Referenceable interface. Its single method -- getReference() -- returns the object's reference. When such an object is bound to a name in any context, the context implementation might store the reference in the underlying system if the object itself cannot be stored natively.

Each reference may contain the name of the class of the object that it represents, and may also contain the location (typically a URL) where the class file for that object can be found. In addition, a reference contains a sequence of objects of class RefAddr . Each RefAddr in turn contains a "type" string and some addressing data, generally a string or a byte array.

A specialization of Reference called a LinkRef is used to add "symbolic" links into the JNDI namespace. It contains the name of a JNDI object. By default, these links are followed whenever JNDI names are resolved.

 

5.1.6 Referrals

Some naming/directory services support the notion of referrals for redirecting a client's request to another server. The JNDI client can request that referrals be automatically followed, be ignored, or be processed manually.

The abstract class ReferralException is used to represent a referral:

public abstract class ReferralException extends NamingException {
    public abstract Context getReferralContext() throws NamingException;
    ...
    public abstract Object getReferralInfo();
    public abstract void retryReferral();
    public abstract boolean skipReferral();
}

When a referral is encountered and the client has requested that referrals not be ignored or automatically followed, a ReferralException is thrown. The getReferralInfo() method provides information--in a format appropriate to the service provider--about where the referral leads. The application is not required to examine this information; however, it might choose to present it to a human user to help him determine whether to follow the referral or not. skipReferral() allows the application to discard a referral and continue to the next referral (if any).

To continue the operation, the application re-invokes the method on the referral context using the same arguments it supplied to the original method.

 

5.2 The Directory Package -- javax.naming.directory 2

 

The javax.naming.directory package. The information in this graphic is available in the <a href=API documentation." WIDTH="1002" HEIGHT="434" ALIGN="BOTTOM" BORDER="0" NATURALSIZEFLAG="3">

(exception classes are not shown)

5.2.1 Directory Objects

The DirContext interface enables the directory capability by defining methods for examining and updating attributes associated with a directory object.

public interface DirContext extends Context {
    public Attributes getAttributes(Name name)
		 throws NamingException;
    public Attributes getAttributes(Name name, String[] attrIds)
		 throws NamingException;
    ...
    public void modifyAttributes(Name name, int modOp, Attributes attrs)
		 throws NamingException;
    public void modifyAttributes(Name name, ModificationItem[] mods)
		 throws NamingException;
    ...
}

The getAttributes() operations on a directory return some or all of its attributes. Attributes are modified using two forms of modifyAttributes() . Both forms make use of a "modification operation," one of:

ADD_ATTRIBUTE
REPLACE_ATTRIBUTE
REMOVE_ATTRIBUTE

The ADD_ATTRIBUTE operation adds values to an attribute if that attribute already exists, while the REPLACE_ATTRIBUTE operation discards any pre-existing values. The first form of modifyAttributes() performs the specified operation on each element of a set of attributes. The second form takes an array of objects of class ModificationItem :

public class ModificationItem {
    public ModificationItem(int modOp, Attribute attr) ...;
    ...
}

Each operation is performed on its corresponding attribute in the order specified. When possible, a context implementation should perform each call to modifyAttributes() as an atomic operation.

5.2.2 Attributes

A directory object contains a set of zero or more Attribute objects. Each attribute is denoted by a string identifier and can have zero or more values of any type.

public interface Attribute ... {
    ...
    public String getID();
    public Object get(int n) throws NamingException;
    public boolean isOrdered();
    public NamingEnumeration getAll()
           throws NamingException;
    ...
}

An attribute's values can be ordered or unordered. If the values are unordered, no duplicates are allowed. If the values are ordered, duplicates are allowed.

Attributes are grouped into a collection by using the Attributes interface.

public interface Attributes ... {
    ...
    public Attribute get(String attrID);
    public NamingEnumeration getIDs();
    public NamingEnumeration getAll();
    public Attribute put(Attribute attr);
    public Attribute remove(String attrID);
    ...
}

JNDI provides implementations for these two interfaces, BasicAttribute and BasicAttributes , for convenience. Service providers and applications are free to use their own implementations.

Note that updates to Attributes and Attribute , such as adding or removing an attribute or its value, do not affect the corresponding representation in the directory. Updates to the directory can only be effected by using DirContext.modifyAttributes() .

 

5.2.3 Directory Objects as Naming Contexts

The DirContext interface also behaves as a naming context by extending the Context interface. This means that any directory object can also provide a naming context. In addition to a directory object keeping a variety of information about a person, for example, it is also a natural naming context for resources associated with that person: a person's printers, file system, calendar, etc.

Hybrid operations perform certain naming and directory operations in a single atomic operation:

public interface DirContext extends Context {
    ...
    public void bind(Name name, Object obj, Attributes attrs)
           throws NamingException;
    ...
}

Other hybrid operations that are provided are rebind() and createSubcontext() that accept an additional Attributes argument.

5.2.4 The Initial Context

An application that is performing directory operations can use InitialDirContext instead of javax.naming.InitialContext to create its initial context:

 
public class InitialDirContext 
       extends InitialContext implements DirContext {
    public InitialDirContext() ...;
    ...
}

It can then invoke any method in the Context or DirContext interface on the initial context.

5.2.5 Searches

The DirContext interface supports content-based searching of directories. In the simplest and most common form of usage, the application specifies a set of attributes -- possibly with specific values -- to match. It then invokes the DirContext.search() method on the directory object, which returns the matching directory objects along with the requested attributes.

public interface DirContext extends Context {
    ...
    public NamingEnumeration search(Name name, Attributes matchingAttributes)
		 throws NamingException;
    public NamingEnumeration search(Name name,
                                    Attributes matchingAttributes,
                                    String[] attributesToReturn)
		 throws NamingException;
    ...
}

The results of the search are returned as a NamingEnumeration containing an enumeration of objects of class SearchResult :

public class SearchResult extends Binding {
    ...
    public Attributes getAttributes() ...;
}

In the more sophisticated case, it is possible to specify a search filter and to provide controlling information such as the scope of the search and the maximum size of the results. The search filter specifies a syntax that follows Internet RFC 2254 for LDAP. The SearchControls argument specifies such things as the scope of the search: this can include a single directory object, all of its children, or all of its descendants in the directory hierarchy.

public interface DirContext extends Context {
    ...
    public NamingEnumeration search(Name name, 
                                    String filter,
                                    SearchControls ctls)
           throws NamingException;
 
    public NamingEnumeration search(Name name,
                                    String filter,
                                    Object[] filterArgs,
                                    SearchControls ctls)
 		throws NamingException;
   	...
}

5.2.6 Schema

A schema describes the rules that define the structure of a namespace and the attributes stored within it. The granularity of the schema can range from a single schema that is associated with the entire namespace, to a per-attribute, fine-grained schema description.

Because schemas can be expressed as an information tree, it is natural to use for this purpose the naming and directory interfaces already defined in JNDI. This is powerful because the schema part of a namespace is accessible to applications in a uniform way. A browser, for example, can access information in the schema tree just as though it were accessing any other directory objects.

Applications can retrieve the schema associated with a directory object when the underlying context implementation provides the appropriate support.

DirContext.getSchema() is used to retrieve the root of the schema tree associated with a directory object. The root has children such as "ClassDefinition", "AttributeDefinition", and "SyntaxDefinition", each denoting the kind of definition being described. The schema root and its descendents are objects of type DirContext . The DirContext.getSchemaClassDefinition() method returns a DirContext that contains class descriptions about a particular directory object.

public interface DirContext extends Context {
	...
	public DirContext getSchema(Name name)
		throws NamingException;

	public DirContext getSchemaClassDefinition(Name name)
		throws NamingException;
	...
}

In addition, the schema associated with any attribute can be accessed using the Attribute.getAttributeDefinition() and getAttributeSyntaxDefinition() methods.

 
public interface Attribute ... {
    ...
    public DirContext getAttributeDefinition() throws NamingException;
    public DirContext getAttributeSyntaxDefinition() 
    throws NamingException;
    ...
}

Example mapping Directory to Schema is an example showing the different associations for accessing schema information.

Example mapping Directory to Schema

Example mapping Directory to Schema

 

5.3 The Event Package -- javax.naming.event 3

The javax.naming.event package. The information in this graphic is available in the <a href=API documentation." WIDTH="769" HEIGHT="258" ALIGN="BOTTOM" BORDER="0" NATURALSIZEFLAG="3">

The javax.naming.event package contains classes and interfaces for supporting event notification in naming and directory services.

 

5.3.1 Naming Events

A NamingEvent represents an event that is generated by a naming/directory service.

public class NamingEvent extends java.util.EventObject {
    ...
    public int getType();
    public Binding getOldBinding();
    public Binding getNewBinding();
    ...
}

The event's type identifies the type of event. The NamingEvent class defines four types of events:

  1. OBJECT_ADDED
  2. OBJECT_REMOVED
  3. OBJECT_RENAMED
  4. OBJECT_CHANGED

These types can be placed into two categories:

In addition to the event's type, a NamingEvent contains other information about the change, such as information about the object before and after the change.

 

5.3.2 Naming Listeners

A naming listener is an object that registers for NamingEvent s. It is represented by the interface NamingListener . Each category of NamingEvent is handled by a corresponding subtype of NamingListener . The NamespaceChangeListener interface represents a listener interested in namespace changes, while the ObjectChangeListener represents a listener interested in changes to an object's contents. A listener implementation might implement one or both of these interfaces, depending on the types of events it is interested in.

 

5.3.3 Event Registration and Deregistration

The EventContext and EventDirContext interfaces extend the Context and DirContext interfaces, respectively, to support event registration and deregistration.

public interface EventContext extends Context {
    ...
    public void addNamingListener(Name target,
                                  int scope,
                                  NamingListener l)
           throws NamingException;
    public void removeNamingListener(NamingListener l)
           throws NamingException;
    public boolean targetMustExist()
           throws NamingException;
}

Like methods in the corresponding Context interface, addNamingListener() has an overload that accepts a String name argument. The name parameter is referred to as the target . The scope parameter specifies whether the registration is for the object named by the target, the immediate children of the context named by the target, or the entire subtree rooted at the object named by the target.

It is possible to register interest in a target that does not exist, but there might be limitations in the extent to which this can be supported by the service provider and underlying protocol/service. An application can use the method targetMustExist() to check whether an EventContext supports registration of nonexistent targets.

public interface EventDirContext extends EventContext, DirContext {
    public void addNamingListener(Name target,
                                  String filter, 
                                  SearchControls ctls, 
                                  NamingListener l)
                throws NamingException;
    public void addNamingListener(Name target,
                                  String filter,
                                  Object[] filterArgs,
                                  SearchControls ctls,
                                  NamingListener l)
                throws NamingException;
    ...
}

The EventDirContext interface extends the EventContext and DirContext interfaces to allow a listener to register interest in objects identified using search filters (Internet RFC 2254).

Like methods in the corresponding DirContext interface, addNamingListener() methods have overloads that accept a String name argument.

The EventContext/EventDirContext instance on which the addNamingListener() method is invoked is the event source of the events that are (potentially) generated. When the registered listener invokes getSource() or getEventContext() on a NamingEvent , the result will be this EventContext / EventDirContext instance.

For example, suppose a listener makes the following registration:

NamespaceChangeListener listener = ...; 
src.addNamingListener("x", SUBTREE_SCOPE, listener);

When an object named "x/y" is subsequently deleted, the corresponding NamingEvent ( evt ) delivered to listener must contain src as its event source. The following will both be true:

evt.getEventContext() == src
evt.getOldBinding().getName().equals("x/y")

5.3.4 Exception Handling

When a listener registers for events with a context, the context might need to do some internal processing in order to collect information required to generate the events. The context, for example, might need to make a request to the server to register interest in changes on the server that will eventually be translated into events. If an error occurs that prevents information about the events from being collected, the listener will never be notified of the events. When such an error occurs, a NamingExceptionEvent is fired to notify the listener, and the listener is automatically deregistered.

The base NamingListener interface defines a namingExceptionThrown() method so that a listener can be notified of such an error.

public interface NamingListener extends java.util.EventListener {
    public void namingExceptionThrown(NamingExceptionEvent evt);
}

5.4 The LDAP Package -- javax.naming.ldap 4

The javax.naming.ldap package. The information in the graphic is available in the <a href=API documentation." WIDTH="826" HEIGHT="429" ALIGN="BOTTOM" BORDER="0" NATURALSIZEFLAG="3">

 

The javax.naming.ldap package contains classes and interfaces for using LDAP v3-specific features that are not already covered by the more generic javax.naming.directory package. In fact, the majority of JNDI applications that use LDAP will find the javax.naming.directory package sufficient, and will not need to use this package at all. This package is primarily for those applications that need to use extended operations, controls, or unsolicited notifications.

5.4.1 Extended Operations

In addition to specifying well-defined operations such as search and modify, the LDAP v3 protocol (Internet RFC 2251) specifies a way of transmitting yet-to-be defined operations between the LDAP client and server. These operations are referred to as extended operations . An extended operation may be defined by a standards organization such as the IETF or by a vendor.

The LdapContext interface defines a method for executing an extended operation:

public interface LdapContext extends DirContext {
    public ExtendedResponse extendedOperation(ExtendedRequest request)
           throws NamingException;
    ...
}

The ExtendedRequest interface represents the argument to an extended operation, while the ExtendedResponse interface represents the result of the extended operation. An ExtendedRequest or ExtendedResponse consists of an identifier that identifies the extended operation and a byte array containing the ASN.1 BER encoded contents of the request/response.

An application typically does not deal directly with the ExtendedRequest / ExtendedResponse interfaces. Instead, it deals with classes that implement these interfaces. The application gets these classes either as part of a repertoire of extended operations standardized through the IETF, or from directory vendors for vendor-specific extended operations. The request classes should have constructors that accept arguments in a type-safe and user-friendly manner, while the response classes should have access methods for getting the data of the response in a type-safe and user-friendly manner. Internally, the request/response classes deal with encoding and decoding BER values.

For example, suppose an LDAP server supports a "get time" extended operation. It would supply classes such as GetTimeRequest and GetTimeResponse , so that applications can use this feature. An application would use these classes as follows:

GetTimeResponse resp =
   (GetTimeResponse)lctx.extendedOperation(new GetTimeRequest());
long time = resp.getTime();

 

5.4.2 Controls

The LDAP v3 protocol (Internet RFC 2251) allows any request or response to be augmented by yet-to-be defined modifiers. These modifiers are referred to as controls . Controls that are sent with requests are called request controls and those that are sent with responses are called response controls . A control may be defined by a standards organization such as the IETF or by a vendor. There is not necessarily a pairing between request controls and response controls.

JNDI classifies request controls into two categories:

Connection request controls are used whenever a connection needs to be established or re-established with an LDAP server. Context request controls are used when all other LDAP operations are sent to the LDAP server. The reason for this distinction is because JNDI is a high-level API that does not deal directly with connections. It is the job of service providers to do any necessary connection management. Hence, a single connection might be shared by multiple context instances, and a service provider is free to use its own algorithms to conserve connection and network usage. Thus, when a method is invoked on the context instance, the service provider might need to do some connection management in addition to performing the corresponding LDAP operations. For connection management, it uses the connection request controls, while for the normal LDAP operations, it uses the context request controls.

The LdapContext interface defines methods for dealing with controls:

public interface LdapContext extends DirContext { 
    public void reconnect(Control[] connCtls) throws NamingException; 
    public Control[] getConnectControls() throws NamingException; 
    ... 
    public LdapContext newInstance(Control[] reqCtls)
           throws NamingException; 
    public void setRequestControls(Control[] reqCtls) 
           throws NamingException; 
    public Control[] getRequestControls() throws NamingException; 
    ... 
    public Control[] getResponseControls() throws NamingException; 
}

The Control interface represents a control. It consists of an identifier that identifies the control and a byte array containing the ASN.1 BER encoded contents of the control.

Connection request controls are initialized using the initial context constructor and are inherited by contexts that are derived from a context. reconnect() is used to change the connection request controls of a context. A context's connection request controls are retrieved using getConnectControls() .

Context request controls are initialized using newInstance() and changed using setRequestControls() . newInstance() is a convenience method for creating a new instance of a context for the purposes of multithreaded access. For example, if multiple threads want to use different context request controls, each thread may use this method to get its own copy of this context and set/get context request controls without having to synchronize with other threads.

Unlike connection request controls, context request controls are not inherited by context instances that are derived from a context. Derived context instances are initialized with no context request controls. You must set the request controls of a derived context instance explicitly using setRequestControls() . A context's context request controls are retrieved using getRequestControls() .

 

5.4.3 The Initial Context

An application that is performing LDAP extended operations or controls can use InitialLdapContext instead of javax.naming.InitialContext or javax.naming.directory.InitialDirContext to create its initial context:

public class InitialLdapContext extends InitialDirContext implements LdapContext { 
    public InitialDirContext() ...; 
    public InitialDirContext(Hashtable env, Control[] connCtls) ...; 
}

It can then invoke any method in the Context , DirContext , or LdapContext interfaces on the initial context. By using the constructor that accepts a connCtls argument, the application can specify controls to be used when establishing a connection with the LDAP server.

 

5.4.4 Unsolicited Notifications

In addition to the normal request/response style of interaction between the client and server, the LDAP v3 protocol also specifies unsolicited notifications --messages that are sent from the server to the client asynchronously, not in response to any client request.

JNDI supports unsolicited notifications using the event model embodied in the javax.naming.event package. It defines an UnsolicitedNotificationEvent class and a corresponding UnsolicitedNotificationListener interface. An application registers to receive UnsolicitedNotificationEvent s by supplying an UnsolicitedNotificationListener to EventContext.addNamingListener() .


1. See Appendix C for legend of class diagram.

2. See Appendix C for legend of class diagram.

3. See Appendix C for legend of class diagram.

4. See Appendix C for legend of class diagram.


CONTENTS | PREV | NEXT
Copyright ©1997-1999 Sun Microsystems, Inc. All Rights Reserved.