All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.net.sf.ldaptemplate.LdapTemplate Maven / Gradle / Ivy

Go to download

Java framework to simplify LDAP operations, based on the pattern of Spring's JdbcTemplate. Relieves the users of the burden of looking up and closing contexts, looping through NamingEnumerations, encoding/decoding values and filters, and more.

There is a newer version: 1.0.2
Show newest version
/*
 * Copyright 2002-2005 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 net.sf.ldaptemplate;

import java.util.List;

import javax.naming.Name;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
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 javax.naming.directory.SearchResult;

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.DataAccessException;

/**
 * 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.
 * 

* Note for Active Directory (AD) users: AD servers are apparently * unable to handle referrals automatically, which causes a * PartialResultException to be thrown whenever a referral is * encountered in a search. To avoid this, set the * ignorePartialResultException property to true. * There is currently no way of manually handling these referrals in the form of * ReferralException, 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 PartialResultException has been ignored (other than in the * log). * * @see net.sf.ldaptemplate.ContextSource * * @author Mattias 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 ContextSource contextSource; private NamingExceptionTranslator exceptionTranslator = new DefaultNamingExceptionTranslator(); private boolean ignorePartialResultException = 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; } /** * Specify whether PartialResultException 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 * PartialResultException being thrown when a referral is * encountered by the server. Setting this property to true * presents a workaround to this problem by causing * PartialResultException to be ignored, so that the search * method returns normally. Default value of this parameter is * false. * * @param ignore * true if PartialResultException * should be ignored in searches, false otherwise. * Default is false. */ public void setIgnorePartialResultException(boolean ignore) { this.ignorePartialResultException = ignore; } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, int, boolean, * net.sf.ldaptemplate.SearchResultCallbackHandler) */ public void search(Name base, String filter, int searchScope, boolean returningObjFlag, SearchResultCallbackHandler handler) { search(base, filter, getDefaultSearchControls(searchScope, returningObjFlag), handler); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, int, boolean, * net.sf.ldaptemplate.SearchResultCallbackHandler) */ public void search(String base, String filter, int searchScope, boolean returningObjFlag, SearchResultCallbackHandler handler) throws DataAccessException { search(base, filter, getDefaultSearchControls(searchScope, returningObjFlag), handler); } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, javax.naming.directory.SearchControls, * net.sf.ldaptemplate.SearchResultCallbackHandler) */ public void search(final Name base, final String filter, final SearchControls controls, SearchResultCallbackHandler handler) { // Create a SearchExecutor to perform the search. SearchExecutor se = new SearchExecutor() { public NamingEnumeration executeSearch(DirContext ctx) throws NamingException { return ctx.search(base, filter, controls); } }; search(se, handler); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, javax.naming.directory.SearchControls, * net.sf.ldaptemplate.SearchResultCallbackHandler) */ public void search(final String base, final String filter, final SearchControls controls, SearchResultCallbackHandler handler) { // Create a SearchExecutor to perform the search. SearchExecutor se = new SearchExecutor() { public NamingEnumeration executeSearch(DirContext ctx) throws NamingException { return ctx.search(base, filter, controls); } }; search(se, handler); } /* * @see net.sf.ldaptemplate.LdapOperations#doSearch(net.sf.ldaptemplate.LdapTemplate.SearchExecutor, * net.sf.ldaptemplate.SearchResultCallbackHandler) */ public void search(SearchExecutor se, SearchResultCallbackHandler handler) { DirContext ctx = contextSource.getReadOnlyContext(); NamingEnumeration results = null; try { results = se.executeSearch(ctx); while (results.hasMore()) { SearchResult searchResult = (SearchResult) results.next(); handler.handleSearchResult(searchResult); } } catch (NameNotFoundException e) { // The base context was not found, which basically means // that the search did not return any results. Just clean up and // exit. } catch (PartialResultException e) { // Workaround for AD servers not handling referrals correctly. if (ignorePartialResultException) { log.debug("PartialResultException encountered and ignored", e); } else { throw getExceptionTranslator().translate(e); } } catch (NamingException e) { throw getExceptionTranslator().translate(e); } finally { closeContextAndNamingEnumeration(ctx, results); } } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, net.sf.ldaptemplate.SearchResultCallbackHandler) */ public void search(Name base, String filter, SearchResultCallbackHandler handler) throws DataAccessException { search(base, filter, getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, DONT_RETURN_OBJ_FLAG), handler); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, net.sf.ldaptemplate.SearchResultCallbackHandler) */ public void search(String base, String filter, SearchResultCallbackHandler handler) throws DataAccessException { search(base, filter, getDefaultSearchControls(DEFAULT_SEARCH_SCOPE, DONT_RETURN_OBJ_FLAG), handler); } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, int, net.sf.ldaptemplate.AttributesMapper) */ public List search(Name base, String filter, int searchScope, AttributesMapper mapper) { return search(base, filter, getDefaultSearchControls(searchScope, DONT_RETURN_OBJ_FLAG), mapper); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, int, net.sf.ldaptemplate.AttributesMapper) */ public List search(String base, String filter, int searchScope, AttributesMapper mapper) throws DataAccessException { return search(base, filter, getDefaultSearchControls(searchScope, DONT_RETURN_OBJ_FLAG), mapper); } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, net.sf.ldaptemplate.AttributesMapper) */ public List search(Name base, String filter, AttributesMapper mapper) throws DataAccessException { return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, net.sf.ldaptemplate.AttributesMapper) */ public List search(String base, String filter, AttributesMapper mapper) throws DataAccessException { return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, int, net.sf.ldaptemplate.ContextMapper) */ public List search(Name base, String filter, int searchScope, ContextMapper mapper) { return search(base, filter, getDefaultSearchControls(searchScope, RETURN_OBJ_FLAG), mapper); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, int, net.sf.ldaptemplate.ContextMapper) */ public List search(String base, String filter, int searchScope, ContextMapper mapper) throws DataAccessException { return search(base, filter, getDefaultSearchControls(searchScope, RETURN_OBJ_FLAG), mapper); } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, net.sf.ldaptemplate.ContextMapper) */ public List search(Name base, String filter, ContextMapper mapper) throws DataAccessException { return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, net.sf.ldaptemplate.ContextMapper) */ public List search(String base, String filter, ContextMapper mapper) throws DataAccessException { return search(base, filter, DEFAULT_SEARCH_SCOPE, mapper); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, javax.naming.directory.SearchControls, * net.sf.ldaptemplate.ContextMapper) */ public List search(String base, String filter, SearchControls controls, ContextMapper mapper) { assureReturnObjFlagSet(controls); ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( mapper); search(base, filter, controls, handler); return handler.getList(); } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, javax.naming.directory.SearchControls, * net.sf.ldaptemplate.ContextMapper) */ public List search(Name base, String filter, SearchControls controls, ContextMapper mapper) { assureReturnObjFlagSet(controls); ContextMapperCallbackHandler handler = new ContextMapperCallbackHandler( mapper); search(base, filter, controls, handler); return handler.getList(); } /* * @see net.sf.ldaptemplate.LdapOperations#search(javax.naming.Name, * java.lang.String, javax.naming.directory.SearchControls, * net.sf.ldaptemplate.AttributesMapper) */ public List search(Name base, String filter, SearchControls controls, AttributesMapper mapper) { AttributesMapperCallbackHandler handler = new AttributesMapperCallbackHandler( mapper); search(base, filter, controls, handler); return handler.getList(); } /* * @see net.sf.ldaptemplate.LdapOperations#search(java.lang.String, * java.lang.String, javax.naming.directory.SearchControls, * net.sf.ldaptemplate.AttributesMapper) */ public List search(String base, String filter, SearchControls controls, AttributesMapper mapper) { AttributesMapperCallbackHandler handler = new AttributesMapperCallbackHandler( mapper); search(base, filter, controls, handler); return handler.getList(); } /* * @see net.sf.ldaptemplate.LdapOperations#executeReadOnly(net.sf.ldaptemplate.ContextExecutor) */ public Object executeReadOnly(ContextExecutor ce) { DirContext ctx = contextSource.getReadOnlyContext(); return executeWithContext(ce, ctx); } /* * @see net.sf.ldaptemplate.LdapOperations#executeReadWrite(net.sf.ldaptemplate.ContextExecutor) */ 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 (NamingException e) { throw getExceptionTranslator().translate(e); } finally { closeContext(ctx); } } /* * @see net.sf.ldaptemplate.LdapOperations#lookup(javax.naming.Name) */ public Object lookup(final Name dn) { return executeReadOnly(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { return ctx.lookup(dn); } }); } /* * @see net.sf.ldaptemplate.LdapOperations#lookup(java.lang.String) */ public Object lookup(final String dn) throws DataAccessException { return executeReadOnly(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { return ctx.lookup(dn); } }); } /* * @see net.sf.ldaptemplate.LdapOperations#lookup(javax.naming.Name, * net.sf.ldaptemplate.AttributesMapper) */ public Object lookup(final Name dn, final AttributesMapper mapper) { return executeReadOnly(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { Attributes attributes = ctx.getAttributes(dn); return mapper.mapFromAttributes(attributes); } }); } /* * @see net.sf.ldaptemplate.LdapOperations#lookup(java.lang.String, * net.sf.ldaptemplate.AttributesMapper) */ public Object lookup(final String dn, final AttributesMapper mapper) throws DataAccessException { return executeReadOnly(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { Attributes attributes = ctx.getAttributes(dn); return mapper.mapFromAttributes(attributes); } }); } /* * @see net.sf.ldaptemplate.LdapOperations#lookup(javax.naming.Name, * net.sf.ldaptemplate.ContextMapper) */ public Object lookup(final Name dn, final ContextMapper mapper) { return executeReadOnly(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { Object object = ctx.lookup(dn); return mapper.mapFromContext(object); } }); } /* * @see net.sf.ldaptemplate.LdapOperations#lookup(java.lang.String, * net.sf.ldaptemplate.ContextMapper) */ public Object lookup(final String dn, final ContextMapper mapper) throws DataAccessException { return executeReadOnly(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { Object object = ctx.lookup(dn); return mapper.mapFromContext(object); } }); } /* * @see net.sf.ldaptemplate.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 NamingException { ctx.modifyAttributes(dn, mods); return null; } }); } /* * @see net.sf.ldaptemplate.LdapOperations#modifyAttributes(java.lang.String, * javax.naming.directory.ModificationItem[]) */ public void modifyAttributes(final String dn, final ModificationItem[] mods) throws DataAccessException { executeReadWrite(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { ctx.modifyAttributes(dn, mods); return null; } }); } /* * @see net.sf.ldaptemplate.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 NamingException { ctx.bind(dn, obj, attributes); return null; } }); } /* * @see net.sf.ldaptemplate.LdapOperations#bind(java.lang.String, * java.lang.Object, javax.naming.directory.Attributes) */ public void bind(final String dn, final Object obj, final Attributes attributes) throws DataAccessException { executeReadWrite(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { ctx.bind(dn, obj, attributes); return null; } }); } /* * @see net.sf.ldaptemplate.LdapOperations#unbind(javax.naming.Name) */ public void unbind(final Name dn) { executeReadWrite(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { ctx.unbind(dn); return null; } }); } /* * @see net.sf.ldaptemplate.LdapOperations#unbind(java.lang.String) */ public void unbind(final String dn) throws DataAccessException { executeReadWrite(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { ctx.unbind(dn); return null; } }); } /* * @see net.sf.ldaptemplate.LdapOperations#rebind(javax.naming.Name, * java.lang.Object, javax.naming.directory.Attributes) */ public void rebind(final Name dn, final Object obj, final Attributes attributes) throws DataAccessException { executeReadWrite(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { ctx.rebind(dn, obj, attributes); return null; } }); } /* * @see net.sf.ldaptemplate.LdapOperations#rebind(java.lang.String, * java.lang.Object, javax.naming.directory.Attributes) */ public void rebind(final String dn, final Object obj, final Attributes attributes) throws DataAccessException { executeReadWrite(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { ctx.rebind(dn, obj, attributes); 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. } } } /** * Get the NamingExceptionTranslator that will be used by this instance. If * no exceptionTranslator has been set, a default instance will be created. * * @return the NamingExceptionTranslator to be used by this instance. */ public NamingExceptionTranslator getExceptionTranslator() { return exceptionTranslator; } /** * Set the NamingExceptionTranslator to be used by this instance. * * @param exceptionTranslator * the NamingExceptionTranslator to use. */ public void setExceptionTranslator( NamingExceptionTranslator exceptionTranslator) { this.exceptionTranslator = exceptionTranslator; } private SearchControls getDefaultSearchControls(int searchScope, boolean returningObjFlag) { SearchControls controls = new SearchControls(); controls.setSearchScope(searchScope); controls.setReturningObjFlag(returningObjFlag); 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); } } /** * A CollectingSearchResultCallbackHandler to wrap an AttributesMapper. That * is, the found objects are extracted from all SearchResults, and then * passed to the specified AttributesMapper for translation. This class * needs to be nested, since we want to be able to get hold of the exception * translator of this instance. * * @author Mattias Arthursson * @author Ulrik Sandberg */ public class AttributesMapperCallbackHandler extends CollectingSearchResultCallbackHandler { private AttributesMapper mapper; public AttributesMapperCallbackHandler(AttributesMapper mapper) { this.mapper = mapper; } protected Object getObjectFromResult(SearchResult searchResult) { Attributes attributes = searchResult.getAttributes(); try { return mapper.mapFromAttributes(attributes); } catch (NamingException e) { throw getExceptionTranslator().translate(e); } } } /** * A CollectingSearchResultCallbackHandler to wrap a ContextMapper. That is, * the found objects are extracted from all SearchResults, and then passed * to the specified ContextMapper for translation. * * @author Mattias Arthursson */ public class ContextMapperCallbackHandler extends CollectingSearchResultCallbackHandler { private ContextMapper mapper; public ContextMapperCallbackHandler(ContextMapper mapper) { this.mapper = mapper; } protected Object getObjectFromResult(SearchResult searchResult) { Object object = searchResult.getObject(); if (object == null) { throw new EntryNotFoundException( "SearchResult did not contain any object."); } return mapper.mapFromContext(object); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy