Hi everyone,
Recently I was trying to get paged search results working properly. As it turned out, I had to make 2 changes in the standard Spring LDAP source. The purpose of this post is to discuss the issue I faced and the way I fixed it. I must say I'm not an LDAP expert, so I hope to get some feedback about the solution. Perhaps there're some consequences I didn't notice. I'm using Spring LDAP 1.3.0.RELEASE.
This what the code looks like to retrieve all results.
Unfortunately this didn't work. Every search resulted inCode:private <T> List<T> pagedSearch(Name base, Filter filter, ParameterizedContextMapper<T> mapper) { List<T> entries = new ArrayList<T>(); String encodedFilter = filter.encode(); PagedResultsCookie cookie = null; do { PagedResult<T> pr = search(base, encodedFilter, mapper, cookie); entries.addAll(pr.getResultList()); cookie = pr.getCookie(); } while (cookie.getCookie() != null); return entries; } private <T> PagedResult<T> search(Name base, String filter, ParameterizedContextMapper<T> mapper, PagedResultsCookie cookie) { PagedResultsDirContextProcessor processor = new PagedResultsDirContextProcessor(PAGE_SIZE, cookie); List<T> entries = ldapTemplate.search(base, filter, SEARCH_CONTROLS, mapper, processor); return new PagedResult<T>(entries, processor.getCookie()); }
FATAL - No matching response control found for paged results - looking for 'class javax.naming.ldap.PagedResultsResponseControl
(and an additional NPE because the PagedResultsCookie cookie object was always null in the while loop.)
This FATAL error is defined in the postProcess method of AbstractFallbackRequestAndResponseControlDirContex tProcessor. As it turned out, ldapContext.getResponseControls() was always null. Then I figured out that in the preProcess method of AbstractRequestControlDirContextProcessor ldapContext.getRequestControls() initially always contained one request control: an instance of com.sun.jndi.ldap.ManageReferralControl. This in turn pointed me to the Context Request Controls chapter in Sun's JNDI tutorial. When I tried something similar for paged search, it worked fine. I noticed one major diffference: they basically use ldapContext.setResponseControls(new Control[] {new PagedResultsControl(...)}); and don't worry about about adding a new request control to the existing array.
That was the first fix I did:
AbstractRequestControlDirContextProcessor/preProcess
The 1st trace statement above prints 1 control: com.sun.jndi.ldap.ManageReferralControl.Code:public void preProcess(DirContext ctx) throws NamingException { LdapContext ldapContext; if (ctx instanceof LdapContext) { ldapContext = (LdapContext) ctx; } else { throw new IllegalArgumentException( "Request Control operations require LDAPv3 - " + "Context must be of type LdapContext"); } //traceControls(ldapContext.getRequestControls()); ldapContext.setRequestControls(new Control[] {createRequestControl()}); //traceControls(ldapContext.getRequestControls()); } // this is the entire method
The 2nd trace statement above prints 2 controls: javax.naming.ldap.PagedResultsControl + com.sun.jndi.ldap.ManageReferralControl
Then I faced another issue because I was using connection pooling (with org.springframework.ldap.pool.factory.MutablePooli ngContextSource). Apparently the contexts in the pool ended up in an "inconsistent" state and I received errors like
WARN - DirContext 'javax.naming.ldap.InitialLdapContext@e0cc23' failed validation with an exception.
javax.naming.OperationNotSupportedException: [LDAP: error code 53 - Base DSE differs from first page request]; remaining name ''
at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.jav a:3114)
and
FATAL - No matching response control found for paged results - looking for 'class javax.naming.ldap.PagedResultsResponseControl
ERROR - [LDAP: error code 53 - Invalid query ref]; nested exception is javax.naming.OperationNotSupportedException: [LDAP: error code 53 - Invalid query ref]; remaining name '<some DN>'
org.springframework.ldap.OperationNotSupportedExce ption: [LDAP: error code 53 - Invalid query ref]; nested exception is javax.naming.OperationNotSupportedException: [LDAP: error code 53 - Invalid query ref]; remaining name '<some DN>'
at org.springframework.ldap.support.LdapUtils.convert LdapException(LdapUtils.java:199)
...
Caused by: javax.naming.OperationNotSupportedException: [LDAP: error code 53 - Invalid query ref]; remaining name '<some DN>'
at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.jav a:3114)
And this is the second fix I did:
AbstractFallbackRequestAndResponseControlDirContex tProcessor/postProcess
The above changes allowed me to retrieve a large number of results using paged search and with connection pooling enabled. I didn't had to use a SingleContextSource or use LDAP transactions. Although I must say I never tested doing paged searches with different threads. So I can imagine LDAP transactions might be required after all. Or maybe it depends on the LDAP server that doesn't has the limitation of reusing the exact same connection between different search requests (we use a Siemens DirX LDAP server).Code:public void postProcess(DirContext ctx) throws NamingException { LdapContext ldapContext = (LdapContext) ctx; ldapContext.setRequestControls(null); // added this line Control[] responseControls = ldapContext.getResponseControls(); ... etc. ... }
Kind regards,
Sigiswald


Reply With Quote