Control de Respuesta
Un control de respuesta permite al servidor enviar más
información al cliente de la permitida por la respuesta de la operación. No es necesario un mapeo de uno-a-uno entre los
controles de petición y los controles de respuesta. Es decir, un servidor puede enviar controles de respuesta junto con
cualquier respuesta--no necesita ser una respuesta a ningún control de petición iniciado por el cliente.
Como un servidor LDAP podría enviar controles de respuesta con cualquier respuesta, podríamos recoger controles
de respuesta después de cualquier llamada a métodos de Context. Sin embargo,
realísticamente, chequeriamos los controles de respuesta sólo si los esperaramos.
Se usa
LdapContext.getResponseControls() para recuperar un control de respuesta
de contexto. Cada vez que un método que se comunica con el servidor se llama sobre un contexto, el proveedor de servicio
LDAP elimina cualquier control de respuesta anterior y luego recoge los controles de respuesta resultantes de la
llamada del método actual.
Por ejemplo, el siguiente fragmento de código exmina los controles de respuesta después de una llamada a
Context.lookup():
// Perform lookup
Object answer = ctx.lookup("ou=People");
// Retrieve the response controls
Control[] respCtls = ctx.getResponseControls();
Si llamamos a dos métodos de contexto y luego usamos getResponseControls(), sólo
obtendremos los controles de respuesta generados por el método de contexto más reciente.
Enumeraciones
Los métodos como
Context.list() y
DirContext.search() devuelven una
NamingEnumeration. Cada miembro de una
NamingEnumeration podría tener controles de respuesta. Uno que implemente el interface
HasControls.
Aquí tenemos un ejemplo que muestra cómo recuperar los controles
de respuesta de cada miembro de una NamingEnumeration que es generada por un método
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());
Este ejemplo realiza una búsqueda, examina los controles de respuesta y luego enumera los resultados de la búsqueda.
Luego, chequea si algún miembro de la enumeración implementa el interface HasControls y,
para cualquiera que lo haga, muestra los controles de respuesta asociados con el miembro. Después de completar la
enumeración, busca controles de respuesta de contexto usando ctx.getResponseControls().
Este ejemplo define un método de utilidad, printControls(), que imprime un array de
Control.
Excepciones
Si un método de contexto lanza una excepción y el servidor LDAP ha enviado controles de respuesta con la respuesta de
error que generó la excepción, podremos recuperar los controles de respuesta usando
ctx.getResponseControls(). Aquí hay un ejemplo:
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
}
Implementaciones
El interface Control es genérico para todos los controles de petición y de respuesta.
Normalmente trataremos con clases de implementación que implementan este interfae en vez de usar directamente sus
métodos. Dichas clases de implementación normalmente tienen tipos amigables y métodos accesores. Después de obtener el
control de respuesta usando getResponseControls(), podemos forzar el control a su clase
más próxima y usar los métodos accesores específicos de la clase.
Por ejemplo, Sun proporciona clases que implementan algunos controles populares, como el control
Sort. Este control de ordenación en el
lado del servidor está representado por la clase SortResponseControl. Podemos usar el
siguiente código para acceder a la información sobre este control:
if (controls[i] instanceof SortResponseControl) {
SortResponseControl src = (SortResponseControl)controls[i];
if (src.isSorted()) {
// Result was sorted ...
}
}
...
Para hacer este forzado y usar clases de control específicas, debemos tener algunas espectativas de recibir dicho
control desde el servidor y debemos tener las clases disponibles para nuestro programa. Si no hacemos ninguna de estas
dos cosas, sólo podremos usar los métodos del interface Control para determinar la
identidad del control y decodificar su contenido.
Factorías de Controles de Respuesta
El JNDI permite a una aplicación usar clases de implementación de controles que son producidas por cualquier vendedor.
Para ayudar al proveedor de servicio a alcanzar este objetivo, el JNDI proporciona el método ControlFactory.getControlInstance(Control, Context, Hashtable)
para que el proveedor de servicio lo use para transformar (es decir, dirigir) controles
genéricos recibidos desde un servidor LDAP en clases de control que están disponibles para la aplicación.
Para dirigir un control usamos una factoría de controles, que está representada por la clase
abstracta ControlFactory.
Un control recibido desde un servidor LDAP empieza su vida como un ejemplar de Control.
El proveedor de servicio LDAP usa getControlInstance() para dirigir el ejemplar de control
dentro de uno de tipo más específico. Este método busca la lista de clases de implementaciones de
ControlFactory especificadas en la propiedad de entorno
LdapContext.CONTROL_FACTORIES
("java.naming.factory.control") una clase a la que pueda dirigir el control.
Por ejemplo, si una aplicación usa un servidor LDAP que devuelve un control de respuesta especial, entonces la
aplicación puede definir una factoría de controles de respuesta para analizar el control y proporcionar métodos
accesores de tipo amigable. Aquí tenemos un ejemplo
de una factoría de controles de respuesta:
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;
}
}
La clase factoría debe tener un constructor público que no acepte argumentos. También debe proporcionar una
implementación del método abstracto
ControlFactory.getControlInstance(Control). Este método debería chequear si
el control de entrada es uno que se puede dirigir. Si es así, el método debería procesar el control y devolver un objeto
de una clase más específica. Si no es así, el método debería devolver null
para que pudieran intentarlo otras factorías.