From ca0ac4cb815cfedd2e2db6131f813105e05d5d58 Mon Sep 17 00:00:00 2001 From: Ulrik Sandberg Date: Thu, 8 Jan 2009 16:43:50 +0000 Subject: [PATCH] LDAP-159: SortControlDirContextProcessor no longer has hard dependencies to ldapbp. --- changelog.txt | 4 +- .../SortControlDirContextProcessor.java | 348 ++++++++---------- .../SortControlDirContextProcessorTest.java | 4 +- 3 files changed, 166 insertions(+), 190 deletions(-) diff --git a/changelog.txt b/changelog.txt index 4f39c8fb..a0200a00 100644 --- a/changelog.txt +++ b/changelog.txt @@ -11,7 +11,7 @@ http://www.ietf.org/rfc/rfc2255.txt http://www.ietf.org/rfc/rfc2256.txt http://www.ietf.org/rfc/rfc2696.txt -Changes in version 1.3.0 (Nov 2008) +Changes in version 1.3.0 (Jan 2009) ------------------------------------------- * Added methods for simple LDAP 'bind' authentication in LdapOperations and SimpleLdapOperations. The methods will perform a search given a supplied filter, call ContextSource#getContext(dn, password), @@ -31,6 +31,8 @@ Changes in version 1.3.0 (Nov 2008) * Made sure article sample tests are possible to run without running web application (i.e. tests automatically start internal LDAP server) (LDAP-143). +* SortControlDirContextProcessor no longer has hard dependencies to controls in LDAP Booster Pack (LDAP-159). + * Added spring-tx as required dependency (the DataAccessExceptions require this). * Added demo projects to simplify refactoring demonstrations. (LDAP-154) diff --git a/core/src/main/java/org/springframework/ldap/control/SortControlDirContextProcessor.java b/core/src/main/java/org/springframework/ldap/control/SortControlDirContextProcessor.java index bf59a5aa..99f1f774 100644 --- a/core/src/main/java/org/springframework/ldap/control/SortControlDirContextProcessor.java +++ b/core/src/main/java/org/springframework/ldap/control/SortControlDirContextProcessor.java @@ -16,8 +16,7 @@ package org.springframework.ldap.control; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import javax.naming.NamingException; @@ -25,223 +24,198 @@ import javax.naming.directory.DirContext; import javax.naming.ldap.Control; import javax.naming.ldap.LdapContext; -import org.springframework.ldap.control.AbstractRequestControlDirContextProcessor; -import org.springframework.ldap.control.CreateControlFailedException; +import org.springframework.ldap.UncategorizedLdapException; +import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; -import com.sun.jndi.ldap.ctl.SortControl; -import com.sun.jndi.ldap.ctl.SortResponseControl; - /** - * DirContextProcessor implementation for managing the {@link SortControl}. - * Note that this class is stateful, so a new instance needs to be instantiated - * for each new search. + * DirContextProcessor implementation for managing the SortControl. Note that + * this class is stateful, so a new instance needs to be instantiated for each + * new search. * * @author Ulrik Sandberg */ -public class SortControlDirContextProcessor extends - AbstractRequestControlDirContextProcessor { +public class SortControlDirContextProcessor extends AbstractRequestControlDirContextProcessor { - private static final Class DEFAULT_RESPONSE_CONTROL = SortResponseControl.class; + private static final boolean CRITICAL_CONTROL = true; - private static final boolean CRITICAL_CONTROL = true; + private static final String DEFAULT_REQUEST_CONTROL = "javax.naming.ldap.SortControl"; - private static final String JAVA5_RESPONSE_CONTROL = "javax.naming.ldap.SortResponseControl"; + private static final String LDAPBP_REQUEST_CONTROL = "com.sun.jndi.ldap.ctl.SortControl"; - /** - * What key to sort on. - */ - private String sortKey; + private static final String DEFAULT_RESPONSE_CONTROL = "javax.naming.ldap.SortResponseControl"; - /** - * Whether the search result actually was sorted. - */ - private boolean sorted; + private static final String LDAPBP_RESPONSE_CONTROL = "com.sun.jndi.ldap.ctl.SortResponseControl"; - /** - * The result code of the supposedly sorted search. - */ - private int resultCode; + /** + * What key to sort on. + */ + private String sortKey; - private Class responseControlClass = DEFAULT_RESPONSE_CONTROL; + /** + * Whether the search result actually was sorted. + */ + private boolean sorted; - private Class fallbackResponseControlClass; + /** + * The result code of the supposedly sorted search. + */ + private int resultCode; - private Class currentResponseControlClass; + private Class responseControlClass; - /** - * Constructs a new instance using the supplied sort key. - * - * @param sortKey - * the sort key, i.e. the attribute name to sort on. - */ - public SortControlDirContextProcessor(String sortKey) { - this.sortKey = sortKey; - fallbackResponseControlClass = loadFallbackResponseControlClass(); - setSorted(false); - setResultCode(-1); - } + private Class requestControlClass; - /** - * Set the class of the expected ResponseControl for the sorted result - * response. The default is {@link SortResponseControl}. - * - * @param responseControlClass - * Class of the expected response control. - */ - public void setResponseControlClass(Class responseControlClass) { - this.responseControlClass = responseControlClass; - } + private boolean critical = CRITICAL_CONTROL; - /** - * Check whether the returned values were actually sorted by the server. - * - * @return true if the result was sorted, false - * otherwise. - */ - public boolean isSorted() { - return sorted; - } + /** + * Constructs a new instance using the supplied sort key. + * + * @param sortKey the sort key, i.e. the attribute name to sort on. + */ + public SortControlDirContextProcessor(String sortKey) { + this.sortKey = sortKey; + setSorted(false); + setResultCode(-1); - private void setSorted(boolean sorted) { - this.sorted = sorted; - } + loadControlClasses(); + } - /** - * Get the result code returned by the control. - * - * @return result code. - */ - public int getResultCode() { - return resultCode; - } + 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); - private void setResultCode(int sortResult) { - this.resultCode = sortResult; - } + 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 sort key. - * - * @return the sort key. - */ - public String getSortKey() { - return sortKey; - } + /** + * Set the class of the expected ResponseControl for the sorted result + * response. + * + * @param responseControlClass Class of the expected response control. + */ + public void setResponseControlClass(Class responseControlClass) { + this.responseControlClass = responseControlClass; + } - /** - * Set the sort key, i.e. the attribute on which to sort on. - * - * @param sortKey - * the sort key. - */ - public void setSortKey(String sortKey) { - this.sortKey = sortKey; - } + public void setRequestControlClass(Class requestControlClass) { + this.requestControlClass = requestControlClass; + } - /* - * @see org.springframework.ldap.control.AbstractRequestControlDirContextProcessor#createRequestControl() - */ - public Control createRequestControl() { - try { - return new SortControl(new String[] { sortKey }, CRITICAL_CONTROL); - } catch (IOException e) { - throw new CreateControlFailedException( - "Error creating SortControl", e); - } - } + /** + * Check whether the returned values were actually sorted by the server. + * + * @return true if the result was sorted, false + * otherwise. + */ + public boolean isSorted() { + return sorted; + } - /* - * @see org.springframework.ldap.core.DirContextProcessor#postProcess(javax.naming.directory.DirContext) - */ - public void postProcess(DirContext ctx) throws NamingException { - // initialize from property - currentResponseControlClass = responseControlClass; + private void setSorted(boolean sorted) { + this.sorted = sorted; + } - LdapContext ldapContext = (LdapContext) ctx; - Control[] responseControls = ldapContext.getResponseControls(); + /** + * Get the result code returned by the control. + * + * @return result code. + */ + public int getResultCode() { + return resultCode; + } - if (responseControls == null) { - return; - } + private void setResultCode(int sortResult) { + this.resultCode = sortResult; + } - // Go through response controls and get info, regardless of class - for (int i = 0; i < responseControls.length; i++) { - Control responseControl = responseControls[i]; + /** + * Get the sort key. + * + * @return the sort key. + */ + public String getSortKey() { + return sortKey; + } - // check for match, try fallback otherwise - if (isSortResponseControl(responseControl)) { - Object control = responseControl; - Boolean result = (Boolean) invokeMethod("isSorted", - currentResponseControlClass, control); - setSorted(result.booleanValue()); - Integer code = (Integer) invokeMethod("getResultCode", - currentResponseControlClass, control); - setResultCode(code.intValue()); - } - } - } + /** + * Set the sort key, i.e. the attribute on which to sort on. + * + * @param sortKey the sort key. + */ + public void setSortKey(String sortKey) { + this.sortKey = sortKey; + } - /** - * Check if the given control matches a sort 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 isSortResponseControl(Control responseControl) { - if (responseControl.getClass().isAssignableFrom( - currentResponseControlClass)) { - return true; - } - if (fallbackResponseControlClass != null - && responseControl.getClass().isAssignableFrom( - fallbackResponseControlClass)) { - currentResponseControlClass = fallbackResponseControlClass; - return true; - } - return false; - } + /* + * @see + * org.springframework.ldap.control.AbstractRequestControlDirContextProcessor + * #createRequestControl() + */ + public Control createRequestControl() { + Constructor constructor = ClassUtils.getConstructorIfAvailable(requestControlClass, new Class[] { + String[].class, boolean.class }); + if (constructor == null) { + throw new IllegalArgumentException("Failed to find an appropriate RequestControl constructor"); + } - 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; - } + Control result = null; + try { + result = (Control) constructor.newInstance(new Object[] { new String[] { sortKey }, + Boolean.valueOf(critical) }); + } + catch (Exception e) { + ReflectionUtils.handleReflectionException(e); + } - 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); - } + return result; + } - 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); - } + /* + * @see + * org.springframework.ldap.core.DirContextProcessor#postProcess(javax.naming + * .directory.DirContext) + */ + public void postProcess(DirContext ctx) throws NamingException { - // Retval will be set unless an exception has been thrown. - return retval; - } + LdapContext ldapContext = (LdapContext) ctx; + Control[] responseControls = ldapContext.getResponseControls(); + if (responseControls == null) { + responseControls = new Control[0]; + } + + // Go through response controls and get info, regardless of class + for (int i = 0; i < responseControls.length; i++) { + Control responseControl = responseControls[i]; + + // check for match, try fallback otherwise + if (responseControl.getClass().isAssignableFrom(responseControlClass)) { + Object control = responseControl; + Boolean result = (Boolean) invokeMethod("isSorted", responseControlClass, control); + setSorted(result.booleanValue()); + Integer code = (Integer) invokeMethod("getResultCode", responseControlClass, control); + setResultCode(code.intValue()); + return; + } + } + + log.fatal("No matching response control found for paged results - looking for '" + responseControlClass); + } + + private Object invokeMethod(String method, Class clazz, Object control) { + Method actualMethod = ReflectionUtils.findMethod(clazz, method); + return ReflectionUtils.invokeMethod(actualMethod, control); + } } diff --git a/core/src/test/java/org/springframework/ldap/control/SortControlDirContextProcessorTest.java b/core/src/test/java/org/springframework/ldap/control/SortControlDirContextProcessorTest.java index 6ee1e47c..e1f3ec63 100644 --- a/core/src/test/java/org/springframework/ldap/control/SortControlDirContextProcessorTest.java +++ b/core/src/test/java/org/springframework/ldap/control/SortControlDirContextProcessorTest.java @@ -20,6 +20,8 @@ import java.io.IOException; import javax.naming.ldap.Control; import javax.naming.ldap.LdapContext; import javax.naming.ldap.PagedResultsControl; +import javax.naming.ldap.SortControl; +import javax.naming.ldap.SortResponseControl; import junit.framework.TestCase; @@ -29,8 +31,6 @@ 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.SortControl; -import com.sun.jndi.ldap.ctl.SortResponseControl; /** * Unit tests for the SortControlDirContextProcessor class.