diff --git a/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java b/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java index dcc9dc6c..271b2731 100644 --- a/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java +++ b/core/src/main/java/org/springframework/ldap/control/PagedResultsRequestControl.java @@ -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 null 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); } } diff --git a/core/src/test/java/org/springframework/ldap/control/PagedResultsRequestControlTest.java b/core/src/test/java/org/springframework/ldap/control/PagedResultsRequestControlTest.java index 0b72e5a1..7b6e3fe9 100644 --- a/core/src/test/java/org/springframework/ldap/control/PagedResultsRequestControlTest.java +++ b/core/src/test/java/org/springframework/ldap/control/PagedResultsRequestControlTest.java @@ -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 {