org.springframework.ldap.core.LdapTemplate.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.ldap.core.LdapTemplate.java

Source

/*
 * Copyright 2005-2010 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.core;

import java.util.List;

import javax.naming.Binding;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.PartialResultException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;

import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.support.LdapUtils;

/**
 * Executes core LDAP functionality and helps to avoid common errors, relieving
 * the user of the burden of looking up contexts, looping through
 * NamingEnumerations and closing contexts.
 * <p>
 * <b>Note for Active Directory (AD) users:</b> AD servers are apparently unable
 * to handle referrals automatically, which causes a
 * <code>PartialResultException</code> to be thrown whenever a referral is
 * encountered in a search. To avoid this, set the
 * <code>ignorePartialResultException</code> property to <code>true</code>.
 * There is currently no way of manually handling these referrals in the form of
 * <code>ReferralException</code>, i.e. either you get the exception (and your
 * results are lost) or all referrals are ignored (if the server is unable to
 * handle them properly. Neither is there any simple way to get notified that a
 * <code>PartialResultException</code> has been ignored (other than in the log).
 * 
 * @see org.springframework.ldap.core.ContextSource
 * 
 * @author Mattias Hellborg Arthursson
 * @author Ulrik Sandberg
 */
public class LdapTemplate implements LdapOperations, InitializingBean {

    private static final Log log = LogFactory.getLog(LdapTemplate.class);

    private static final int DEFAULT_SEARCH_SCOPE = SearchControls.SUBTREE_SCOPE;

    private static final boolean DONT_RETURN_OBJ_FLAG = false;

    private static final boolean RETURN_OBJ_FLAG = true;

    private static final String[] ALL_ATTRIBUTES = null;

    private ContextSource contextSource;

    private boolean ignorePartialResultException = false;

    private boolean ignoreNameNotFoundException = false;

    /**
     * Constructor for bean usage.
     */
    public LdapTemplate() {
    }

    /**
     * Constructor to setup instance directly.
     * 
     * @param contextSource the ContextSource to use.
     */
    public LdapTemplate(ContextSource contextSource) {
        this.contextSource = contextSource;
    }

    /**
     * Set the ContextSource. Call this method when the default constructor has
     * been used.
     * 
     * @param contextSource the ContextSource.
     */
    public void setContextSource(ContextSource contextSource) {
        this.contextSource = contextSource;
    }

    /**
     * Get the ContextSource.
     * 
     * @return the ContextSource.
     */
    public ContextSource getContextSource() {
        return contextSource;
    }

    /**
     * Specify whether <code>NameNotFoundException</code> should be ignored in
     * searches. In previous version, <code>NameNotFoundException</code> caused
     * by the search base not being found was silently ignored. The default
     * behavior is now to treat this as an error (as it should), and to convert
     * and re-throw the exception. The ability to revert to the previous
     * behavior still exists. The only difference is that the incident is in
     * that case no longer silently ignored, but logged as a warning.
     * 
     * @param ignore <code>true</code> if <code>NameNotFoundException</code>
     * should be ignored in searches, <code>false</code> otherwise. Default is
     * <code>false</code>.
     * 
     * @since 1.3
     */
    public void setIgnoreNameNotFoundException(boolean ignore) {
        this.ignoreNameNotFoundException = ignore;
    }

    /**
     * Specify whether <code>PartialResultException</code> should be ignored in
     * searches. AD servers typically have a problem with referrals. Normally a
     * referral should be followed automatically, but this does not seem to work
     * with AD servers. The problem manifests itself with a a
     * <code>PartialResultException</code> being thrown when a referral is
     * encountered by the server. Setting this property to <code>true</code>
     * presents a workaround to this problem by causing
     * <code>PartialResultException</code> to be ignored, so that the search
     * method returns normally. Default value of this parameter is
     * <code>false</code>.
     * 
     * @param ignore <code>true</code> if <code>PartialResultException</code>
     * should be ignored in searches, <code>false</code> otherwise. Default is
     * <code>false</code>.
     */
    public void setIgnorePartialResultException(boolean ignore) {
        this.ignorePartialResultException = ignore;
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, int, boolean,
     * org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void search(Name base, String filter, int searchScope, boolean returningObjFlag,
            NameClassPairCallbackHandler handler) {

        search(base, filter, getDefaultSearchControls(searchScope, returningObjFlag, ALL_ATTRIBUTES), handler);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, int, boolean,
     * org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void search(String base, String filter, int searchScope, boolean returningObjFlag,
            NameClassPairCallbackHandler handler) {

        search(base, filter, getDefaultSearchControls(searchScope, returningObjFlag, ALL_ATTRIBUTES), handler);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void search(final Name base, final String filter, final SearchControls controls,
            NameClassPairCallbackHandler handler) {

        // Create a SearchExecutor to perform the search.
        SearchExecutor se = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.search(base, filter, controls);
            }
        };
        if (handler instanceof ContextMapperCallbackHandler) {
            assureReturnObjFlagSet(controls);
        }
        search(se, handler);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void search(final String base, final String filter, final SearchControls controls,
            NameClassPairCallbackHandler handler) {

        // Create a SearchExecutor to perform the search.
        SearchExecutor se = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.search(base, filter, controls);
            }
        };
        if (handler instanceof ContextMapperCallbackHandler) {
            assureReturnObjFlagSet(controls);
        }
        search(se, handler);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.NameClassPairCallbackHandler,
     * org.springframework.ldap.core.DirContextProcessor)
     */
    public void search(final Name base, final String filter, final SearchControls controls,
            NameClassPairCallbackHandler handler, DirContextProcessor processor) {

        // Create a SearchExecutor to perform the search.
        SearchExecutor se = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.search(base, filter, controls);
            }
        };
        if (handler instanceof ContextMapperCallbackHandler) {
            assureReturnObjFlagSet(controls);
        }
        search(se, handler, processor);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.NameClassPairCallbackHandler,
     * org.springframework.ldap.core.DirContextProcessor)
     */
    public void search(final String base, final String filter, final SearchControls controls,
            NameClassPairCallbackHandler handler, DirContextProcessor processor) {

        // Create a SearchExecutor to perform the search.
        SearchExecutor se = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.search(base, filter, controls);
            }
        };
        if (handler instanceof ContextMapperCallbackHandler) {
            assureReturnObjFlagSet(controls);
        }
        search(se, handler, processor);
    }

    /**
     * Perform a search operation, such as a search(), list() or listBindings().
     * This method handles all the plumbing; getting a readonly context; looping
     * through the NamingEnumeration and closing the context and enumeration. It
     * also calls the supplied DirContextProcessor before and after the search,
     * respectively. This enables custom pre-processing and post-processing,
     * like for example when handling paged results or other search controls.
     * <p>
     * The actual list is delegated to the {@link SearchExecutor} and each
     * {@link NameClassPair} (this might be a NameClassPair or a subclass
     * thereof) is passed to the CallbackHandler. Any encountered
     * NamingException will be translated using the NamingExceptionTranslator.
     * 
     * @param se the SearchExecutor to use for performing the actual list.
     * @param handler the NameClassPairCallbackHandler to which each found entry
     * will be passed.
     * @param processor DirContextProcessor for custom pre- and post-processing.
     * Must not be <code>null</code>. If no custom processing should take place,
     * please use e.g.
     * {@link #search(SearchExecutor, NameClassPairCallbackHandler)}.
     * @throws NamingException if any error occurs. Note that a
     * NameNotFoundException will be ignored. Instead this is interpreted that
     * no entries were found.
     */
    public void search(SearchExecutor se, NameClassPairCallbackHandler handler, DirContextProcessor processor) {
        DirContext ctx = contextSource.getReadOnlyContext();

        NamingEnumeration results = null;
        RuntimeException ex = null;
        try {
            processor.preProcess(ctx);
            results = se.executeSearch(ctx);

            while (results.hasMore()) {
                NameClassPair result = (NameClassPair) results.next();
                handler.handleNameClassPair(result);
            }
        } catch (NameNotFoundException e) {
            // It is possible to ignore errors caused by base not found
            if (ignoreNameNotFoundException) {
                log.warn("Base context not found, ignoring: " + e.getMessage());
            } else {
                ex = LdapUtils.convertLdapException(e);
            }
        } catch (PartialResultException e) {
            // Workaround for AD servers not handling referrals correctly.
            if (ignorePartialResultException) {
                log.debug("PartialResultException encountered and ignored", e);
            } else {
                ex = LdapUtils.convertLdapException(e);
            }
        } catch (javax.naming.NamingException e) {
            ex = LdapUtils.convertLdapException(e);
        } finally {
            try {
                processor.postProcess(ctx);
            } catch (javax.naming.NamingException e) {
                if (ex == null) {
                    ex = LdapUtils.convertLdapException(e);
                } else {
                    // We already had an exception from above and should ignore
                    // this one.
                    log.debug("Ignoring Exception from postProcess, " + "main exception thrown instead", e);
                }
            }
            closeContextAndNamingEnumeration(ctx, results);
            // If we got an exception it should be thrown.
            if (ex != null) {
                throw ex;
            }
        }
    }

    /**
     * Perform a search operation, such as a search(), list() or listBindings().
     * This method handles all the plumbing; getting a readonly context; looping
     * through the NamingEnumeration and closing the context and enumeration.
     * <p>
     * The actual list is delegated to the {@link SearchExecutor} and each
     * {@link NameClassPair} (this might be a NameClassPair or a subclass
     * thereof) is passed to the CallbackHandler. Any encountered
     * NamingException will be translated using the NamingExceptionTranslator.
     * 
     * @param se the SearchExecutor to use for performing the actual list.
     * @param handler the NameClassPairCallbackHandler to which each found entry
     * will be passed.
     * @throws NamingException if any error occurs. Note that a
     * NameNotFoundException will be ignored. Instead this is interpreted that
     * no entries were found.
     */
    public void search(SearchExecutor se, NameClassPairCallbackHandler handler) {
        search(se, handler, new NullDirContextProcessor());
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String,
     * org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void search(Name base, String filter, NameClassPairCallbackHandler handler) {

        SearchControls controls = getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, DONT_RETURN_OBJ_FLAG,
                ALL_ATTRIBUTES);
        if (handler instanceof ContextMapperCallbackHandler) {
            assureReturnObjFlagSet(controls);
        }
        search(base, filter, controls, handler);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String,
     * org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void search(String base, String filter, NameClassPairCallbackHandler handler) {

        SearchControls controls = getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, DONT_RETURN_OBJ_FLAG,
                ALL_ATTRIBUTES);
        if (handler instanceof ContextMapperCallbackHandler) {
            assureReturnObjFlagSet(controls);
        }
        search(base, filter, controls, handler);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, int, java.lang.String[],
     * org.springframework.ldap.core.AttributesMapper)
     */
    public List search(Name base, String filter, int searchScope, String[] attrs, AttributesMapper mapper) {
        return search(base, filter, getDefaultSearchControls(searchScope, DONT_RETURN_OBJ_FLAG, attrs), mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, int, java.lang.String[],
     * org.springframework.ldap.core.AttributesMapper)
     */
    public List search(String base, String filter, int searchScope, String[] attrs, AttributesMapper mapper) {
        return search(base, filter, getDefaultSearchControls(searchScope, DONT_RETURN_OBJ_FLAG, attrs), mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, int, org.springframework.ldap.core.AttributesMapper)
     */
    public List search(Name base, String filter, int searchScope, AttributesMapper mapper) {

        return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, int, org.springframework.ldap.core.AttributesMapper)
     */
    public List search(String base, String filter, int searchScope, AttributesMapper mapper) {

        return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, org.springframework.ldap.core.AttributesMapper)
     */
    public List search(Name base, String filter, AttributesMapper mapper) {

        return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, org.springframework.ldap.core.AttributesMapper)
     */
    public List search(String base, String filter, AttributesMapper mapper) {

        return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, int, java.lang.String[],
     * org.springframework.ldap.core.ContextMapper)
     */
    public List search(Name base, String filter, int searchScope, String[] attrs, ContextMapper mapper) {

        return search(base, filter, getDefaultSearchControls(searchScope, RETURN_OBJ_FLAG, attrs), mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, int, java.lang.String[],
     * org.springframework.ldap.core.ContextMapper)
     */
    public List search(String base, String filter, int searchScope, String[] attrs, ContextMapper mapper) {

        return search(base, filter, getDefaultSearchControls(searchScope, RETURN_OBJ_FLAG, attrs), mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, int, org.springframework.ldap.core.ContextMapper)
     */
    public List search(Name base, String filter, int searchScope, ContextMapper mapper) {

        return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, int, org.springframework.ldap.core.ContextMapper)
     */
    public List search(String base, String filter, int searchScope, ContextMapper mapper) {

        return search(base, filter, searchScope, ALL_ATTRIBUTES, mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, org.springframework.ldap.core.ContextMapper)
     */
    public List search(Name base, String filter, ContextMapper mapper) {

        return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, org.springframework.ldap.core.ContextMapper)
     */
    public List search(String base, String filter, ContextMapper mapper) {

        return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.ContextMapper)
     */
    public List search(String base, String filter, SearchControls controls, ContextMapper mapper) {

        return search(base, filter, controls, mapper, new NullDirContextProcessor());
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.ContextMapper)
     */
    public List search(Name base, String filter, SearchControls controls, ContextMapper mapper) {

        return search(base, filter, controls, mapper, new NullDirContextProcessor());
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.AttributesMapper)
     */
    public List search(Name base, String filter, SearchControls controls, AttributesMapper mapper) {

        return search(base, filter, controls, mapper, new NullDirContextProcessor());
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.AttributesMapper)
     */
    public List search(String base, String filter, SearchControls controls, AttributesMapper mapper) {
        return search(base, filter, controls, mapper, new NullDirContextProcessor());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.AttributesMapper,
     * org.springframework.ldap.core.DirContextProcessor)
     */
    public List search(String base, String filter, SearchControls controls, AttributesMapper mapper,
            DirContextProcessor processor) {
        AttributesMapperCallbackHandler handler = new AttributesMapperCallbackHandler(mapper);
        search(base, filter, controls, handler, processor);

        return handler.getList();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.AttributesMapper,
     * org.springframework.ldap.core.DirContextProcessor)
     */
    public List search(Name base, String filter, SearchControls controls, AttributesMapper mapper,
            DirContextProcessor processor) {
        AttributesMapperCallbackHandler handler = new AttributesMapperCallbackHandler(mapper);
        search(base, filter, controls, handler, processor);

        return handler.getList();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#search(java.lang.String,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.ContextMapper,
     * org.springframework.ldap.core.DirContextProcessor)
     */
    public List search(String base, String filter, SearchControls controls, ContextMapper mapper,
            DirContextProcessor processor) {
        assureReturnObjFlagSet(controls);
        ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler(mapper);
        search(base, filter, controls, handler, processor);

        return handler.getList();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#search(javax.naming.Name,
     * java.lang.String, javax.naming.directory.SearchControls,
     * org.springframework.ldap.core.ContextMapper,
     * org.springframework.ldap.core.DirContextProcessor)
     */
    public List search(Name base, String filter, SearchControls controls, ContextMapper mapper,
            DirContextProcessor processor) {
        assureReturnObjFlagSet(controls);
        ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler(mapper);
        search(base, filter, controls, handler, processor);

        return handler.getList();
    }

    /*
     * @see org.springframework.ldap.core.LdapOperations#list(java.lang.String,
     * org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void list(final String base, NameClassPairCallbackHandler handler) {
        SearchExecutor searchExecutor = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.list(base);
            }
        };

        search(searchExecutor, handler);
    }

    /*
     * @see org.springframework.ldap.core.LdapOperations#list(javax.naming.Name,
     * org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void list(final Name base, NameClassPairCallbackHandler handler) {
        SearchExecutor searchExecutor = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.list(base);
            }
        };

        search(searchExecutor, handler);
    }

    /*
     * @see org.springframework.ldap.core.LdapOperations#list(java.lang.String,
     * org.springframework.ldap.core.NameClassPairMapper)
     */
    public List list(String base, NameClassPairMapper mapper) {
        CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler(mapper);
        list(base, handler);
        return handler.getList();
    }

    /*
     * @see org.springframework.ldap.core.LdapOperations#list(javax.naming.Name,
     * org.springframework.ldap.core.NameClassPairMapper)
     */
    public List list(Name base, NameClassPairMapper mapper) {
        CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler(mapper);
        list(base, handler);
        return handler.getList();
    }

    /*
     * @see org.springframework.ldap.core.LdapOperations#list(javax.naming.Name)
     */
    public List list(final Name base) {
        return list(base, new DefaultNameClassPairMapper());
    }

    /*
     * @see org.springframework.ldap.core.LdapOperations#list(java.lang.String)
     */
    public List list(final String base) {
        return list(base, new DefaultNameClassPairMapper());
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#listBindings(java.lang.String
     * , org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void listBindings(final String base, NameClassPairCallbackHandler handler) {
        SearchExecutor searchExecutor = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.listBindings(base);
            }
        };

        search(searchExecutor, handler);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#listBindings(javax.naming
     * .Name, org.springframework.ldap.core.NameClassPairCallbackHandler)
     */
    public void listBindings(final Name base, NameClassPairCallbackHandler handler) {
        SearchExecutor searchExecutor = new SearchExecutor() {
            public NamingEnumeration executeSearch(DirContext ctx) throws javax.naming.NamingException {
                return ctx.listBindings(base);
            }
        };

        search(searchExecutor, handler);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#listBindings(java.lang.String
     * , org.springframework.ldap.core.NameClassPairMapper)
     */
    public List listBindings(String base, NameClassPairMapper mapper) {
        CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler(mapper);
        listBindings(base, handler);
        return handler.getList();
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#listBindings(javax.naming
     * .Name, org.springframework.ldap.core.NameClassPairMapper)
     */
    public List listBindings(Name base, NameClassPairMapper mapper) {
        CollectingNameClassPairCallbackHandler handler = new MappingCollectingNameClassPairCallbackHandler(mapper);
        listBindings(base, handler);
        return handler.getList();
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#listBindings(java.lang.String
     * )
     */
    public List listBindings(final String base) {
        return listBindings(base, new DefaultNameClassPairMapper());
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#listBindings(javax.naming
     * .Name)
     */
    public List listBindings(final Name base) {
        return listBindings(base, new DefaultNameClassPairMapper());
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#listBindings(java.lang.String
     * , org.springframework.ldap.core.ContextMapper)
     */
    public List listBindings(String base, ContextMapper mapper) {

        ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler(mapper);
        listBindings(base, handler);

        return handler.getList();
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#listBindings(javax.naming
     * .Name, org.springframework.ldap.core.ContextMapper)
     */
    public List listBindings(Name base, ContextMapper mapper) {

        ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler(mapper);
        listBindings(base, handler);

        return handler.getList();
    }

    /*
     * @seeorg.springframework.ldap.core.LdapOperations#executeReadOnly(org.
     * springframework.ldap.core.DirContextProcessor)
     */
    public Object executeReadOnly(ContextExecutor ce) {
        DirContext ctx = contextSource.getReadOnlyContext();
        return executeWithContext(ce, ctx);
    }

    /*
     * @seeorg.springframework.ldap.core.LdapOperations#executeReadWrite(org.
     * springframework.ldap.core.DirContextProcessor)
     */
    public Object executeReadWrite(ContextExecutor ce) {
        DirContext ctx = contextSource.getReadWriteContext();
        return executeWithContext(ce, ctx);
    }

    private Object executeWithContext(ContextExecutor ce, DirContext ctx) {
        try {
            return ce.executeWithContext(ctx);
        } catch (javax.naming.NamingException e) {
            throw LdapUtils.convertLdapException(e);
        } finally {
            closeContext(ctx);
        }
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name)
     */
    public Object lookup(final Name dn) {
        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                return ctx.lookup(dn);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(java.lang.String)
     */
    public Object lookup(final String dn) {
        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                return ctx.lookup(dn);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name,
     * org.springframework.ldap.core.AttributesMapper)
     */
    public Object lookup(final Name dn, final AttributesMapper mapper) {
        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                Attributes attributes = ctx.getAttributes(dn);
                return mapper.mapFromAttributes(attributes);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(java.lang.String,
     * org.springframework.ldap.core.AttributesMapper)
     */
    public Object lookup(final String dn, final AttributesMapper mapper) {

        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                Attributes attributes = ctx.getAttributes(dn);
                return mapper.mapFromAttributes(attributes);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name,
     * org.springframework.ldap.core.ContextMapper)
     */
    public Object lookup(final Name dn, final ContextMapper mapper) {
        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                Object object = ctx.lookup(dn);
                return mapper.mapFromContext(object);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(java.lang.String,
     * org.springframework.ldap.core.ContextMapper)
     */
    public Object lookup(final String dn, final ContextMapper mapper) {

        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                Object object = ctx.lookup(dn);
                return mapper.mapFromContext(object);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name,
     * java.lang.String[], org.springframework.ldap.core.AttributesMapper)
     */
    public Object lookup(final Name dn, final String[] attributes, final AttributesMapper mapper) {

        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                Attributes filteredAttributes = ctx.getAttributes(dn, attributes);
                return mapper.mapFromAttributes(filteredAttributes);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(java.lang.String,
     * java.lang.String[], org.springframework.ldap.core.AttributesMapper)
     */
    public Object lookup(final String dn, final String[] attributes, final AttributesMapper mapper) {
        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                Attributes filteredAttributes = ctx.getAttributes(dn, attributes);
                return mapper.mapFromAttributes(filteredAttributes);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(javax.naming.Name,
     * java.lang.String[], org.springframework.ldap.core.ContextMapper)
     */
    public Object lookup(final Name dn, final String[] attributes, final ContextMapper mapper) {

        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                Attributes filteredAttributes = ctx.getAttributes(dn, attributes);
                DirContextAdapter contextAdapter = new DirContextAdapter(filteredAttributes, dn);
                return mapper.mapFromContext(contextAdapter);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#lookup(java.lang.String,
     * java.lang.String[], org.springframework.ldap.core.ContextMapper)
     */
    public Object lookup(final String dn, final String[] attributes, final ContextMapper mapper) {

        return executeReadOnly(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                Attributes filteredAttributes = ctx.getAttributes(dn, attributes);
                DistinguishedName name = new DistinguishedName(dn);
                DirContextAdapter contextAdapter = new DirContextAdapter(filteredAttributes, name);
                return mapper.mapFromContext(contextAdapter);
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#modifyAttributes(javax.naming
     * .Name, javax.naming.directory.ModificationItem[])
     */
    public void modifyAttributes(final Name dn, final ModificationItem[] mods) {
        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.modifyAttributes(dn, mods);
                return null;
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#modifyAttributes(java.lang
     * .String, javax.naming.directory.ModificationItem[])
     */
    public void modifyAttributes(final String dn, final ModificationItem[] mods) {

        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.modifyAttributes(dn, mods);
                return null;
            }
        });
    }

    /*
     * @see org.springframework.ldap.core.LdapOperations#bind(javax.naming.Name,
     * java.lang.Object, javax.naming.directory.Attributes)
     */
    public void bind(final Name dn, final Object obj, final Attributes attributes) {

        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.bind(dn, obj, attributes);
                return null;
            }
        });
    }

    /*
     * @see org.springframework.ldap.core.LdapOperations#bind(java.lang.String,
     * java.lang.Object, javax.naming.directory.Attributes)
     */
    public void bind(final String dn, final Object obj, final Attributes attributes) {

        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.bind(dn, obj, attributes);
                return null;
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#unbind(javax.naming.Name)
     */
    public void unbind(final Name dn) {
        doUnbind(dn);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#unbind(java.lang.String)
     */
    public void unbind(final String dn) {
        doUnbind(dn);
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#unbind(javax.naming.Name,
     * boolean)
     */
    public void unbind(final Name dn, boolean recursive) {
        if (!recursive) {
            doUnbind(dn);
        } else {
            doUnbindRecursively(dn);
        }
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#unbind(java.lang.String,
     * boolean)
     */
    public void unbind(final String dn, boolean recursive) {
        if (!recursive) {
            doUnbind(dn);
        } else {
            doUnbindRecursively(dn);
        }
    }

    private void doUnbind(final Name dn) {
        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.unbind(dn);
                return null;
            }
        });
    }

    private void doUnbind(final String dn) {
        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.unbind(dn);
                return null;
            }
        });
    }

    private void doUnbindRecursively(final Name dn) {
        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) {
                deleteRecursively(ctx, new DistinguishedName(dn));
                return null;
            }
        });
    }

    private void doUnbindRecursively(final String dn) {
        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                deleteRecursively(ctx, new DistinguishedName(dn));
                return null;
            }
        });
    }

    /**
     * Delete all subcontexts including the current one recursively.
     * 
     * @param ctx The context to use for deleting.
     * @param name The starting point to delete recursively.
     * @throws NamingException if any error occurs
     */
    protected void deleteRecursively(DirContext ctx, DistinguishedName name) {

        NamingEnumeration enumeration = null;
        try {
            enumeration = ctx.listBindings(name);
            while (enumeration.hasMore()) {
                Binding binding = (Binding) enumeration.next();
                DistinguishedName childName = new DistinguishedName(binding.getName());
                childName.prepend((DistinguishedName) name);
                deleteRecursively(ctx, childName);
            }
            ctx.unbind(name);
            if (log.isDebugEnabled()) {
                log.debug("Entry " + name + " deleted");
            }
        } catch (javax.naming.NamingException e) {
            throw LdapUtils.convertLdapException(e);
        } finally {
            try {
                enumeration.close();
            } catch (Exception e) {
                // Never mind this
            }
        }
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#rebind(javax.naming.Name,
     * java.lang.Object, javax.naming.directory.Attributes)
     */
    public void rebind(final Name dn, final Object obj, final Attributes attributes) {

        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.rebind(dn, obj, attributes);
                return null;
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#rebind(java.lang.String,
     * java.lang.Object, javax.naming.directory.Attributes)
     */
    public void rebind(final String dn, final Object obj, final Attributes attributes) {

        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.rebind(dn, obj, attributes);
                return null;
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#rename(javax.naming.Name,
     * javax.naming.Name)
     */
    public void rename(final Name oldDn, final Name newDn) {

        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.rename(oldDn, newDn);
                return null;
            }
        });
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#rename(java.lang.String,
     * java.lang.String)
     */
    public void rename(final String oldDn, final String newDn) {

        executeReadWrite(new ContextExecutor() {
            public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                ctx.rename(oldDn, newDn);
                return null;
            }
        });
    }

    /*
     * @see
     * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        if (contextSource == null) {
            throw new IllegalArgumentException("Property 'contextSource' must be set.");
        }
    }

    private void closeContextAndNamingEnumeration(DirContext ctx, NamingEnumeration results) {

        closeNamingEnumeration(results);
        closeContext(ctx);
    }

    /**
     * Close the supplied DirContext if it is not null. Swallow any exceptions,
     * as this is only for cleanup.
     * 
     * @param ctx the context to close.
     */
    private void closeContext(DirContext ctx) {
        if (ctx != null) {
            try {
                ctx.close();
            } catch (Exception e) {
                // Never mind this.
            }
        }
    }

    /**
     * Close the supplied NamingEnumeration if it is not null. Swallow any
     * exceptions, as this is only for cleanup.
     * 
     * @param results the NamingEnumeration to close.
     */
    private void closeNamingEnumeration(NamingEnumeration results) {
        if (results != null) {
            try {
                results.close();
            } catch (Exception e) {
                // Never mind this.
            }
        }
    }

    private SearchControls getDefaultSearchControls(int searchScope, boolean returningObjFlag, String[] attrs) {

        SearchControls controls = new SearchControls();
        controls.setSearchScope(searchScope);
        controls.setReturningObjFlag(returningObjFlag);
        controls.setReturningAttributes(attrs);
        return controls;
    }

    /**
     * Make sure the returnObjFlag is set in the supplied SearchControls. Set it
     * and log if it's not set.
     * 
     * @param controls the SearchControls to check.
     */
    private void assureReturnObjFlagSet(SearchControls controls) {
        Validate.notNull(controls);
        if (!controls.getReturningObjFlag()) {
            log.info("The returnObjFlag of supplied SearchControls is not set"
                    + " but a ContextMapper is used - setting flag to true");
            controls.setReturningObjFlag(true);
        }
    }

    private final static class NullDirContextProcessor implements DirContextProcessor {
        public void postProcess(DirContext ctx) throws NamingException {
            // Do nothing
        }

        public void preProcess(DirContext ctx) throws NamingException {
            // Do nothing
        }
    }

    /**
     * A {@link NameClassPairCallbackHandler} that passes the NameClassPairs
     * found to a NameClassPairMapper and collects the results in a list.
     * 
     * @author Mattias Hellborg Arthursson
     */
    public final static class MappingCollectingNameClassPairCallbackHandler
            extends CollectingNameClassPairCallbackHandler {

        private NameClassPairMapper mapper;

        public MappingCollectingNameClassPairCallbackHandler(NameClassPairMapper mapper) {
            this.mapper = mapper;
        }

        /*
         * @seeorg.springframework.ldap.CollectingNameClassPairCallbackHandler#
         * getObjectFromNameClassPair(javax.naming.NameClassPair)
         */
        public Object getObjectFromNameClassPair(NameClassPair nameClassPair) {
            try {
                return mapper.mapFromNameClassPair(nameClassPair);
            } catch (javax.naming.NamingException e) {
                throw LdapUtils.convertLdapException(e);
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#lookupContext(javax.naming
     * .Name)
     */
    public DirContextOperations lookupContext(Name dn) {
        return (DirContextOperations) lookup(dn);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#lookupContext(java.lang.
     * String)
     */
    public DirContextOperations lookupContext(String dn) {
        return (DirContextOperations) lookup(dn);
    }

    /*
     * (non-Javadoc)
     * 
     * @seeorg.springframework.ldap.core.LdapOperations#modifyAttributes(org.
     * springframework.ldap.core.DirContextOperations)
     */
    public void modifyAttributes(DirContextOperations ctx) {
        Name dn = ctx.getDn();
        if (dn != null && ctx.isUpdateMode()) {
            modifyAttributes(dn, ctx.getModificationItems());
        } else {
            throw new IllegalStateException("The DirContextOperations instance needs to be properly initialized.");
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @seeorg.springframework.ldap.core.LdapOperations#bind(org.
     * springframework.ldap.core.DirContextOperations)
     */
    public void bind(DirContextOperations ctx) {
        Name dn = ctx.getDn();
        if (dn != null && !ctx.isUpdateMode()) {
            bind(dn, ctx, null);
        } else {
            throw new IllegalStateException("The DirContextOperations instance needs to be properly initialized.");
        }
    }

    /*
     * @see
     * org.springframework.ldap.core.LdapOperations#rebind(org.springframework.
     * ldap.core.DirContextOperations)
     */
    public void rebind(DirContextOperations ctx) {
        Name dn = ctx.getDn();
        if (dn != null && !ctx.isUpdateMode()) {
            rebind(dn, ctx, null);
        } else {
            throw new IllegalStateException("The DirContextOperations instance needs to be properly initialized.");
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#authenticate(javax.naming
     * .Name, java.lang.String, java.lang.String)
     */
    public boolean authenticate(Name base, String filter, String password) {
        return authenticate(base, filter, password, new NullAuthenticatedLdapEntryContextCallback(),
                new NullAuthenticationErrorCallback());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#authenticate(java.lang.String
     * , java.lang.String, java.lang.String)
     */
    public boolean authenticate(String base, String filter, String password) {
        return authenticate(new DistinguishedName(base), filter, password,
                new NullAuthenticatedLdapEntryContextCallback(), new NullAuthenticationErrorCallback());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#authenticate(java.lang.String
     * , java.lang.String, java.lang.String,
     * org.springframework.ldap.core.AuthenticatedLdapEntryContextCallback)
     */
    public boolean authenticate(String base, String filter, String password,
            AuthenticatedLdapEntryContextCallback callback) {
        return authenticate(new DistinguishedName(base), filter, password, callback,
                new NullAuthenticationErrorCallback());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#authenticate(javax.naming
     * .Name, java.lang.String, java.lang.String,
     * org.springframework.ldap.core.AuthenticatedLdapEntryContextCallback)
     */
    public boolean authenticate(Name base, String filter, String password,
            final AuthenticatedLdapEntryContextCallback callback) {
        return authenticate(base, filter, password, callback, new NullAuthenticationErrorCallback());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#authenticate(java.lang.String
     * , java.lang.String, java.lang.String,
     * org.springframework.ldap.core.AuthenticationErrorCallback)
     */
    public boolean authenticate(String base, String filter, String password,
            AuthenticationErrorCallback errorCallback) {
        return authenticate(new DistinguishedName(base), filter, password,
                new NullAuthenticatedLdapEntryContextCallback(), errorCallback);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#authenticate(javax.naming
     * .Name, java.lang.String, java.lang.String,
     * org.springframework.ldap.core.AuthenticationErrorCallback)
     */
    public boolean authenticate(Name base, String filter, String password,
            final AuthenticationErrorCallback errorCallback) {
        return authenticate(base, filter, password, new NullAuthenticatedLdapEntryContextCallback(), errorCallback);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#authenticate(java.lang.String
     * , java.lang.String, java.lang.String,
     * org.springframework.ldap.core.AuthenticatedLdapEntryContextCallback,
     * org.springframework.ldap.core.AuthenticationErrorCallback)
     */
    public boolean authenticate(String base, String filter, String password,
            final AuthenticatedLdapEntryContextCallback callback, final AuthenticationErrorCallback errorCallback) {
        return authenticate(new DistinguishedName(base), filter, password, callback, errorCallback);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#authenticate(javax.naming
     * .Name, java.lang.String, java.lang.String,
     * org.springframework.ldap.core.AuthenticatedLdapEntryContextCallback,
     * org.springframework.ldap.core.AuthenticationErrorCallback)
     */
    public boolean authenticate(Name base, String filter, String password,
            final AuthenticatedLdapEntryContextCallback callback, final AuthenticationErrorCallback errorCallback) {

        List result = search(base, filter, new LdapEntryIdentificationContextMapper());
        if (result.size() == 0) {
            String msg = "No results found for search, base: '" + base + "'; filter: '" + filter + "'.";
            log.info(msg);
            return false;
        } else if (result.size() > 1) {
            String msg = "base: '" + base + "'; filter: '" + filter + "'.";
            throw new IncorrectResultSizeDataAccessException(msg, 1, result.size());
        }

        final LdapEntryIdentification entryIdentification = (LdapEntryIdentification) result.get(0);

        try {
            DirContext ctx = contextSource.getContext(entryIdentification.getAbsoluteDn().toString(), password);
            executeWithContext(new ContextExecutor() {
                public Object executeWithContext(DirContext ctx) throws javax.naming.NamingException {
                    callback.executeWithContext(ctx, entryIdentification);
                    return null;
                }
            }, ctx);
            return true;
        } catch (Exception e) {
            log.info("Authentication failed for entry with DN '" + entryIdentification.getAbsoluteDn() + "'", e);
            errorCallback.execute(e);
            return false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#searchForObject(javax.naming
     * .Name, java.lang.String, org.springframework.ldap.core.ContextMapper)
     */
    public Object searchForObject(Name base, String filter, ContextMapper mapper) {
        List result = search(base, filter, mapper);
        if (result.size() == 0) {
            throw new EmptyResultDataAccessException(1);
        } else if (result.size() != 1) {
            throw new IncorrectResultSizeDataAccessException(1, result.size());
        }

        return result.get(0);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.ldap.core.LdapOperations#searchForObject(java.lang
     * .String, java.lang.String, org.springframework.ldap.core.ContextMapper)
     */
    public Object searchForObject(String base, String filter, ContextMapper mapper) {
        return searchForObject(new DistinguishedName(base), filter, mapper);
    }

    private static final class NullAuthenticatedLdapEntryContextCallback
            implements AuthenticatedLdapEntryContextCallback {
        public void executeWithContext(DirContext ctx, LdapEntryIdentification ldapEntryIdentification) {
            // Do nothing
        }
    }

    private static final class NullAuthenticationErrorCallback implements AuthenticationErrorCallback {
        public void execute(Exception e) {
            // Do nothing
        }
    }
}