处理 DirContext

本节介绍如何处理 DirContext,包括前置处理和后置处理。

自定义 DirContext 前置处理和后置处理

在某些情况下,您可能希望在搜索操作之前和之后对 DirContext 执行操作。用于此目的的接口称为 DirContextProcessor。以下列出了 DirContextProcessor 接口

public interface DirContextProcessor {
   public void preProcess(DirContext ctx) throws NamingException;
   public void postProcess(DirContext ctx) throws NamingException;
}

LdapTemplate 类有一个接受 DirContextProcessor 的搜索方法,如下所示

public void search(SearchExecutor se, NameClassPairCallbackHandler handler,
   DirContextProcessor processor) throws DataAccessException;

在搜索操作之前,会调用给定的 DirContextProcessor 实例上的 preProcess 方法。在搜索运行完毕并且处理完结果 NamingEnumeration 后,会调用 postProcess 方法。这允许您对用于搜索的 DirContext 执行操作,并在执行搜索后检查 DirContext。这非常有用(例如,在处理请求和响应控件时)。

当您不需要自定义 SearchExecutor 时,还可以使用以下便利方法

public void search(Name base, String filter,
   SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor)

public void search(String base, String filter,
   SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor)

public void search(Name base, String filter,
   SearchControls controls, AttributesMapper mapper, DirContextProcessor processor)

public void search(String base, String filter,
   SearchControls controls, AttributesMapper mapper, DirContextProcessor processor)

public void search(Name base, String filter,
   SearchControls controls, ContextMapper mapper, DirContextProcessor processor)

public void search(String base, String filter,
   SearchControls controls, ContextMapper mapper, DirContextProcessor processor)

实现请求控件 DirContextProcessor

LDAPv3 协议使用“控件”(Controls)来发送和接收附加数据,以影响预定义操作的行为。为了简化请求控件 DirContextProcessor 的实现,Spring LDAP 提供了 AbstractRequestControlDirContextProcessor 基类。此类负责从 LdapContext 中检索当前的请求控件,调用模板方法创建请求控件,并将其添加到 LdapContext 中。您在子类中需要做的就是实现名为 createRequestControl 的模板方法以及在搜索后执行所需操作的 postProcess 方法。以下列出了相关的签名

public abstract class AbstractRequestControlDirContextProcessor implements
      DirContextProcessor {

   public void preProcess(DirContext ctx) throws NamingException {
      ...
   }

   public abstract Control createRequestControl();
}

典型的 DirContextProcessor 类似于以下示例

示例 1. 请求控件 DirContextProcessor 实现
public class MyCoolRequestControl extends AbstractRequestControlDirContextProcessor {
   private static final boolean CRITICAL_CONTROL = true;
   private MyCoolCookie cookie;
   ...
   public MyCoolCookie getCookie() {
      return cookie;
   }

   public Control createRequestControl() {
      return new SomeCoolControl(cookie.getCookie(), CRITICAL_CONTROL);
   }

   public void postProcess(DirContext ctx) throws NamingException {
      LdapContext ldapContext = (LdapContext) ctx;
      Control[] responseControls = ldapContext.getResponseControls();

      for (int i = 0; i < responseControls.length; i++) {
         if (responseControls[i] instanceof SomeCoolResponseControl) {
            SomeCoolResponseControl control = (SomeCoolResponseControl) responseControls[i];
            this.cookie = new MyCoolCookie(control.getCookie());
         }
      }
   }
}
使用控件时,请确保使用 LdapContextSourceControl 接口是 LDAPv3 特有的,要求使用 LdapContext 而不是 DirContext。如果调用 `AbstractRequestControlDirContextProcessor` 子类时传递的参数不是 LdapContext,它将抛出 IllegalArgumentException

分页搜索结果

某些搜索可能返回大量结果。当没有简单的方法过滤出少量结果时,让服务器每次调用时只返回一定数量的结果会很方便。这被称为“分页搜索结果”(paged search results)。然后可以显示结果的每一“页”,并带有指向下一页和上一页的链接。如果没有此功能,客户端必须手动将搜索结果限制为页面,或者检索全部结果,然后将其分割成适当大小的页面。前者会相当复杂,而后者会消耗不必要的内存。

一些 LDAP 服务器支持 PagedResultsControl,它要求 LDAP 服务器按指定大小的页返回搜索操作的结果。用户通过控制搜索调用的速率来控制页面的返回速率。但是,您必须在调用之间跟踪一个 cookie。服务器使用此 cookie 来跟踪上次处理分页结果请求时停止的位置。

Spring LDAP 通过使用前述章节讨论的对 LdapContext 进行前置和后置处理的概念来提供对分页结果的支持。它通过使用 PagedResultsDirContextProcessor 类来实现这一点。PagedResultsDirContextProcessor 类创建一个带有请求页大小的 PagedResultsControl 并将其添加到 LdapContext 中。搜索后,它获取 PagedResultsResponseControl 并检索分页结果 cookie,这对于在连续的分页结果请求之间保持上下文是必需的。

以下示例展示了如何使用分页搜索结果功能

示例 2. 使用 PagedResultsDirContextProcessor 实现分页结果
public List<String> getAllPersonNames() {
  final SearchControls searchControls = new SearchControls();
  searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

  final PagedResultsDirContextProcessor processor =
        new PagedResultsDirContextProcessor(PAGE_SIZE);

  return SingleContextSource.doWithSingleContext(
        contextSource, new LdapOperationsCallback<List<String>>() {

      @Override
      public List<String> doWithLdapOperations(LdapOperations operations) {
        List<String> result = new LinkedList<String>();

        do {
          List<String> oneResult = operations.search(
            "ou=People",
            "(&(objectclass=person))",
            searchControls,
            CN_ATTRIBUTES_MAPPER,
            processor);
          result.addAll(oneResult);
        } while(processor.hasMore());

        return result;
      }
  });
}
为了使分页结果 cookie 保持有效,每次分页结果调用都必须使用相同的底层连接。如前面的示例所示,您可以使用 SingleContextSource 来实现这一点。