LDAP-160, LDAP-161: Cleaned up DirContextProcessor implementations.
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
package org.springframework.ldap.control;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.ldap.Control;
|
||||
import javax.naming.ldap.LdapContext;
|
||||
|
||||
import org.springframework.ldap.UncategorizedLdapException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Convenient base class useful when implementing a standard DirContextProcessor
|
||||
* which has a request control and a response control. It handles the loading of
|
||||
* the control classes, using fallback implementations specified by the subclass
|
||||
* if necessary. It handles the request control constructor invocation; it only
|
||||
* needs the constructor arguments to be provided. It also handles most of the
|
||||
* work in the post processing of the response control, only delegating to a
|
||||
* template method for the actual value retrieval. In short, it makes it easy to
|
||||
* implement a custom DirContextProcessor.</p>
|
||||
*
|
||||
* <pre>
|
||||
* public class SortControlDirContextProcessor extends AbstractFallbackRequestAndResponseControlDirContextProcessor {
|
||||
* String sortKey;
|
||||
*
|
||||
* private boolean sorted = false;
|
||||
*
|
||||
* private int resultCode = -1;
|
||||
*
|
||||
* public SortControlDirContextProcessor(String sortKey) {
|
||||
* this.sortKey = sortKey;
|
||||
*
|
||||
* defaultRequestControl = "javax.naming.ldap.SortControl";
|
||||
* defaultResponseControl = "com.sun.jndi.ldap.ctl.SortControl";
|
||||
* fallbackRequestControl = "javax.naming.ldap.SortResponseControl";
|
||||
* fallbackResponseControl = "com.sun.jndi.ldap.ctl.SortResponseControl";
|
||||
*
|
||||
* loadControlClasses();
|
||||
* }
|
||||
*
|
||||
* public boolean isSorted() {
|
||||
* return sorted;
|
||||
* }
|
||||
*
|
||||
* public int getResultCode() {
|
||||
* return resultCode;
|
||||
* }
|
||||
*
|
||||
* public Control createRequestControl() {
|
||||
* return super.createRequestControl(new Class[] { String[].class, boolean.class }, new Object[] {
|
||||
* new String[] { sortKey }, Boolean.valueOf(critical) });
|
||||
* }
|
||||
*
|
||||
* protected void handleResponse(Object control) {
|
||||
* Boolean result = (Boolean) invokeMethod("isSorted", responseControlClass, control);
|
||||
* this.sorted = result.booleanValue();
|
||||
* Integer code = (Integer) invokeMethod("getResultCode", responseControlClass, control);
|
||||
* resultCode = code.intValue();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Ulrik Sandberg
|
||||
*/
|
||||
public abstract class AbstractFallbackRequestAndResponseControlDirContextProcessor extends
|
||||
AbstractRequestControlDirContextProcessor {
|
||||
|
||||
private static final boolean CRITICAL_CONTROL = true;
|
||||
|
||||
protected Class responseControlClass;
|
||||
|
||||
protected Class requestControlClass;
|
||||
|
||||
protected boolean critical = CRITICAL_CONTROL;
|
||||
|
||||
protected String defaultRequestControl;
|
||||
|
||||
protected String defaultResponseControl;
|
||||
|
||||
protected String fallbackRequestControl;
|
||||
|
||||
protected String fallbackResponseControl;
|
||||
|
||||
protected void loadControlClasses() {
|
||||
Assert.notNull(defaultRequestControl, "defaultRequestControl must not be null");
|
||||
Assert.notNull(defaultResponseControl, "defaultResponseControl must not be null");
|
||||
Assert.notNull(fallbackRequestControl, "fallbackRequestControl must not be null");
|
||||
Assert.notNull(fallbackResponseControl, "fallbackReponseControl must not be null");
|
||||
try {
|
||||
requestControlClass = Class.forName(defaultRequestControl);
|
||||
responseControlClass = Class.forName(defaultResponseControl);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
log.debug("Default control classes not found - falling back to LdapBP classes", e);
|
||||
|
||||
try {
|
||||
requestControlClass = Class.forName(fallbackRequestControl);
|
||||
responseControlClass = Class.forName(fallbackResponseControl);
|
||||
}
|
||||
catch (ClassNotFoundException e1) {
|
||||
throw new UncategorizedLdapException(
|
||||
"Neither default nor fallback classes are available - unable to proceed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public void setRequestControlClass(Class requestControlClass) {
|
||||
this.requestControlClass = requestControlClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for invoking a method on a Control.
|
||||
* @param method name of method to invoke
|
||||
* @param clazz Class of the object that the method should be invoked on
|
||||
* @param control Instance that the method should be invoked on
|
||||
* @return the invocation result, if any
|
||||
*/
|
||||
protected Object invokeMethod(String method, Class clazz, Object control) {
|
||||
Method actualMethod = ReflectionUtils.findMethod(clazz, method);
|
||||
return ReflectionUtils.invokeMethod(actualMethod, control);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request control using the constructor parameters given in
|
||||
* <code>params</code>.
|
||||
* @param paramTypes Types of the constructor parameters
|
||||
* @param params Actual constructor parameters
|
||||
* @return Control to be used by the DirContextProcessor
|
||||
*/
|
||||
public Control createRequestControl(Class[] paramTypes, Object[] params) {
|
||||
Constructor constructor = ClassUtils.getConstructorIfAvailable(requestControlClass, paramTypes);
|
||||
if (constructor == null) {
|
||||
throw new IllegalArgumentException("Failed to find an appropriate RequestControl constructor");
|
||||
}
|
||||
|
||||
Control result = null;
|
||||
try {
|
||||
result = (Control) constructor.newInstance(params);
|
||||
}
|
||||
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 {
|
||||
|
||||
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;
|
||||
handleResponse(control);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log.fatal("No matching response control found for paged results - looking for '" + responseControlClass);
|
||||
}
|
||||
|
||||
protected abstract void handleResponse(Object control);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2005-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.control;
|
||||
|
||||
import javax.naming.ldap.Control;
|
||||
|
||||
/**
|
||||
* DirContextProcessor implementation for managing the paged results control.
|
||||
* Note that due to the internal workings of <code>LdapTemplate</code>, the
|
||||
* target connection is closed after each LDAP call. The PagedResults control
|
||||
* require the same connection be used for each call, which means we need to
|
||||
* make sure the target connection is never actually closed. There's basically
|
||||
* two ways of making this happen: use the <code>SingleContextSource</code>
|
||||
* implementation or make sure all calls happen within a single LDAP transaction
|
||||
* (using <code>ContextSourceTransactionManager</code>).
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @author Ulrik Sandberg
|
||||
*/
|
||||
public class PagedResultsDirContextProcessor extends AbstractFallbackRequestAndResponseControlDirContextProcessor {
|
||||
|
||||
private static final String DEFAULT_REQUEST_CONTROL = "javax.naming.ldap.PagedResultsControl";
|
||||
|
||||
private static final String FALLBACK_REQUEST_CONTROL = "com.sun.jndi.ldap.ctl.PagedResultsControl";
|
||||
|
||||
private static final String DEFAULT_RESPONSE_CONTROL = "javax.naming.ldap.PagedResultsResponseControl";
|
||||
|
||||
private static final String FALLBACK_RESPONSE_CONTROL = "com.sun.jndi.ldap.ctl.PagedResultsResponseControl";
|
||||
|
||||
private int pageSize;
|
||||
|
||||
private PagedResultsCookie cookie;
|
||||
|
||||
private int resultSize;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public PagedResultsDirContextProcessor(int pageSize) {
|
||||
this(pageSize, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance with the supplied page size and cookie. The
|
||||
* cookie must be the exact same instance as received from a previous paged
|
||||
* results 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.
|
||||
*/
|
||||
public PagedResultsDirContextProcessor(int pageSize, PagedResultsCookie cookie) {
|
||||
this.pageSize = pageSize;
|
||||
this.cookie = cookie;
|
||||
|
||||
defaultRequestControl = DEFAULT_REQUEST_CONTROL;
|
||||
defaultResponseControl = DEFAULT_RESPONSE_CONTROL;
|
||||
fallbackRequestControl = FALLBACK_REQUEST_CONTROL;
|
||||
fallbackResponseControl = FALLBACK_RESPONSE_CONTROL;
|
||||
|
||||
loadControlClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cookie.
|
||||
*
|
||||
* @return the cookie.
|
||||
*/
|
||||
public PagedResultsCookie getCookie() {
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the page size.
|
||||
*
|
||||
* @return the page size.
|
||||
*/
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return resultSize;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see
|
||||
* org.springframework.ldap.control.AbstractRequestControlDirContextProcessor
|
||||
* #createRequestControl()
|
||||
*/
|
||||
public Control createRequestControl() {
|
||||
byte[] actualCookie = null;
|
||||
if (cookie != null) {
|
||||
actualCookie = cookie.getCookie();
|
||||
}
|
||||
return super.createRequestControl(new Class[] { int.class, byte[].class, boolean.class },
|
||||
new Object[] {new Integer(pageSize), actualCookie, Boolean.valueOf(critical)});
|
||||
}
|
||||
|
||||
/*
|
||||
* @seeorg.springframework.ldap.control.
|
||||
* AbstractFallbackRequestAndResponseControlDirContextProcessor
|
||||
* #handleResponse(java.lang.Object)
|
||||
*/
|
||||
protected void handleResponse(Object control) {
|
||||
byte[] result = (byte[]) invokeMethod("getCookie", responseControlClass, control);
|
||||
this.cookie = new PagedResultsCookie(result);
|
||||
Integer wrapper = (Integer) invokeMethod("getResultSize", responseControlClass, control);
|
||||
this.resultSize = wrapper.intValue();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user