Removed dependency on ldapbp classes for paged results. All classes are now dynamically resolved.

This commit is contained in:
Mattias Arthursson
2008-10-21 12:33:05 +00:00
parent f078b5d657
commit 2517e0e94e
2 changed files with 86 additions and 125 deletions

View File

@@ -16,34 +16,32 @@
package org.springframework.ldap.control;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
import org.springframework.util.ReflectionUtils;
import com.sun.jndi.ldap.ctl.PagedResultsControl;
import com.sun.jndi.ldap.ctl.PagedResultsResponseControl;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* DirContextProcessor implementation for managing the paged results control.
*
*
* @author Mattias Arthursson
* @author Ulrik Sandberg
*/
public class PagedResultsRequestControl extends
AbstractRequestControlDirContextProcessor {
private static final Class DEFAULT_RESPONSE_CONTROL = PagedResultsResponseControl.class;
private static final boolean CRITICAL_CONTROL = true;
private static final String JAVA5_RESPONSE_CONTROL = "javax.naming.ldap.PagedResultsResponseControl";
private static final String DEFAULT_REQUEST_CONTROL = "javax.naming.ldap.PagedResultsControl";
private static final String LDAPBP_REQUEST_CONTROL = "com.sun.jndi.ldap.ctl.PagedResultsControl";
private static final String DEFAULT_RESPONSE_CONTROL = "javax.naming.ldap.PagedResultsResponseControl";
private static final String LDAPBP_RESPONSE_CONTROL = "com.sun.jndi.ldap.ctl.PagedResultsResponseControl";
private int pageSize;
@@ -51,19 +49,17 @@ public class PagedResultsRequestControl extends
private int resultSize;
private Class responseControlClass = DEFAULT_RESPONSE_CONTROL;
private boolean critical = CRITICAL_CONTROL;
private Class fallbackResponseControlClass;
private Class currentResponseControlClass;
private Class responseControlClass;
private Class requestControlClass;
/**
* Constructs a new instance. This constructor should be used when
* performing the first paged search operation, when no other results have
* been retrieved.
*
* @param pageSize
* the page size.
*
* @param pageSize the page size.
*/
public PagedResultsRequestControl(int pageSize) {
this(pageSize, null);
@@ -74,21 +70,37 @@ public class PagedResultsRequestControl extends
* cookie must be the exact same instance as received from a previous paged
* resullts search, or <code>null</code> if it is the first in an
* operation sequence.
*
* @param pageSize
* the page size.
* @param cookie
* the cookie, as received from a previous search.
*
* @param pageSize the page size.
* @param cookie the cookie, as received from a previous search.
*/
public PagedResultsRequestControl(int pageSize, PagedResultsCookie cookie) {
this.pageSize = pageSize;
this.cookie = cookie;
fallbackResponseControlClass = loadFallbackResponseControlClass();
loadControlClasses();
}
private void loadControlClasses() {
try {
requestControlClass = Class.forName(DEFAULT_REQUEST_CONTROL);
responseControlClass = Class.forName(DEFAULT_RESPONSE_CONTROL);
} catch (ClassNotFoundException e) {
log.debug("Default control classes not found - falling back to LdapBP classes", e);
try {
requestControlClass = Class.forName(LDAPBP_REQUEST_CONTROL);
responseControlClass = Class.forName(LDAPBP_RESPONSE_CONTROL);
} catch (ClassNotFoundException e1) {
throw new UncategorizedLdapException("Neither default nor fallback classes are available - unable to proceed", e);
}
}
}
/**
* Get the cookie.
*
*
* @return the cookie.
*/
public PagedResultsCookie getCookie() {
@@ -97,7 +109,7 @@ public class PagedResultsRequestControl extends
/**
* Get the page size.
*
*
* @return the page size.
*/
public int getPageSize() {
@@ -108,7 +120,7 @@ public class PagedResultsRequestControl extends
* Get the total estimated number of entries that matches the issued search.
* Note that this value is optional for the LDAP server to return, so it
* does not always contain any valid data.
*
*
* @return the estimated result size, if returned from the server.
*/
public int getResultSize() {
@@ -117,38 +129,47 @@ public class PagedResultsRequestControl extends
/**
* Set the class of the expected ResponseControl for the paged results
* response. The default is {@link PagedResultsResponseControl}.
*
* @param responseControlClass
* Class of the expected response control.
* response.
*
* @param responseControlClass Class of the expected response control.
*/
public void setResponseControlClass(Class responseControlClass) {
this.responseControlClass = responseControlClass;
}
/*
* @see org.springframework.ldap.control.AbstractRequestControlDirContextProcessor#createRequestControl()
*/
public Control createRequestControl() {
try {
if (cookie != null) {
return new PagedResultsControl(pageSize, cookie.getCookie(),
CRITICAL_CONTROL);
} else {
return new PagedResultsControl(pageSize);
}
} catch (IOException e) {
throw new CreateControlFailedException(
"Error creating PagedResultsControl", e);
}
public void setRequestControlClass(Class requestControlClass) {
this.requestControlClass = requestControlClass;
}
/*
* @see org.springframework.ldap.core.DirContextProcessor#postProcess(javax.naming.directory.DirContext)
* @see org.springframework.ldap.control.AbstractRequestControlDirContextProcessor#createRequestControl()
*/
public Control createRequestControl() {
byte[] actualCookie = null;
if (cookie != null) {
actualCookie = cookie.getCookie();
}
Constructor constructor = ClassUtils.getConstructorIfAvailable(requestControlClass, new Class[]{int.class, byte[].class, boolean.class});
if (constructor == null) {
throw new IllegalArgumentException("Failed to find an appropriate RequestControl constructor");
}
Control result = null;
try {
result = (Control) constructor.newInstance(new Object[]{new Integer(pageSize), actualCookie, new Boolean(critical)});
} catch (Exception e) {
ReflectionUtils.handleReflectionException(e);
}
return result;
}
/*
* @see org.springframework.ldap.core.DirContextProcessor#postProcess(javax.naming.directory.DirContext)
*/
public void postProcess(DirContext ctx) throws NamingException {
// initialize from property
currentResponseControlClass = responseControlClass;
LdapContext ldapContext = (LdapContext) ctx;
Control[] responseControls = ldapContext.getResponseControls();
@@ -161,78 +182,23 @@ public class PagedResultsRequestControl extends
Control responseControl = responseControls[i];
// check for match, try fallback otherwise
if (isPagedResultsResponseControl(responseControl)) {
if (responseControl.getClass().isAssignableFrom(responseControlClass)) {
Object control = responseControl;
byte[] result = (byte[]) invokeMethod("getCookie",
currentResponseControlClass, control);
responseControlClass, control);
this.cookie = new PagedResultsCookie(result);
Integer wrapper = (Integer) invokeMethod("getResultSize",
currentResponseControlClass, control);
responseControlClass, control);
this.resultSize = wrapper.intValue();
return;
}
}
}
/**
* Check if the given control matches a paged results response control. Try
* the fallback class from Java5 if there is no match. Set the
* {@link #currentResponseControlClass} to the fallback if it matches.
*
* @param responseControl
* the control to check for a match
* @return whether the control is a paged results response control
*/
private boolean isPagedResultsResponseControl(Control responseControl) {
if (responseControl.getClass().isAssignableFrom(
currentResponseControlClass)) {
return true;
}
if (fallbackResponseControlClass != null
&& responseControl.getClass().isAssignableFrom(
fallbackResponseControlClass)) {
currentResponseControlClass = fallbackResponseControlClass;
return true;
}
return false;
}
private Class loadFallbackResponseControlClass() {
Class fallbackResponseControlClass = null;
try {
fallbackResponseControlClass = Class
.forName(JAVA5_RESPONSE_CONTROL);
} catch (ClassNotFoundException e) {
log.debug("Could not load Java5 response control class "
+ JAVA5_RESPONSE_CONTROL);
}
return fallbackResponseControlClass;
log.fatal("No matching response control found for paged results - looking for '" + responseControlClass);
}
private Object invokeMethod(String method, Class clazz, Object control) {
// For Spring 2.0 ReflectionUtils could be used for all of this, but
// since we still want to support the 1.2 branch we do it manually and
// only use the stuff present in 1.2.8.
Method actualMethod = null;
Object retval = null;
try {
actualMethod = clazz.getMethod(method, new Class[0]);
} catch (SecurityException e) {
ReflectionUtils.handleReflectionException(e);
} catch (NoSuchMethodException e) {
ReflectionUtils.handleReflectionException(e);
}
try {
retval = actualMethod.invoke(control, new Object[0]);
} catch (IllegalArgumentException e) {
ReflectionUtils.handleReflectionException(e);
} catch (IllegalAccessException e) {
ReflectionUtils.handleReflectionException(e);
} catch (InvocationTargetException e) {
ReflectionUtils.handleReflectionException(e);
}
// Retval will be set unless an exception has been thrown.
return retval;
Method actualMethod = ReflectionUtils.findMethod(clazz, method);
return ReflectionUtils.invokeMethod(actualMethod, control);
}
}

View File

@@ -15,23 +15,18 @@
*/
package org.springframework.ldap.control;
import java.io.IOException;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.ldap.control.PagedResultsCookie;
import org.springframework.ldap.control.PagedResultsRequestControl;
import com.sun.jndi.ldap.Ber;
import com.sun.jndi.ldap.BerDecoder;
import com.sun.jndi.ldap.BerEncoder;
import com.sun.jndi.ldap.ctl.DirSyncResponseControl;
import com.sun.jndi.ldap.ctl.PagedResultsControl;
import com.sun.jndi.ldap.ctl.PagedResultsResponseControl;
import junit.framework.TestCase;
import org.easymock.MockControl;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.PagedResultsControl;
import javax.naming.ldap.PagedResultsResponseControl;
import java.io.IOException;
public class PagedResultsRequestControlTest extends TestCase {