LDAP-159: SortControlDirContextProcessor no longer has hard dependencies to ldapbp.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 <code>true</code> if the result was sorted, <code>false</code>
|
||||
* 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 <code>true</code> if the result was sorted, <code>false</code>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user