Controls and Extensions

 


Response Controls

A response control allows a server to send more information to the client than is allowed by the operation's response. A one-to-one mapping between request controls and response controls is not needed. That is, a server can send response controls along with any response--they need not be in response to any client-initiated request control.

Because an LDAP server might send response controls with any response, you might be able to collect response controls after any Context method invocation. However, realistically, you would check for response controls only if you were expecting them.

LdapContext.getResponseControls() is used to retrieve a context's response controls. Each time that a method that communicates with the server is invoked on a context, the LDAP service provider clears any previously collected response controls and then collects all response controls resulting from the current method invocation.

For example, the following code fragment examines the response controls after a Context.lookup() call.

// Perform lookup
Object answer = ctx.lookup("ou=People");

// Retrieve the response controls
Control[] respCtls = ctx.getResponseControls();
If you invoke two context methods and then use getResponseControls(), you will get only the response controls generated by the most recent context method.

 

 

Enumerations

Methods such as Context.list() and DirContext.search() return a NamingEnumeration. Each member of a NamingEnumeration might have response controls. One that does will implement the HasControls interface.

Here is an example that shows you how to retrieve the response controls from each member of a NamingEnumeration that is generated by a search().

// Perform the search
NamingEnumeration answer = ctx.search("ou=People", "(cn=*)", null);

// Examine the response controls (if any)
printControls("After search", ctx.getResponseControls());

// Enumerate the answers
while (answer.hasMore()) {
    SearchResult si = (SearchResult)answer.next();
    System.out.println(si);

    // Examine the response controls (if any)
    if (si instanceof HasControls) {
        printControls(si.getName(), ((HasControls)si).getControls());
    }
}

// Examine the response controls (if any)
printControls("After enumeration", ctx.getResponseControls());
This example performs a search, examines the response controls after the search, and then enumerates the search results. Next, it checks whether any member of the enumeration implements the HasControls interface and, for any that do, displays the response controls associated with the member. After the enumeration completes, it checks for context response controls by using ctx.getResponseControls(). This example defines a utility method, printControls(), that prints out a Control array.

 

 

Exceptions

If a context method throws an exception and the LDAP server had sent response controls with the error response that generated the exception, then you can retrieve the response controls by using ctx.getResponseControls(). Here is an example.
try {
    // Perform the lookup
    Object answer = ctx.lookup("ou=People");

    // Retrieve the response controls
    Control[] respCtls = ctx.getResponseControls();

    // Display respCtls
} catch (NamingException e) {
    // Retrieve the response controls
    Control[] respCtls = ctx.getResponseControls();

    // Handle the exception
}

 

 

Implementations

The Control interface is generic for all request and response controls. Typically, you will deal with implementation classes that implement this interface rather than directly use the methods in this interface. Such implementation classes typically have type-friendly and accessor methods. After getting the response controls by using getResponseControls(), you can cast the control to its most derived class and use the class-specific accessor methods.

For example, Sun provides classes that implement some popular controls, such as the server-side Sort control. The server-side Sort response control is represented by the SortResponseControl class. You can use the following code to access information about the server-side Sort response control.

if (controls[i] instanceof SortResponseControl) {
    SortResponseControl src = (SortResponseControl)controls[i];
		
    if (src.isSorted()) {
	// Result was sorted ...
    }
}
...
To do this casting and use specific control classes, have some expectation of receiving such a control from the server and must have made the control classes available to your program. If either of these is lacking, then you can use only the methods in the Control interface to determine the identity of the control and to decode its contents.

 

 

Response Control Factories

The JNDI allows an application to use control implementation classes that are produced by any vendor. To help service providers achieve this goal, the JNDI provides the method ControlFactory.getControlInstance(Control, Context, Hashtable) for service providers to use to transform (that is, to narrow) generic controls received from an LDAP server into control classes that are made available to the application.

To narrow a control, you use a control factory, which is represented by the abstract class ControlFactory. A control received from an LDAP server begins life as a Control instance. The LDAP service provider uses getControlInstance() to narrow the control instance into a more type-specific one. This method searches the list of ControlFactory class implementations specified in the LdapContext.CONTROL_FACTORIES ("java.naming.factory.control") environment property for a class that can narrow the control.

For example, if an application uses an LDAP server that returns a special response control, then the application can define a response control factory to parse the control and to provide type-friendly accessor methods. Here is an example of a response control factory.

public  class SampleResponseControlFactory extends ControlFactory {
    public SampleResponseControlFactory() {
    }

    public Control getControlInstance(Control ctl) throws NamingException {
	String id = ctl.getID();

	// See if it's one of yours
	if (id.equals(SampleResponseControl.OID)) {
	    return new SampleResponseControl(id, ctl.isCritical(),
		ctl.getEncodedValue());
	} 

	// It's not one of yours, so return null and
	// let someone else try
	return null;
    }
}
The factory class must have a public constructor that accepts no arguments. It also must provide an implementation for the abstract method ControlFactory.getControlInstance(Control) . This method should check whether the input control is one that it can narrow. If it is, then the method should process the control and return an object of a more type-specific class. If it is not, then it should return null so that other factories may be tried.