LDAP-267: Minor tweaks to XML namespace support more tests and reference documentation.
This commit is contained in:
@@ -18,22 +18,25 @@ package org.springframework.ldap.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.ldap.CommunicationException;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.ldap.pool.PoolExhaustedAction;
|
||||
import org.springframework.ldap.pool.factory.PoolingContextSource;
|
||||
import org.springframework.ldap.pool.validation.DefaultDirContextValidator;
|
||||
import org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import static org.springframework.ldap.config.ParserUtils.NAMESPACE;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springframework.ldap.config.ParserUtils.getBoolean;
|
||||
import static org.springframework.ldap.config.ParserUtils.getInt;
|
||||
import static org.springframework.ldap.config.ParserUtils.getString;
|
||||
@@ -43,12 +46,14 @@ import static org.springframework.ldap.config.ParserUtils.getString;
|
||||
*/
|
||||
public class ContextSourceParser implements BeanDefinitionParser {
|
||||
private final static String ATT_ANONYMOUS_READ_ONLY = "anonymous-read-only";
|
||||
private final static String ATT_AUTHENTICATION_SOURCE_REF = "authentication-source-ref";
|
||||
private final static String ATT_AUTHENTICATION_STRATEGY_REF = "authentication-strategy-ref";
|
||||
private final static String ATT_BASE = "base";
|
||||
private final static String ATT_PASSWORD = "password";
|
||||
private final static String ATT_NATIVE_POOLING = "native-pooling";
|
||||
private final static String ATT_REFERRAL = "referral";
|
||||
private final static String ATT_URL = "url";
|
||||
private final static String ATT_BASE_ENV_PROPS_REF = "base-env-props-ref";
|
||||
|
||||
// pooling attributes
|
||||
private final static String ATT_MAX_ACTIVE = "max-active";
|
||||
@@ -66,6 +71,7 @@ public class ContextSourceParser implements BeanDefinitionParser {
|
||||
private final static String ATT_VALIDATION_QUERY_BASE = "validation-query-base";
|
||||
private final static String ATT_VALIDATION_QUERY_FILTER = "validation-query-filter";
|
||||
private final static String ATT_VALIDATION_QUERY_SEARCH_CONTROLS_REF = "validation-query-search-controls-ref";
|
||||
private final static String ATT_NON_TRANSIENT_EXCEPTIONS = "non-transient-exceptions";
|
||||
|
||||
private final static String ATT_USERNAME = "username";
|
||||
static final String DEFAULT_ID = "contextSource";
|
||||
@@ -78,8 +84,6 @@ public class ContextSourceParser implements BeanDefinitionParser {
|
||||
String password = element.getAttribute(ATT_PASSWORD);
|
||||
String url = element.getAttribute(ATT_URL);
|
||||
|
||||
Assert.hasText(username, "username attribute must be specified");
|
||||
Assert.hasText(password, "password attribute must be specified");
|
||||
Assert.hasText(url, "url attribute must be specified");
|
||||
|
||||
builder.addPropertyValue("userDn", username);
|
||||
@@ -89,35 +93,60 @@ public class ContextSourceParser implements BeanDefinitionParser {
|
||||
builder.addPropertyValue("base", getString(element, ATT_BASE, ""));
|
||||
builder.addPropertyValue("referral", getString(element, ATT_REFERRAL, null));
|
||||
|
||||
builder.addPropertyValue("anonymousReadOnly", getBoolean(element, ATT_ANONYMOUS_READ_ONLY, false));
|
||||
builder.addPropertyValue("pooled", getBoolean(element, ATT_NATIVE_POOLING, false));
|
||||
boolean anonymousReadOnly = getBoolean(element, ATT_ANONYMOUS_READ_ONLY, false);
|
||||
builder.addPropertyValue("anonymousReadOnly", anonymousReadOnly);
|
||||
boolean nativePooling = getBoolean(element, ATT_NATIVE_POOLING, false);
|
||||
builder.addPropertyValue("pooled", nativePooling);
|
||||
|
||||
String authStrategyRef = element.getAttribute(ATT_AUTHENTICATION_STRATEGY_REF);
|
||||
if(StringUtils.hasText(authStrategyRef)) {
|
||||
builder.addPropertyReference("authenticationStrategy", authStrategyRef);
|
||||
}
|
||||
|
||||
String authSourceRef = element.getAttribute(ATT_AUTHENTICATION_SOURCE_REF);
|
||||
if(StringUtils.hasText(authSourceRef)) {
|
||||
builder.addPropertyReference("authenticationSource", authSourceRef);
|
||||
} else {
|
||||
Assert.hasText(username, "username attribute must be specified unless an authentication-source-ref explicitly configured");
|
||||
Assert.hasText(password, "password attribute must be specified unless an authentication-source-ref explicitly configured");
|
||||
}
|
||||
|
||||
String baseEnvPropsRef = element.getAttribute(ATT_BASE_ENV_PROPS_REF);
|
||||
if(StringUtils.hasText(baseEnvPropsRef)) {
|
||||
builder.addPropertyReference("baseEnvironmentProperties", baseEnvPropsRef);
|
||||
}
|
||||
|
||||
BeanDefinition targetContextSourceDefinition = builder.getBeanDefinition();
|
||||
targetContextSourceDefinition = applyPoolingIfApplicable(targetContextSourceDefinition, element);
|
||||
targetContextSourceDefinition = applyPoolingIfApplicable(targetContextSourceDefinition, element, nativePooling);
|
||||
|
||||
|
||||
BeanDefinitionBuilder proxyBuilder = BeanDefinitionBuilder.rootBeanDefinition(TransactionAwareContextSourceProxy.class);
|
||||
proxyBuilder.addConstructorArgValue(targetContextSourceDefinition);
|
||||
AbstractBeanDefinition proxyBeanDefinition = proxyBuilder.getBeanDefinition();
|
||||
BeanDefinition actualContextSourceDefinition = targetContextSourceDefinition;
|
||||
if (!anonymousReadOnly) {
|
||||
BeanDefinitionBuilder proxyBuilder = BeanDefinitionBuilder.rootBeanDefinition(TransactionAwareContextSourceProxy.class);
|
||||
proxyBuilder.addConstructorArgValue(targetContextSourceDefinition);
|
||||
actualContextSourceDefinition = proxyBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
String id = getString(element, AbstractBeanDefinitionParser.ID_ATTRIBUTE, DEFAULT_ID);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(proxyBeanDefinition, id));
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(actualContextSourceDefinition, id));
|
||||
|
||||
return proxyBeanDefinition;
|
||||
return actualContextSourceDefinition;
|
||||
}
|
||||
|
||||
private BeanDefinition applyPoolingIfApplicable(BeanDefinition targetContextSourceDefinition, Element element) {
|
||||
NodeList poolingChildren = element.getElementsByTagNameNS(NAMESPACE, Elements.POOLING);
|
||||
if(poolingChildren.getLength() == 0) {
|
||||
private BeanDefinition applyPoolingIfApplicable(
|
||||
BeanDefinition targetContextSourceDefinition,
|
||||
Element element,
|
||||
boolean nativePooling) {
|
||||
|
||||
Element poolingElement = DomUtils.getChildElementByTagName(element, Elements.POOLING);
|
||||
if(poolingElement == null) {
|
||||
return targetContextSourceDefinition;
|
||||
}
|
||||
|
||||
Element poolingElement = (Element) poolingChildren.item(0);
|
||||
if(nativePooling) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("%s cannot be enabled together with %s", ATT_NATIVE_POOLING, Elements.POOLING));
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PoolingContextSource.class);
|
||||
builder.addPropertyValue("contextSource", targetContextSourceDefinition);
|
||||
|
||||
@@ -160,6 +189,18 @@ public class ContextSourceParser implements BeanDefinitionParser {
|
||||
builder.addPropertyValue("timeBetweenEvictionRunsMillis", getInt(element, ATT_EVICTION_RUN_MILLIS, -1));
|
||||
builder.addPropertyValue("numTestsPerEvictionRun", getInt(element, ATT_TESTS_PER_EVICTION_RUN, 3));
|
||||
builder.addPropertyValue("minEvictableIdleTimeMillis", getInt(element, ATT_EVICTABLE_TIME_MILLIS, 1000 * 60 * 30));
|
||||
}
|
||||
|
||||
String nonTransientExceptions = getString(element, ATT_NON_TRANSIENT_EXCEPTIONS, CommunicationException.class.getName());
|
||||
String[] strings = StringUtils.commaDelimitedListToStringArray(nonTransientExceptions);
|
||||
Set<Class<?>> nonTransientExceptionClasses = new HashSet<Class<?>>();
|
||||
for (String className : strings) {
|
||||
try {
|
||||
nonTransientExceptionClasses.add(ClassUtils.getDefaultClassLoader().loadClass(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format("%s is not a valid class name", className));
|
||||
}
|
||||
}
|
||||
|
||||
builder.addPropertyValue("nonTransientExceptions", nonTransientExceptionClasses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,9 @@ import org.springframework.ldap.transaction.compensating.support.DefaultTempEntr
|
||||
import org.springframework.ldap.transaction.compensating.support.DifferentSubtreeTempEntryRenamingStrategy;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import static org.springframework.ldap.config.ParserUtils.NAMESPACE;
|
||||
import static org.springframework.ldap.config.ParserUtils.getString;
|
||||
|
||||
/**
|
||||
@@ -62,17 +61,15 @@ public class TransactionManagerParser implements BeanDefinitionParser {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ContextSourceTransactionManager.class);
|
||||
builder.addPropertyReference("contextSource", contextSourceRef);
|
||||
|
||||
NodeList defaultStrategyChildren =
|
||||
element.getElementsByTagNameNS(NAMESPACE, Elements.DEFAULT_RENAMING_STRATEGY);
|
||||
NodeList differentSubtreeChildren =
|
||||
element.getElementsByTagNameNS(NAMESPACE, Elements.DIFFERENT_SUBTREE_RENAMING_STRATEGY);
|
||||
Element defaultStrategyChild = DomUtils.getChildElementByTagName(element, Elements.DEFAULT_RENAMING_STRATEGY);
|
||||
Element differentSubtreeChild = DomUtils.getChildElementByTagName(element, Elements.DIFFERENT_SUBTREE_RENAMING_STRATEGY);
|
||||
|
||||
if(defaultStrategyChildren.getLength() == 1) {
|
||||
builder.addPropertyValue("renamingStrategy", parseDefaultRenamingStrategy((Element) defaultStrategyChildren.item(0)));
|
||||
if(defaultStrategyChild != null) {
|
||||
builder.addPropertyValue("renamingStrategy", parseDefaultRenamingStrategy(defaultStrategyChild));
|
||||
}
|
||||
|
||||
if(differentSubtreeChildren.getLength() == 1) {
|
||||
builder.addPropertyValue("renamingStrategy", parseDifferentSubtreeRenamingStrategy((Element) differentSubtreeChildren.item(0)));
|
||||
if(differentSubtreeChild != null) {
|
||||
builder.addPropertyValue("renamingStrategy", parseDifferentSubtreeRenamingStrategy(differentSubtreeChild));
|
||||
}
|
||||
|
||||
String id = getString(element, AbstractBeanDefinitionParser.ID_ATTRIBUTE, DEFAULT_ID);
|
||||
|
||||
@@ -665,6 +665,5 @@ public abstract class AbstractContextSource implements BaseLdapPathContextSource
|
||||
public String getCredentials() {
|
||||
return password;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.ldap.transaction.compensating.manager;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.support.AbstractContextSource;
|
||||
import org.springframework.ldap.transaction.compensating.LdapCompensatingTransactionOperationFactory;
|
||||
import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy;
|
||||
import org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy;
|
||||
@@ -68,6 +69,14 @@ public class ContextSourceTransactionManagerDelegate extends
|
||||
} else {
|
||||
this.contextSource = contextSource;
|
||||
}
|
||||
|
||||
if (contextSource instanceof AbstractContextSource) {
|
||||
AbstractContextSource abstractContextSource = (AbstractContextSource) contextSource;
|
||||
if(abstractContextSource.isAnonymousReadOnly()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Compensating LDAP transactions cannot be used when context-source is anonymous-read-only");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ContextSource getContextSource() {
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
<xs:attribute name="base" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The base DN. If specified, all LDAP operations on contexts retrieved from this ContextSource will
|
||||
The base DN. If configured, all LDAP operations on contexts retrieved from this ContextSource will
|
||||
be relative to this DN. Default is an empty distinguished name (i.e. all operations will be
|
||||
relative to the directory root).
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="password" type="xs:string" use="required">
|
||||
<xs:attribute name="password" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The password to use for authentication.
|
||||
@@ -82,7 +82,7 @@
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="username" type="xs:string" use="required">
|
||||
<xs:attribute name="username" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The username (principal) to use for authentication. This will normally be the distinguished name
|
||||
@@ -90,6 +90,14 @@
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="base-env-props-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Reference to a Map of custom environment properties that should supplied with the environment
|
||||
sent to the DirContext on construction.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:attributeGroup name="pooling.attlist">
|
||||
@@ -240,6 +248,14 @@
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="non-transient-exceptions" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;
|
||||
countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:element name="context-source">
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2005-2013 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.config;
|
||||
|
||||
import org.springframework.ldap.core.AuthenticationSource;
|
||||
|
||||
/**
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
public class DummyAuthenticationSource implements AuthenticationSource {
|
||||
@Override
|
||||
public String getPrincipal() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCredentials() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2005-2013 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.config;
|
||||
|
||||
import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.DirContext;
|
||||
import java.util.Hashtable;
|
||||
|
||||
/**
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
public class DummyAuthenticationStrategy implements DirContextAuthenticationStrategy {
|
||||
@Override
|
||||
public void setupEnvironment(Hashtable<String, Object> env, String userDn, String password) throws NamingException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirContext processContextAfterCreation(DirContext ctx, String userDn, String password) throws NamingException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,13 @@ package org.springframework.ldap.config;
|
||||
|
||||
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.ldap.core.AuthenticationSource;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.ldap.pool.factory.PoolingContextSource;
|
||||
import org.springframework.ldap.pool.validation.DefaultDirContextValidator;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
@@ -32,7 +35,10 @@ import org.springframework.ldap.transaction.compensating.support.DefaultTempEntr
|
||||
import org.springframework.ldap.transaction.compensating.support.DifferentSubtreeTempEntryRenamingStrategy;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import javax.naming.CannotProceedException;
|
||||
import javax.naming.CommunicationException;
|
||||
import javax.naming.directory.SearchControls;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -75,6 +81,48 @@ public class LdapTemplateNamespaceHandlerTest {
|
||||
assertEquals(SearchControls.SUBTREE_SCOPE, getInternalState(ldapTemplate, "defaultSearchScope"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyThatAnonymousReadOnlyContextWillNotBeWrappedInProxy() {
|
||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-anonymous-read-only.xml");
|
||||
ContextSource contextSource = ctx.getBean(ContextSource.class);
|
||||
|
||||
assertNotNull(contextSource);
|
||||
assertTrue(contextSource instanceof LdapContextSource);
|
||||
assertEquals(Boolean.TRUE, getInternalState(contextSource, "anonymousReadOnly"));
|
||||
}
|
||||
|
||||
@Test(expected = BeansException.class)
|
||||
public void verifyThatAnonymousReadOnlyAndTransactionalThrowsException() {
|
||||
new ClassPathXmlApplicationContext("/ldap-namespace-config-anonymous-read-only-and-transactions.xml");
|
||||
}
|
||||
|
||||
@Test(expected = BeansException.class)
|
||||
public void verifyThatMissingUsernameThrowsException() {
|
||||
new ClassPathXmlApplicationContext("/ldap-namespace-config-missing-username.xml");
|
||||
}
|
||||
|
||||
@Test(expected = BeansException.class)
|
||||
public void verifyThatMissingPasswordThrowsException() {
|
||||
new ClassPathXmlApplicationContext("/ldap-namespace-config-missing-password.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyReferences() {
|
||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-references.xml");
|
||||
ContextSource outerContextSource = ctx.getBean(ContextSource.class);
|
||||
AuthenticationSource authenticationSource = ctx.getBean(AuthenticationSource.class);
|
||||
DirContextAuthenticationStrategy authenticationStrategy = ctx.getBean(DirContextAuthenticationStrategy.class);
|
||||
Object baseEnv = ctx.getBean("baseEnvProps");
|
||||
|
||||
assertNotNull(outerContextSource);
|
||||
|
||||
assertTrue(outerContextSource instanceof TransactionAwareContextSourceProxy);
|
||||
ContextSource contextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
|
||||
|
||||
assertSame(authenticationSource, getInternalState(contextSource, "authenticationSource"));
|
||||
assertSame(authenticationStrategy, getInternalState(contextSource, "authenticationStrategy"));
|
||||
assertEquals(baseEnv, getInternalState(contextSource, "baseEnv"));
|
||||
}
|
||||
@Test
|
||||
public void verifyParseWithCustomValues() {
|
||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-values.xml");
|
||||
@@ -93,7 +141,7 @@ public class LdapTemplateNamespaceHandlerTest {
|
||||
assertEquals("apassword", getInternalState(contextSource, "password"));
|
||||
assertArrayEquals(new String[]{"ldap://localhost:389"}, (Object[]) getInternalState(contextSource, "urls"));
|
||||
assertEquals(Boolean.TRUE, getInternalState(contextSource, "pooled"));
|
||||
assertEquals(Boolean.TRUE, getInternalState(contextSource, "anonymousReadOnly"));
|
||||
assertEquals(Boolean.FALSE, getInternalState(contextSource, "anonymousReadOnly"));
|
||||
assertEquals("follow", getInternalState(contextSource, "referral"));
|
||||
assertSame(authenticationStrategy, getInternalState(contextSource, "authenticationStrategy"));
|
||||
|
||||
@@ -164,6 +212,7 @@ public class LdapTemplateNamespaceHandlerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void verifyParsePoolingDefaults() {
|
||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-pooling-defaults.xml");
|
||||
|
||||
@@ -178,6 +227,10 @@ public class LdapTemplateNamespaceHandlerTest {
|
||||
Object objectFactory = getInternalState(pooledContextSource, "dirContextPoolableObjectFactory");
|
||||
assertNotNull(getInternalState(objectFactory, "contextSource"));
|
||||
assertNull(getInternalState(objectFactory, "dirContextValidator"));
|
||||
Set<Class<? extends Throwable>> nonTransientExceptions =
|
||||
(Set<Class<? extends Throwable>>) getInternalState(objectFactory, "nonTransientExceptions");
|
||||
assertEquals(1, nonTransientExceptions.size());
|
||||
assertTrue(nonTransientExceptions.contains(CommunicationException.class));
|
||||
|
||||
GenericKeyedObjectPool objectPool = (GenericKeyedObjectPool) getInternalState(pooledContextSource, "keyedObjectPool");
|
||||
assertEquals(8, objectPool.getMaxActive());
|
||||
@@ -208,6 +261,7 @@ public class LdapTemplateNamespaceHandlerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void verifyParsePoolingValidationSet() {
|
||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-pooling-test-specified.xml");
|
||||
|
||||
@@ -230,5 +284,16 @@ public class LdapTemplateNamespaceHandlerTest {
|
||||
SearchControls searchControls = ctx.getBean(SearchControls.class);
|
||||
assertEquals("objectclass=person", validator.getFilter());
|
||||
assertSame(searchControls, validator.getSearchControls());
|
||||
|
||||
Set<Class<? extends Throwable>> nonTransientExceptions =
|
||||
(Set<Class<? extends Throwable>>) getInternalState(objectFactory, "nonTransientExceptions");
|
||||
assertEquals(2, nonTransientExceptions.size());
|
||||
assertTrue(nonTransientExceptions.contains(CommunicationException.class));
|
||||
assertTrue(nonTransientExceptions.contains(CannotProceedException.class));
|
||||
}
|
||||
|
||||
@Test(expected = BeansException.class)
|
||||
public void verifyParseWithPoolingAndNativePoolingWillFail() {
|
||||
new ClassPathXmlApplicationContext("/ldap-namespace-config-pooling-with-native.xml");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ldap="http://www.springframework.org/schema/ldap"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
|
||||
|
||||
<ldap:context-source
|
||||
password="apassword"
|
||||
url="ldap://localhost:389"
|
||||
username="uid=admin"
|
||||
anonymous-read-only="true" />
|
||||
|
||||
<!-- This is not valid together with anonymous-read-only -->
|
||||
<ldap:transaction-manager>
|
||||
<ldap:default-renaming-strategy />
|
||||
</ldap:transaction-manager>
|
||||
|
||||
<ldap:ldap-template />
|
||||
</beans>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ldap="http://www.springframework.org/schema/ldap"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
|
||||
|
||||
<ldap:context-source
|
||||
password="apassword"
|
||||
url="ldap://localhost:389"
|
||||
username="uid=admin"
|
||||
anonymous-read-only="true" />
|
||||
|
||||
<ldap:ldap-template />
|
||||
</beans>
|
||||
@@ -2,8 +2,8 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ldap="http://www.springframework.org/schema/ldap"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
|
||||
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
|
||||
|
||||
<ldap:context-source password="apassword" url="ldap://localhost:389" username="uid=admin"/>
|
||||
<ldap:ldap-template />
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ldap="http://www.springframework.org/schema/ldap"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
|
||||
|
||||
<ldap:context-source url="ldap://localhost:389" username="uid=admin"/>
|
||||
<ldap:ldap-template />
|
||||
</beans>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ldap="http://www.springframework.org/schema/ldap"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
|
||||
|
||||
<ldap:context-source password="apassword" url="ldap://localhost:389" />
|
||||
</beans>
|
||||
@@ -15,7 +15,8 @@
|
||||
tests-per-eviction-run="22"
|
||||
validation-query-base="ou=test"
|
||||
validation-query-filter="objectclass=person"
|
||||
validation-query-search-controls-ref="searchControls"/>
|
||||
validation-query-search-controls-ref="searchControls"
|
||||
non-transient-exceptions="javax.naming.CannotProceedException,javax.naming.CommunicationException" />
|
||||
</ldap:context-source>
|
||||
|
||||
<bean class="javax.naming.directory.SearchControls" id="searchControls" />
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ldap="http://www.springframework.org/schema/ldap"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
|
||||
|
||||
<!--
|
||||
The below is invalid, since native pooling is not supported together with Spring LDAP pooling.
|
||||
-->
|
||||
<ldap:context-source
|
||||
password="apassword" url="ldap://localhost:389" username="uid=admin"
|
||||
native-pooling="true">
|
||||
<ldap:pooling />
|
||||
</ldap:context-source>
|
||||
|
||||
<ldap:ldap-template />
|
||||
</beans>
|
||||
21
core/src/test/resources/ldap-namespace-config-references.xml
Normal file
21
core/src/test/resources/ldap-namespace-config-references.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ldap="http://www.springframework.org/schema/ldap" xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
|
||||
|
||||
<ldap:context-source url="ldap://localhost:389"
|
||||
authentication-source-ref="authenticationSource"
|
||||
authentication-strategy-ref="authenticationStrategy"
|
||||
base-env-props-ref="baseEnvProps" />
|
||||
|
||||
<bean class="org.springframework.ldap.config.DummyAuthenticationSource" id="authenticationSource" />
|
||||
<bean class="org.springframework.ldap.config.DummyAuthenticationStrategy" id="authenticationStrategy" />
|
||||
|
||||
<util:map id="baseEnvProps">
|
||||
<entry key="dummy" value="dummyValue" />
|
||||
</util:map>
|
||||
|
||||
<ldap:ldap-template />
|
||||
</beans>
|
||||
@@ -11,7 +11,6 @@
|
||||
url="ldap://localhost:389"
|
||||
username="uid=admin"
|
||||
base="dc=261consulting,dc=com"
|
||||
anonymous-read-only="true"
|
||||
authentication-strategy-ref="authenticationStrategy"
|
||||
native-pooling="true"
|
||||
referral="follow" />
|
||||
|
||||
@@ -402,21 +402,6 @@ public class PersonDaoImpl implements PersonDao {
|
||||
|
||||
<para>It is recommended that you review the Spring LDAP sample
|
||||
applications included in the release distribution for best-practice
|
||||
illustrations of the features of this library. A description of each
|
||||
sample is provided below:</para>
|
||||
|
||||
<para><orderedlist>
|
||||
<listitem>
|
||||
<para>spring-ldap-person - the sample demonstrating most
|
||||
features.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>spring-ldap-article - the sample application that was written
|
||||
to accompany a <ulink
|
||||
url="http://today.java.net/pub/a/today/2006/04/18/ldaptemplate-java-ldap-made-simple.html">java.net
|
||||
article</ulink> about Spring LDAP.</para>
|
||||
</listitem>
|
||||
</orderedlist></para>
|
||||
illustrations of the features of this library.</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
@@ -1,205 +1,362 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="configuration">
|
||||
<title>Configuration</title>
|
||||
<sect1 id="configuration-intro">
|
||||
<title>Introduction</title>
|
||||
The recommended way of configuring Spring LDAP is using the custom XML configuration namespace.
|
||||
In order to make this available you need to include the Spring LDAP namespace declaration in your
|
||||
bean file, e.g.:
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<emphasis role="bold">xmlns:ldap="http://www.springframework.org/schema/ldap"</emphasis>
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
<emphasis role="bold">http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd"</emphasis>>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="context-source-configuration">
|
||||
<title>ContextSource Configuration</title>
|
||||
<sect1 id="context-source-configuration">
|
||||
<title>ContextSource Configuration</title>
|
||||
|
||||
<para>There are several properties in <literal>AbstractContextSource</literal>
|
||||
(superclass of <literal>DirContextSource</literal> and <literal>LdapContextSource</literal>)
|
||||
that can be used to modify its behaviour.</para>
|
||||
|
||||
<sect2 id="dir-context-url">
|
||||
<title>LDAP Server URLs</title>
|
||||
|
||||
<para>The URL of the LDAP server is specified using the <literal>url</literal> property.
|
||||
The URL should be in the format <literal>ldap://myserver.example.com:389</literal>.
|
||||
For SSL access, use the <literal>ldaps</literal> protocol and the appropriate port, e.g.
|
||||
<literal>ldaps://myserver.example.com:636</literal></para>
|
||||
<para>It is possible to configure multiple alternate LDAP servers using the
|
||||
<literal>urls</literal> property. In this case, supply all server urls in a String
|
||||
array to the <literal>urls</literal> property.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="dir-context-base">
|
||||
<title>Base LDAP path</title>
|
||||
|
||||
<para>It is possible to specify the root context for all LDAP operations using the
|
||||
<literal>base</literal> property of <literal>AbstractContextSource</literal>.
|
||||
When a value has been specified to this property, all Distinguished Names supplied to and received from LDAP operations
|
||||
will be relative to the LDAP path supplied. This can significantly simplify working against the LDAP
|
||||
tree; however there are several occations when you will need to have access to the base path.
|
||||
For more information on this, please refer to <xref linkend="base-context-configuration" /></para>
|
||||
</sect2>
|
||||
<para>
|
||||
The <literal>ContextSource</literal> is defined using a <literal><ldap:context-source></literal>
|
||||
tag. The simplest possible <literal>context-source</literal> declaration requires you to specify a
|
||||
server url, a username, and a password:
|
||||
<example>
|
||||
<title>Simplest possible context-source declaration</title>
|
||||
<programlisting><![CDATA[
|
||||
<ldap:context-source username="cn=Administrator" password="secret" url="ldap://localhost:389" />]]></programlisting>
|
||||
</example>
|
||||
This will create an <literal>LdapContextSource</literal> with default values (see below),
|
||||
and the url and authentication information as specified.
|
||||
</para>
|
||||
<para>
|
||||
The configurable attributes on context-source are as follows (required attributes marked with *):
|
||||
</para>
|
||||
<table frame="all">
|
||||
<title>ContextSource Configuration Attributes</title>
|
||||
<tgroup align="left" cols="3" colsep="1" rowsep="1">
|
||||
<colspec colname="c1" />
|
||||
<colspec colname="c2" />
|
||||
<colspec colname="c3" />
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Attribute</entry>
|
||||
<entry>Default</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>id</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>contextSource</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
The id of the created bean.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>username</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
</entry>
|
||||
<entry>
|
||||
The username (principal) to use when authenticating with the LDAP server.
|
||||
This will usually be the distinguished name of an admin user (e.g.
|
||||
<literal>cn=Administrator</literal>, but may differ depending on server
|
||||
and authentication method.
|
||||
Required if <literal>authentication-source-ref</literal> is not explicitly configured.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>password</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
</entry>
|
||||
<entry>
|
||||
The password (credentials) to use when authenticating with the LDAP server.
|
||||
Required if <literal>authentication-source-ref</literal> is not explicitly configured.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>url</literal> *
|
||||
</entry>
|
||||
<entry>
|
||||
</entry>
|
||||
<entry>
|
||||
The URL of the LDAP server to use. The URL should be in the format
|
||||
<literal>ldap://myserver.example.com:389</literal>.
|
||||
For SSL access, use the <literal>ldaps</literal> protocol and the appropriate port, e.g.
|
||||
<literal>ldaps://myserver.example.com:636</literal>. If fail-over functionality is desired,
|
||||
more than one URL can be specified, separated using comma (,).
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>base</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>LdapUtils.emptyLdapName()</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
The base DN. When this attribute has been configured, all Distinguished Names supplied to
|
||||
and received from LDAP operations will be relative to the sepecified LDAP path.
|
||||
This can significantly simplify working against the LDAP tree; however there are several
|
||||
occasions when you will need to have access to the base path.
|
||||
For more information on this, please refer to <xref linkend="base-context-configuration" />
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>anonymous-read-only</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>false</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
Defines whether read-only operations will be performed using an anonymous (unauthenticated) context.
|
||||
<emphasis role="bold">Note</emphasis> that setting this parameter to <literal>true</literal>
|
||||
together with the compensating transaction support is not supported and will be rejected.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>referral</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>null</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
Defines the strategy to handle referrals, as described
|
||||
<ulink url="http://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html">here</ulink>.
|
||||
Valid values are:
|
||||
<itemizedlist>
|
||||
<listitem><literal>ignore</literal></listitem>
|
||||
<listitem><literal>follow</literal></listitem>
|
||||
<listitem><literal>throw</literal></listitem>
|
||||
</itemizedlist>
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>native-pooling</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>false</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
Specify whether native Java LDAP connection pooling should be used. Consider using
|
||||
Spring LDAP connection pooling instead. See <xref linkend="pooling" /> for more information.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>authentication-source-ref</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
A <literal>SimpleAuthenticationSource</literal> instance.
|
||||
</entry>
|
||||
<entry>
|
||||
Id of the AuthenticationSource instance to use (see below).
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>authentication-strategy-ref</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
A <literal>SimpleDirContextAuthenticationStrategy</literal> instance.
|
||||
</entry>
|
||||
<entry>
|
||||
Id of the DirContextAuthenticationStrategy instance to use (see below).
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>base-env-props-ref</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
A <literal>SimpleDirContextAuthenticationStrategy</literal> instance.
|
||||
</entry>
|
||||
<entry>
|
||||
Reference to a Map of custom environment properties that should supplied with the environment
|
||||
sent to the <literal>DirContext</literal> on construction.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<sect2 id="dir-context-authentication">
|
||||
<title>DirContext Authentication</title>
|
||||
|
||||
<para>When <literal>DirContext</literal> instances are created to be used for performing
|
||||
operations on an LDAP server these contexts often need to be authenticated. There are
|
||||
different options for configuring this using Spring LDAP, described in this chapter.</para>
|
||||
<para>
|
||||
When <literal>DirContext</literal> instances are created to be used for performing
|
||||
operations on an LDAP server these contexts often need to be authenticated. There are
|
||||
different options for configuring this using Spring LDAP, described in this chapter.
|
||||
|
||||
<para><note><para>This section refers to authenticating contexts in the core functionality
|
||||
of the <literal>ContextSource</literal> - to construct <literal>DirContext</literal> instances
|
||||
for use by <literal>LdapTemplate</literal>. LDAP is commonly used for the sole purpose
|
||||
of user authentication, and the <literal>ContextSource</literal> may be used for that as
|
||||
well. This process is discussed in <xref linkend="user-authentication" />.
|
||||
</para></note></para>
|
||||
<note>
|
||||
<para>
|
||||
This section refers to authenticating contexts in the core functionality
|
||||
of the <literal>ContextSource</literal> - to construct <literal>DirContext</literal> instances
|
||||
for use by <literal>LdapTemplate</literal>. LDAP is commonly used for the sole purpose
|
||||
of user authentication, and the <literal>ContextSource</literal> may be used for that as
|
||||
well. This process is discussed in <xref linkend="user-authentication" />.
|
||||
</para>
|
||||
</note>
|
||||
</para>
|
||||
|
||||
<para>Authenticated contexts are created for both read-only and
|
||||
read-write operations by default. You specify
|
||||
<literal>userDn</literal> and <literal>password</literal> of the LDAP
|
||||
user to be used for authentication on the
|
||||
<literal>ContextSource</literal>.</para>
|
||||
<para>
|
||||
Authenticated contexts are created for both read-only and
|
||||
read-write operations by default. You specify
|
||||
<literal>username</literal> and <literal>password</literal> of the LDAP
|
||||
user to be used for authentication on the
|
||||
<literal>context-source</literal> element.
|
||||
|
||||
<para><note>
|
||||
<para>The <literal>userDn</literal> needs to be the full
|
||||
Distinguished Name (DN) of the user from the root of the LDAP tree,
|
||||
regardless of whether a <literal>base</literal> LDAP path has been supplied to
|
||||
the <literal>ContextSource</literal>.</para>
|
||||
</note></para>
|
||||
|
||||
<para>Some LDAP server setups allow anonymous read-only access. If you
|
||||
want to use anonymous Contexts for read-only operations, set the
|
||||
<literal>anonymousReadOnly</literal> property to
|
||||
<literal>true</literal>.<literal></literal></para>
|
||||
<note>
|
||||
<para>
|
||||
If <literal>username</literal> is the dn of an LDAP user, it needs to be the full
|
||||
Distinguished Name (DN) of the user from the root of the LDAP tree,
|
||||
regardless of whether a <literal>base</literal> LDAP path has been specified on
|
||||
the <literal>context-source</literal> element.
|
||||
</para>
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
Some LDAP server setups allow anonymous read-only access. If you
|
||||
want to use anonymous Contexts for read-only operations, set the
|
||||
<literal>anonymous-read-only</literal> attribute to
|
||||
<literal>true</literal>.
|
||||
</para>
|
||||
|
||||
<sect3 id="custom-authentication-processing">
|
||||
<title>Custom DirContext Authentication Processing</title>
|
||||
|
||||
<para>The default authentication mechanism used in Spring LDAP is SIMPLE authentication.
|
||||
This means that in the user DN (as specified to the <literal>userDn</literal> property) and
|
||||
the credentials (as specified to the <literal>password</literal>) are set in
|
||||
the Hashtable sent to the <literal>DirContext</literal> implementation constructor.</para>
|
||||
<para>There are many occasions when this processing is not sufficient. For instance,
|
||||
LDAP Servers are commonly set up to only accept communication on a secure TLS channel;
|
||||
there might be a need to use the particular LDAP Proxy Auth mechanism, etc.</para>
|
||||
<para>It is possible to specify an alternative authentication mechanism by supplying a
|
||||
<literal>DirContextAuthenticationStrategy</literal> implementation to the <literal>ContextSource</literal>
|
||||
in the configuration.</para>
|
||||
<para>
|
||||
The default authentication mechanism used in Spring LDAP is <literal>SIMPLE</literal> authentication.
|
||||
This means that the principal (as specified to the <literal>username</literal> attribute) and
|
||||
the credentials (as specified to the <literal>password</literal>) are set in
|
||||
the Hashtable sent to the <literal>DirContext</literal> implementation constructor.
|
||||
</para>
|
||||
<para>
|
||||
There are many occasions when this processing is not sufficient. For instance,
|
||||
LDAP Servers are commonly set up to only accept communication on a secure TLS channel;
|
||||
there might be a need to use the particular LDAP Proxy Auth mechanism, etc.
|
||||
</para>
|
||||
<para>
|
||||
It is possible to specify an alternative authentication mechanism by supplying a
|
||||
<literal>DirContextAuthenticationStrategy</literal> implementation reference
|
||||
to the <literal>context-source</literal> element using the <literal>authentication-strategy-ref</literal>
|
||||
attribute.
|
||||
</para>
|
||||
<sect4 id="authentication-tls">
|
||||
<title>TLS</title>
|
||||
|
||||
<para>Spring LDAP provides two different configuration options for LDAP servers requiring TLS secure
|
||||
channel communication: <literal>DefaultTlsDirContextAuthenticationStrategy</literal> and
|
||||
<literal>ExternalTlsDirContextAuthenticationStrategy</literal>. Both these
|
||||
implementations will negotiate a TLS channel on the target connection, but they differ in the actual authentication mechanism.
|
||||
Whereas the <literal>DefaultTlsDirContextAuthenticationStrategy</literal> will apply SIMPLE authentication
|
||||
on the secure channel (using the specified <literal>userDn</literal> and <literal>password</literal>),
|
||||
the <literal>ExternalDirContextAuthenticationStrategy</literal> will use EXTERNAL SASL authentication,
|
||||
applying a client certificate configured using system properties for authentication.</para>
|
||||
<para>
|
||||
Spring LDAP provides two different configuration options for LDAP servers requiring TLS secure
|
||||
channel communication: <literal>DefaultTlsDirContextAuthenticationStrategy</literal> and
|
||||
<literal>ExternalTlsDirContextAuthenticationStrategy</literal>. Both these
|
||||
implementations will negotiate a TLS channel on the target connection, but they differ in the actual authentication mechanism.
|
||||
Whereas the <literal>DefaultTlsDirContextAuthenticationStrategy</literal> will apply SIMPLE authentication
|
||||
on the secure channel (using the specified <literal>userDn</literal> and <literal>password</literal>),
|
||||
the <literal>ExternalDirContextAuthenticationStrategy</literal> will use EXTERNAL SASL authentication,
|
||||
applying a client certificate configured using system properties for authentication.
|
||||
</para>
|
||||
|
||||
<para>Since different LDAP server implementations respond differently to explicit shutdown of the
|
||||
TLS channel (some servers require the connection be shutdown gracefully; others do not support it),
|
||||
the TLS <literal>DirContextAuthenticationStrategy</literal> implementations support specifying
|
||||
the shutdown behavior using the <literal>shutdownTlsGracefully</literal> parameter. If this
|
||||
property is set to <literal>false</literal> (the default), no explicit TLS shutdown will happen;
|
||||
if it is <literal>true</literal>, Spring LDAP will try to shutdown the TLS channel gracefully
|
||||
before closing the target context.</para>
|
||||
<para>
|
||||
Since different LDAP server implementations respond differently to explicit shutdown of the
|
||||
TLS channel (some servers require the connection be shutdown gracefully; others do not support it),
|
||||
the TLS <literal>DirContextAuthenticationStrategy</literal> implementations support specifying
|
||||
the shutdown behavior using the <literal>shutdownTlsGracefully</literal> parameter. If this
|
||||
property is set to <literal>false</literal> (the default), no explicit TLS shutdown will happen;
|
||||
if it is <literal>true</literal>, Spring LDAP will try to shutdown the TLS channel gracefully
|
||||
before closing the target context.
|
||||
</para>
|
||||
|
||||
<para><note><para>When working with TLS connections you need to make sure that the native LDAP
|
||||
Pooling functionality is turned off. As of release 1.3, the default setting is off. For earlier
|
||||
versions, simply set the <literal>pooled</literal> property to <literal>false</literal>. This is
|
||||
particularly important if <literal>shutdownTlsGracefully</literal> is set to <literal>false</literal>.
|
||||
However, since the TLS channel negotiation process is quite expensive, great performance benefits will
|
||||
be gained by using the Spring LDAP Pooling Support, described in <xref linkend="pooling" />.
|
||||
</para></note></para>
|
||||
<note>
|
||||
<para>
|
||||
When working with TLS connections you need to make sure that the native LDAP
|
||||
Pooling functionality (as specified using the <literal>native-pooling</literal> attribute
|
||||
is turned off. This is particularly important if <literal>shutdownTlsGracefully</literal>
|
||||
is set to <literal>false</literal>. However, since the TLS channel negotiation process is
|
||||
quite expensive, great performance benefits will be gained by using the Spring LDAP
|
||||
Pooling Support, described in <xref linkend="pooling" />.
|
||||
</para>
|
||||
</note>
|
||||
</sect4>
|
||||
</sect3>
|
||||
<sect3>
|
||||
<title>Custom Principal and Credentials Management</title>
|
||||
|
||||
<para>While the user name (i.e. user DN) and password used for
|
||||
creating an authenticated <literal>Context</literal> are static by
|
||||
default - the ones set on the <literal>ContextSource</literal> on
|
||||
startup will be used throughout the lifetime of the
|
||||
<literal>ContextSource</literal> - there are however several cases in
|
||||
which this is not the desired behaviour. A common scenario is that the
|
||||
principal and credentials of the current user should be used when
|
||||
executing LDAP operations for that user. The default behaviour can be
|
||||
modified by supplying a custom <literal>AuthenticationSource</literal>
|
||||
implementation to the <literal>ContextSource</literal> on startup,
|
||||
instead of explicitly specifying the <literal>userDn</literal> and
|
||||
<literal>password</literal>. The
|
||||
<literal>AuthenticationSource</literal> will be queried by the
|
||||
<literal>ContextSource</literal> for principal and credentials each
|
||||
time an authenticated <literal>Context</literal> is to be
|
||||
created.</para>
|
||||
<para>
|
||||
While the user name (i.e. user DN) and password used for
|
||||
creating an authenticated <literal>Context</literal> are statically defined by
|
||||
default - the ones defined in the <literal>context-source</literal> element
|
||||
configuration will be used throughout the lifetime of the
|
||||
<literal>ContextSource</literal> - there are several cases where this is not the desired behaviour.
|
||||
A common scenario is that the principal and credentials of the current user should be used when
|
||||
executing LDAP operations for that user. The default behaviour can be
|
||||
modified by supplying a reference to an <literal>AuthenticationSource</literal>
|
||||
implementation to the <literal>context-source</literal> element using the
|
||||
<literal>authentication-source-ref</literal> element,
|
||||
instead of explicitly specifying the <literal>username</literal> and
|
||||
<literal>password</literal>. The
|
||||
<literal>AuthenticationSource</literal> will be queried by the
|
||||
<literal>ContextSource</literal> for principal and credentials each
|
||||
time an authenticated <literal>Context</literal> is to be
|
||||
created.
|
||||
</para>
|
||||
|
||||
<para>If you are using <ulink url="http://springsecurity.org">Spring Security</ulink>
|
||||
you can make sure the principal and credentials of the currently logged in user
|
||||
is used at all times by configuring your <literal>ContextSource</literal>
|
||||
with an instance of the <literal>SpringSecurityAuthenticationSource</literal>
|
||||
shipped with Spring Security.</para>
|
||||
<para>
|
||||
If you are using <ulink url="http://springsecurity.org">Spring Security</ulink>
|
||||
you can make sure the principal and credentials of the currently logged in user
|
||||
is used at all times by configuring your <literal>ContextSource</literal>
|
||||
with an instance of the <literal>SpringSecurityAuthenticationSource</literal>
|
||||
shipped with Spring Security.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>The Spring bean definition for a
|
||||
SpringSecurityAuthenticationSource</title>
|
||||
<title>Using the <literal>SpringSecurityAuthenticationSource</literal></title>
|
||||
|
||||
<programlisting><beans>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="authenticationSource" ref="springSecurityAuthenticationSource" />
|
||||
</bean>
|
||||
<programlisting><![CDATA[
|
||||
<beans>
|
||||
...
|
||||
<ldap:context-source
|
||||
url="ldap://localhost:389"
|
||||
authentication-source-ref="springSecurityAuthenticationSource/>
|
||||
|
||||
<bean id="springSecurityAuthenticationSource"
|
||||
class="org.springframework.security.ldap.SpringSecurityAuthenticationSource" />
|
||||
...
|
||||
</beans></programlisting>
|
||||
<bean id="springSecurityAuthenticationSource"
|
||||
class="org.springframework.security.ldap.SpringSecurityAuthenticationSource" />
|
||||
...
|
||||
</beans>]]></programlisting>
|
||||
</example>
|
||||
|
||||
<note>
|
||||
<para>We don't specify any <literal>userDn</literal> or
|
||||
<literal>password</literal> to our <literal>ContextSource</literal>
|
||||
when using an <literal>AuthenticationSource</literal> - these
|
||||
properties are needed only when the default behaviour is
|
||||
used.</para>
|
||||
<para>
|
||||
We don't specify any <literal>username</literal> or
|
||||
<literal>password</literal> to our <literal>context-source</literal>
|
||||
when using an <literal>AuthenticationSource</literal> - these
|
||||
properties are needed only when the default behaviour is
|
||||
used.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<note>
|
||||
<para>When using the <literal>SpringSecurityAuthenticationSource</literal>
|
||||
you need to use Spring Security's
|
||||
<literal>LdapAuthenticationProvider</literal> to authenticate the
|
||||
users against LDAP.</para>
|
||||
<para>
|
||||
When using the <literal>SpringSecurityAuthenticationSource</literal>
|
||||
you need to use Spring Security's <literal>LdapAuthenticationProvider</literal> to authenticate the
|
||||
users against LDAP.
|
||||
</para>
|
||||
</note>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Default Authentication</title>
|
||||
|
||||
<para>When using <literal>SpringSecurityAuthenticationSource</literal>,
|
||||
authenticated contexts will only be possible to create once the user
|
||||
is logged in using Spring Security. To use default authentication information
|
||||
when no user is logged in, use the
|
||||
<literal>DefaultValuesAuthenticationSourceDecorator</literal>:</para>
|
||||
|
||||
<example>
|
||||
<title>Configuring a
|
||||
DefaultValuesAuthenticationSourceDecorator</title>
|
||||
|
||||
<programlisting><beans>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="authenticationSource" ref="authenticationSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="authenticationSource"
|
||||
class="org.springframework.ldap.authentication.DefaultValuesAuthenticationSourceDecorator">
|
||||
<property name="target" ref="springSecurityAuthenticationSource" />
|
||||
<property name="defaultUser" value="cn=myDefaultUser" />
|
||||
<property name="defaultPassword" value="pass" />
|
||||
</bean>
|
||||
|
||||
<bean id="springSecurityAuthenticationSource"
|
||||
class="org.springframework.security.ldap.SpringSecurityAuthenticationSource" />
|
||||
...
|
||||
</beans></programlisting>
|
||||
</example>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="context-source-pooling">
|
||||
@@ -208,7 +365,7 @@
|
||||
This LDAP connection pooling can be turned on/off using the
|
||||
<literal>pooled</literal> flag on <literal>AbstractContextSource</literal>.
|
||||
The default value is <literal>false</literal> (since release 1.3), i.e. the native
|
||||
Java LDAP pooling will be turned on. The configuration of LDAP connection pooling is managed using
|
||||
Java LDAP pooling will be turned off. The configuration of LDAP connection pooling is managed using
|
||||
<literal>System</literal> properties, so this needs to be handled
|
||||
manually, outside of the Spring Context configuration. Details of the native pooling configuration
|
||||
can be found <ulink url="http://java.sun.com/products/jndi/tutorial/ldap/connect/config.html">here</ulink>.
|
||||
@@ -226,64 +383,166 @@
|
||||
</sect2>
|
||||
<sect2 id="context-source-advanced">
|
||||
<title>Advanced ContextSource Configuration</title>
|
||||
<sect3 id="context-source-context-factory">
|
||||
<title>Alternate ContextFactory</title>
|
||||
|
||||
<para>It is possible to configure the <literal>ContextFactory</literal> that the
|
||||
<literal>ContextSource</literal> is to use when creating Contexts using the
|
||||
<literal>contextFactory</literal> property. The default value is
|
||||
<literal>com.sun.jndi.ldap.LdapCtxFactory</literal>.</para>
|
||||
</sect3>
|
||||
<sect3 id="context-source-object-factory">
|
||||
<title>Custom DirObjectFactory</title>
|
||||
|
||||
<para>As described in <xref linkend="dirobjectfactory" />, a <literal>DirObjectFactory</literal>
|
||||
can be used to translate the <literal>Attributes</literal> of found Contexts
|
||||
to a more useful <literal>DirContext</literal> implementation. This can be
|
||||
configured using the <literal>dirObjectFactory</literal> property. You can use
|
||||
this property if you have your own, custom <literal>DirObjectFactory</literal> implementation.</para>
|
||||
<para>The default value is <literal>DefaultDirObjectFactory</literal>.</para>
|
||||
</sect3>
|
||||
<sect3 id="context-source-custom-env-properties">
|
||||
<title>Custom DirContext Environment Properties</title>
|
||||
|
||||
<para>In some cases the user might want to specify additional environment setup properties
|
||||
in addition to the ones directly configurable from <literal>AbstractContextSource</literal>.
|
||||
Such properties should be set in a <literal>Map</literal> and supplied to
|
||||
the <literal>baseEnvironmentProperties</literal> property.</para>
|
||||
<para>
|
||||
In some cases the user might want to specify additional environment setup properties
|
||||
in addition to the ones directly configurable on <literal>context-source</literal>.
|
||||
Such properties should be set in a <literal>Map</literal> and referenced in
|
||||
the <literal>base-env-props-ref</literal> attribute.</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="ldap-template-configuration">
|
||||
<title>LdapTemplate Configuration</title>
|
||||
|
||||
<sect2 id="ldap-template-ignore-partial-result">
|
||||
<title>Ignoring PartialResultExceptions</title>
|
||||
|
||||
<para>Some Active Directory (AD) servers are unable to automatically following
|
||||
referrals, which often leads to a <literal>PartialResultException</literal> being
|
||||
thrown in searches. You can specify that <literal>PartialResultException</literal>
|
||||
is to be ignored by setting the <literal>ignorePartialResultException</literal>
|
||||
property to <literal>true</literal>.
|
||||
<note>This causes all referrals to be ignored, and no notice will be given that
|
||||
a <literal>PartialResultException</literal> has been encountered.
|
||||
There is currently no way of manually following referrals using LdapTemplate.</note></para>
|
||||
</sect2>
|
||||
<para>
|
||||
The <literal>LdapTemplate</literal> is defined using a <literal><ldap:ldap-template></literal>
|
||||
tag. The simplest possible <literal>ldap-template</literal> declaration is the simple tag:
|
||||
<example>
|
||||
<title>Simplest possible ldap-template declaration</title>
|
||||
<programlisting><![CDATA[
|
||||
<ldap:ldap-template />]]></programlisting>
|
||||
</example>
|
||||
This will create an <literal>LdapTemplate</literal> instance with the default id, referencing the
|
||||
default <literal>ContextSource</literal>, which is expected to have the id <literal>contextSource</literal>
|
||||
(the default for the <literal>context-source</literal> element).
|
||||
</para>
|
||||
<para>
|
||||
The configurable attributes on <literal>ldap-template</literal> are as follows:
|
||||
</para>
|
||||
<table frame="all">
|
||||
<title>LdapTemplate Configuration Attributes</title>
|
||||
<tgroup align="left" cols="3" colsep="1" rowsep="1">
|
||||
<colspec colname="c1" />
|
||||
<colspec colname="c2" />
|
||||
<colspec colname="c3" />
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Attribute</entry>
|
||||
<entry>Default</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>id</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>ldapTemplate</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
The id of the created bean.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>context-source-ref</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>contextSource</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
Id of the ContextSource instance to use.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>count-limit</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>0</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
The default count limit for searches. 0 means no limit.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>time-limit</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>0</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
The default time limit for searches in milliseconds. 0 means no limit.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>search-scope</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>SUBTREE</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
The default search scope for searches.
|
||||
Valid values are:
|
||||
<itemizedlist>
|
||||
<listitem><literal>OBJECT</literal></listitem>
|
||||
<listitem><literal>ONELEVEL</literal></listitem>
|
||||
<listitem><literal>SUBTREE</literal></listitem>
|
||||
</itemizedlist>
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>ignore-name-not-found</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>false</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
Specifies whether NameNotFoundException should be ignored in searches. Setting this
|
||||
attribute to true will cause errors caused by invalid search base to be silently swallowed.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>ignore-partial-result</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
<literal>false</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
Specifies whether PartialResultException should be ignored in searches. Some LDAP servers
|
||||
have problems with referrals; these should normally be followed automatically, but if this
|
||||
doesn't work it will manifest itself with a PartialResultException. Setting this attribute
|
||||
to true presents a work-around to this problem.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>odm-ref</literal>
|
||||
</entry>
|
||||
<entry>
|
||||
</entry>
|
||||
<entry>
|
||||
Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="base-context-configuration">
|
||||
<title>Obtaining a reference to the base LDAP path</title>
|
||||
<para>As described above, a base LDAP path may be supplied to the <literal>ContextSource</literal>,
|
||||
specifying the root in the LDAP tree to which all operations will be relative. This means that
|
||||
you will only be working with relative distinguished names throughout your system, which is
|
||||
typically rather handy. There are however some cases in which you will need to have access
|
||||
to the base path in order to be able to construct full DNs, relative to the actual root of the LDAP tree.
|
||||
One example would be when working with LDAP groups (e.g. <literal>groupOfNames</literal> objectclass),
|
||||
in which case each group member attribute value will need to be the full DN of the referenced member.</para>
|
||||
<para>For that reason, Spring LDAP has a mechanism by which any Spring controlled bean may be supplied
|
||||
the base path on startup. For beans to be notified of the base path, two things need to be in place:
|
||||
First of all, the bean that wants the base path reference needs to implement the
|
||||
<literal>BaseLdapNameAware</literal> interface. Secondly, a <literal>BaseLdapPathBeanPostProcessor</literal>
|
||||
needs to be defined in the application context</para>
|
||||
<para>
|
||||
As described above, a base LDAP path may be supplied to the <literal>ContextSource</literal>,
|
||||
specifying the root in the LDAP tree to which all operations will be relative. This means that
|
||||
you will only be working with relative distinguished names throughout your system, which is
|
||||
typically rather handy. There are however some cases in which you will need to have access
|
||||
to the base path in order to be able to construct full DNs, relative to the actual root of the LDAP tree.
|
||||
One example would be when working with LDAP groups (e.g. <literal>groupOfNames</literal> objectclass),
|
||||
in which case each group member attribute value will need to be the full DN of the referenced member.</para>
|
||||
<para>For that reason, Spring LDAP has a mechanism by which any Spring controlled bean may be supplied
|
||||
the base path on startup. For beans to be notified of the base path, two things need to be in place:
|
||||
First of all, the bean that wants the base path reference needs to implement the
|
||||
<literal>BaseLdapNameAware</literal> interface. Secondly, a <literal>BaseLdapPathBeanPostProcessor</literal>
|
||||
needs to be defined in the application context
|
||||
</para>
|
||||
<example>
|
||||
<title>Implementing <literal>BaseLdapNameAware</literal></title>
|
||||
<programlisting>package com.example.service;
|
||||
@@ -308,20 +567,21 @@ public class PersonService implements PersonService, <emphasis role="bold">BaseL
|
||||
|
||||
<programlisting><beans>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="authenticationSource" ref="authenticationSource" />
|
||||
</bean>
|
||||
<ldap:context-source
|
||||
username="cn=Administrator"
|
||||
password="secret"
|
||||
url="ldap://localhost:389"
|
||||
base="dc=261consulting,dc=com" />
|
||||
...
|
||||
<emphasis role="bold"><bean class="org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor" /></emphasis>
|
||||
</beans>
|
||||
</programlisting>
|
||||
</example>
|
||||
<para>The default behaviour of the <literal>BaseLdapPathBeanPostProcessor</literal> is to use the base path of the single
|
||||
defined <literal>BaseLdapPathSource</literal> (<literal>AbstractContextSource</literal> )in the <literal>ApplicationContext</literal>.
|
||||
If more than one <literal>BaseLdapPathSource</literal> is defined, you will need to specify which one to use with the
|
||||
<literal>baseLdapPathSourceName</literal> property.
|
||||
<para>
|
||||
The default behaviour of the <literal>BaseLdapPathBeanPostProcessor</literal> is to use the base path of the single
|
||||
defined <literal>BaseLdapPathSource</literal> (<literal>AbstractContextSource</literal>)in the <literal>ApplicationContext</literal>.
|
||||
If more than one <literal>BaseLdapPathSource</literal> is defined, you will need to specify which one to use with the
|
||||
<literal>baseLdapPathSourceName</literal> property.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
@@ -41,15 +41,15 @@
|
||||
<xi:include href="overview.xml" />
|
||||
<xi:include href="basic.xml" />
|
||||
<xi:include href="dirobjectfactory.xml" />
|
||||
<xi:include href="odm.xml" />
|
||||
<xi:include href="advancedqueries.xml" />
|
||||
<xi:include href="configuration.xml" />
|
||||
<xi:include href="pooling.xml" />
|
||||
<xi:include href="executors.xml" />
|
||||
<xi:include href="contextprocessor.xml" />
|
||||
<xi:include href="transactions.xml" />
|
||||
<xi:include href="simple.xml" />
|
||||
<xi:include href="configuration.xml" />
|
||||
<xi:include href="pooling.xml" />
|
||||
<xi:include href="user-authentication.xml" />
|
||||
<xi:include href="ldif-parsing.xml" />
|
||||
<xi:include href="odm.xml" />
|
||||
<xi:include href="utilities.xml" />
|
||||
<xi:include href="simple.xml" />
|
||||
</book>
|
||||
|
||||
@@ -222,16 +222,12 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="ldapTemplate"
|
||||
class="org.springframework.ldap.core.LdapTemplate">
|
||||
<property name="objectDirectoryMapper">
|
||||
<bean class="org.springframework.ldap.odm.impl.DefaultObjectDirectoryMapper">
|
||||
<property name="converterManager" ref="converterManager" />
|
||||
</bean>
|
||||
</property>
|
||||
<!-- More configuration of LdapTemplate here -->
|
||||
<ldap:ldap-template id="ldapTemplate" odm-ref="odm" />
|
||||
<bean id="odm" class="org.springframework.ldap.odm.impl.DefaultObjectDirectoryMapper">
|
||||
<property name="converterManager" ref="converterManager" />
|
||||
</bean>
|
||||
|
||||
|
||||
</programlisting>
|
||||
</example>
|
||||
</sect1>
|
||||
|
||||
@@ -138,23 +138,32 @@ public class PersonDaoImpl implements PersonDao {
|
||||
defined in various ways, but the most common is through XML:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting><beans>
|
||||
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="userDn" value="cn=Manager" />
|
||||
<property name="password" value="secret" />
|
||||
</bean>
|
||||
<programlisting><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ldap="http://www.springframework.org/schema/ldap"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
|
||||
|
||||
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
|
||||
<constructor-arg ref="contextSource" />
|
||||
</bean>
|
||||
<ldap:context-source
|
||||
url="ldap://localhost:389"
|
||||
base="dc=example,dc=com"
|
||||
username="cn=Manager"
|
||||
password="secret" />
|
||||
|
||||
<bean id="personDao" class="com.example.dao.PersonDaoImpl">
|
||||
<property name="ldapTemplate" ref="ldapTemplate" />
|
||||
</bean>
|
||||
</beans></programlisting>
|
||||
<ldap:ldap-template id="ldapTemplate" />
|
||||
|
||||
<bean id="personDao" class="com.example.dao.PersonDaoImpl">
|
||||
<property name="ldapTemplate" ref="ldapTemplate" />
|
||||
</bean>
|
||||
</beans>
|
||||
]]></programlisting>
|
||||
</informalexample>
|
||||
<note>
|
||||
In order to use the custom XML namespace for configuring the Spring LDAP components
|
||||
you need to include references to this namespace in your XML declaration as in the example above.
|
||||
</note>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="introduction-packaging">
|
||||
@@ -244,6 +253,15 @@ public class PersonDaoImpl implements PersonDao {
|
||||
lots of compilation warnings, and you are obviously encouraged to take appropriate action to get rid
|
||||
of these warning.
|
||||
</listitem>
|
||||
<listitem>
|
||||
The ODM (Object-Directory Mapping) functionality has been moved to core and there are new methods
|
||||
in <literal>LdapOperations</literal>/<literal>LdapTemplate</literal> that uses this automatic
|
||||
translation to/from ODM-annotated classes. See <xref linkend="odm" /> for more information.
|
||||
</listitem>
|
||||
<listitem>
|
||||
A custom XML namespace is now provided to simplify configuration of Spring LDAP.
|
||||
See <xref linkend="configuration" /> for more information.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<literal>DistinguishedName</literal> and associated classes have been deprecated in favor of standard
|
||||
Java <literal>LdapName</literal>. See <xref linkend="ldap-names" /> for information on how the library
|
||||
|
||||
@@ -23,13 +23,10 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Pooling support is provided by
|
||||
<literal>PoolingContextSource</literal>
|
||||
which can wrap any
|
||||
<literal>ContextSource</literal>
|
||||
and pool both read-only and read-write
|
||||
<literal>DirContext</literal>
|
||||
objects.
|
||||
Pooling support is provided by supplying a <literal><ldap:pooling /></literal> sub-element
|
||||
to the <literal><ldap:context-source /></literal> element in the application context configuration.
|
||||
Read-only and read-write <literal>DirContext</literal> objects are pooled separately
|
||||
(if <literal>anonymous-read-only</literal> is specified.
|
||||
<ulink url="http://commons.apache.org/pool/index.html">
|
||||
Jakarta Commons-Pool
|
||||
</ulink>
|
||||
@@ -47,17 +44,12 @@
|
||||
<literal>DirContext</literal>
|
||||
connections to be checked to ensure they are still properly
|
||||
connected and configured when checking them out of the pool,
|
||||
in to the pool or while idle in the pool
|
||||
in to the pool or while idle in the pool.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The
|
||||
<literal>DirContextValidator</literal>
|
||||
interface is used by the
|
||||
<literal>PoolingContextSource</literal>
|
||||
for validation and
|
||||
<literal>DefaultDirContextValidator</literal>
|
||||
is provided as the default validation implementation.
|
||||
If connection validation is configured, pooled connections are validated using
|
||||
<literal>DefaultDirContextValidator</literal>.
|
||||
<literal>DefaultDirContextValidator</literal>
|
||||
does a
|
||||
<literal>
|
||||
@@ -75,12 +67,11 @@
|
||||
passes validation, if no results are returned or an
|
||||
exception is thrown the
|
||||
<literal>DirContext</literal>
|
||||
fails validation. The
|
||||
<literal>DefaultDirContextValidator</literal>
|
||||
fails validation. The default settings
|
||||
should work with no configuration changes on most LDAP
|
||||
servers and provide the fastest way to validate the
|
||||
<literal>DirContext</literal>
|
||||
.
|
||||
<literal>DirContext</literal>. If customization required this can be done using the validation
|
||||
configuration attributes, described below
|
||||
</para>
|
||||
<note>
|
||||
Connections will be automatically invalidated if they throw an exception that is considered
|
||||
@@ -94,21 +85,16 @@
|
||||
</sect1>
|
||||
|
||||
<sect1 id="pooling-properties">
|
||||
<title>Pool Properties</title>
|
||||
<title>Pool Configuration</title>
|
||||
|
||||
<para>
|
||||
The following properties are available on the
|
||||
<literal>PoolingContextSource</literal>
|
||||
for configuration of the DirContext pool. The
|
||||
<literal>contextSource</literal>
|
||||
property must be set and the
|
||||
<literal>dirContextValidator</literal>
|
||||
property must be set if validation is enabled, all other
|
||||
properties are optional.
|
||||
The following attributes are available on the
|
||||
<literal><ldap:pooling /></literal> element
|
||||
for configuration of the DirContext pool:
|
||||
</para>
|
||||
|
||||
<table frame="all">
|
||||
<title>Pooling Configuration Properties</title>
|
||||
<title>Pooling Configuration Attributes</title>
|
||||
<tgroup align="left" cols="3" colsep="1" rowsep="1">
|
||||
<colspec colname="c1" />
|
||||
|
||||
@@ -118,7 +104,7 @@
|
||||
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Parameter</entry>
|
||||
<entry>Attribute</entry>
|
||||
|
||||
<entry>Default</entry>
|
||||
|
||||
@@ -129,50 +115,7 @@
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>contextSource</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>null</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
The
|
||||
<literal>ContextSource</literal>
|
||||
implementation to get
|
||||
<literal>DirContext</literal>
|
||||
s from to populate the pool.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>dirContextValidator</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>null</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
The
|
||||
<literal>DirContextValidator</literal>
|
||||
implementation to use when validating
|
||||
connections. This is required if
|
||||
<literal>testOnBorrow</literal>
|
||||
,
|
||||
<literal>testOnReturn</literal>
|
||||
, or
|
||||
<literal>testWhileIdle</literal>
|
||||
options are set to
|
||||
<literal>true</literal>
|
||||
.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>maxActive</literal>
|
||||
<literal>max-active</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -189,7 +132,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>maxTotal</literal>
|
||||
<literal>max-total</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -206,7 +149,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>maxIdle</literal>
|
||||
<literal>max-idle</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -224,7 +167,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>minIdle</literal>
|
||||
<literal>min-idle</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -241,7 +184,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>maxWait</literal>
|
||||
<literal>max-wait</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -259,11 +202,11 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>whenExhaustedAction</literal>
|
||||
<literal>when-exhausted</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>1</literal> (BLOCK)
|
||||
<literal>BLOCK</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -272,9 +215,7 @@
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The
|
||||
FAIL (<literal>0</literal>)
|
||||
option will throw a
|
||||
The <literal>FAIL</literal> option will throw a
|
||||
<literal>
|
||||
NoSuchElementException
|
||||
</literal>
|
||||
@@ -284,29 +225,27 @@
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The
|
||||
BLOCK (<literal>1</literal>)
|
||||
The <literal>BLOCK</literal>
|
||||
option will wait until a new
|
||||
object is available. If
|
||||
<literal>maxWait</literal>
|
||||
<literal>max-wait</literal>
|
||||
is positive a
|
||||
<literal>
|
||||
NoSuchElementException
|
||||
</literal>
|
||||
is thrown if no new object is
|
||||
available after the
|
||||
<literal>maxWait</literal>
|
||||
<literal>max-wait</literal>
|
||||
time expires.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The
|
||||
GROW (<literal>2</literal>)
|
||||
The <literal>GROW</literal>
|
||||
option will create and return a
|
||||
new object (essentially making
|
||||
<literal>maxActive</literal>
|
||||
<literal>max-active</literal>
|
||||
meaningless).
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -316,7 +255,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>testOnBorrow</literal>
|
||||
<literal>test-on-borrow</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -334,7 +273,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>testOnReturn</literal>
|
||||
<literal>test-on-return</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -349,7 +288,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>testWhileIdle</literal>
|
||||
<literal>test-while-idle</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -367,7 +306,7 @@
|
||||
<row>
|
||||
<entry>
|
||||
<literal>
|
||||
timeBetweenEvictionRunsMillis
|
||||
eviction-run-interval-millis
|
||||
</literal>
|
||||
</entry>
|
||||
|
||||
@@ -385,7 +324,7 @@
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>numTestsPerEvictionRun</literal>
|
||||
<literal>tests-per-eviction-run</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
@@ -402,7 +341,7 @@
|
||||
<row>
|
||||
<entry>
|
||||
<literal>
|
||||
minEvictableIdleTimeMillis
|
||||
min-evictable-time-millis
|
||||
</literal>
|
||||
</entry>
|
||||
|
||||
@@ -420,7 +359,58 @@
|
||||
<row>
|
||||
<entry>
|
||||
<literal>
|
||||
nonTransientExceptions
|
||||
validation-query-base
|
||||
</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>LdapUtils.emptyName()</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
The search base to be used when validating connections. Only used if
|
||||
<literal>test-on-borrow</literal>, <literal>test-on-return</literal>,
|
||||
or <literal>test-while-idle</literal> is specified
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>
|
||||
validation-query-filter
|
||||
</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>objectclass=*</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
The search filter to be used when validating connections. Only used if
|
||||
<literal>test-on-borrow</literal>, <literal>test-on-return</literal>,
|
||||
or <literal>test-while-idle</literal> is specified
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>
|
||||
validation-query-search-controls-ref
|
||||
</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>null</literal>; default search control settings are described above.
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
Id of a SearchControls instance to be used when validating connections. Only used if
|
||||
<literal>test-on-borrow</literal>, <literal>test-on-return</literal>,
|
||||
or <literal>test-while-idle</literal> is specified
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>
|
||||
non-transient-exceptions
|
||||
</literal>
|
||||
</entry>
|
||||
|
||||
@@ -429,8 +419,8 @@
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
The Exceptions that should be considered non-transient with
|
||||
regards to eager invalidation. Should any of the listed exceptions
|
||||
Comma-separated list of Exception classes. The listed exceptions will be considered
|
||||
non-transient with regards to eager invalidation. Should any of the listed exceptions
|
||||
(or subclasses of them) be thrown by a call to a
|
||||
pooled <literal>DirContext</literal> instance, that object will be
|
||||
automatically invalidated without any additional testOnReturn operation.
|
||||
@@ -456,17 +446,10 @@
|
||||
<programlisting><![CDATA[
|
||||
<beans>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
|
||||
<property name="contextSource" ref="contextSourceTarget" />
|
||||
</bean>
|
||||
|
||||
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="userDn" value="cn=Manager" />
|
||||
<property name="password" value="secret" />
|
||||
<property name="pooled" value="false"/>
|
||||
</bean>
|
||||
<ldap:context-source
|
||||
password="secret" url="ldap://localhost:389" username="cn=Manager">
|
||||
<ldap:pooling />
|
||||
</ldap:context-source>
|
||||
...
|
||||
</beans>
|
||||
]]></programlisting>
|
||||
@@ -474,37 +457,6 @@
|
||||
In a real world example you would probably configure the
|
||||
pool options and enable connection validation; the above
|
||||
serves as an example to demonstrate the general idea.
|
||||
<note>
|
||||
<para>
|
||||
Ensure that the
|
||||
<literal>pooled</literal>
|
||||
property is set to
|
||||
<literal>false</literal>
|
||||
on any
|
||||
<literal>ContextSource</literal>
|
||||
that will be wrapped in a
|
||||
<literal>PoolingContextSource</literal>
|
||||
. The
|
||||
<literal>PoolingContextSource</literal>
|
||||
must be able to create new connections when needed
|
||||
and if
|
||||
<literal>pooled</literal>
|
||||
is set to
|
||||
<literal>true</literal>
|
||||
that may not be possible.
|
||||
</para>
|
||||
</note>
|
||||
<note>
|
||||
<para>
|
||||
You'll notice that the actual
|
||||
<literal>ContextSource</literal>
|
||||
gets an id with a "Target" suffix. The bean you will
|
||||
actually refer to is the
|
||||
<literal>PoolingContextSource</literal>
|
||||
that wraps the target
|
||||
<literal>contextSource</literal>
|
||||
</para>
|
||||
</note>
|
||||
</para>
|
||||
|
||||
<sect2 id="pooling-advanced-configuration">
|
||||
@@ -520,23 +472,12 @@
|
||||
<programlisting><![CDATA[
|
||||
<beans>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
|
||||
<property name="contextSource" ref="contextSourceTarget" />
|
||||
<property name="dirContextValidator" ref="dirContextValidator" />
|
||||
<property name="testOnBorrow" value="true" />
|
||||
<property name="testWhileIdle" value="true" />
|
||||
</bean>
|
||||
|
||||
<bean id="dirContextValidator"
|
||||
class="org.springframework.ldap.pool.validation.DefaultDirContextValidator" />
|
||||
|
||||
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="userDn" value="cn=Manager" />
|
||||
<property name="password" value="secret" />
|
||||
<property name="pooled" value="false"/>
|
||||
</bean>
|
||||
<ldap:context-source
|
||||
username="cn=Manager" password="secret" url="ldap://localhost:389" >
|
||||
<ldap:pooling
|
||||
test-on-borrow="true"
|
||||
test-while-idle="true" />
|
||||
</ldap:context-source>
|
||||
...
|
||||
</beans>
|
||||
]]></programlisting>
|
||||
@@ -544,8 +485,7 @@
|
||||
The above example will test each
|
||||
<literal>DirContext</literal>
|
||||
before it is passed to the client application and test
|
||||
<literal>DirContext</literal>
|
||||
s that have been sitting idle in the pool.
|
||||
<literal>DirContext</literal>s that have been sitting idle in the pool.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
@@ -43,74 +43,83 @@
|
||||
Configuring Spring LDAP transactions should look very familiar if you're used to configuring Spring transactions.
|
||||
You will annotate your transacted classes with <literal>@Transactional</literal>, create a
|
||||
<literal>TransactionManager</literal> instance and include a <literal><tx:annotation-driven></literal>
|
||||
tag in your bean configuraion. In addition to this, you will also need to wrap your <literal>ContextSource</literal>
|
||||
in a <literal>TransactionAwareContextSourceProxy</literal>.
|
||||
tag in your bean configuraion.
|
||||
<informalexample>
|
||||
<programlisting><beans>
|
||||
<programlisting>
|
||||
<beans>
|
||||
...
|
||||
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="userDn" value="cn=Manager" />
|
||||
<property name="password" value="secret" />
|
||||
</bean>
|
||||
<ldap:context-source
|
||||
url="ldap://localhost:389"
|
||||
base="dc=example,dc=com"
|
||||
username="cn=Manager"
|
||||
password="secret" />
|
||||
|
||||
<bean id="contextSource"
|
||||
class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy">
|
||||
<constructor-arg ref="contextSourceTarget" />
|
||||
</bean>
|
||||
|
||||
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
|
||||
<constructor-arg ref="contextSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager"
|
||||
class="org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager">
|
||||
<property name="contextSource" ref="contextSource" />
|
||||
<property name="renamingStrategy">
|
||||
<ldap:ldap-template id="ldapTemplate" />
|
||||
<ldap:transaction-manager>
|
||||
<!--
|
||||
Note this default configuration will not work for more complex scenarios, see below for more information on RenamingStrategies.
|
||||
Note this default configuration will not work for more complex scenarios, see below for more information on RenamingStrategies.
|
||||
-->
|
||||
<bean class="org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy" />
|
||||
</property>
|
||||
|
||||
</bean>
|
||||
<ldap:default-renaming-strategy />
|
||||
</ldap:transaction-manager>
|
||||
|
||||
<!--
|
||||
The MyDataAccessObject class is annotated with <literal>@Transactional</literal>.
|
||||
-->
|
||||
<bean id="myDataAccessObject" class="com.example.MyDataAccessObject">
|
||||
<property name="ldapTemplate" ref="ldapTemplate" />
|
||||
</bean>
|
||||
|
||||
<tx:annotation-driven />
|
||||
|
||||
...</programlisting>
|
||||
<note>While the this setup will work fine for most simple use cases, some more complex scenarios will
|
||||
<note>While this setup will work fine for most simple use cases, some more complex scenarios will
|
||||
require additional configuration; more specifically if you will be creating or deleting subtrees within
|
||||
transactions, you will need to use an alternative <literal>TempEntryRenamingStrategy</literal>, as described
|
||||
in <xref linkend="renaming-strategies"/> below</note>
|
||||
</informalexample>
|
||||
In a real world example you would probably apply the transactions on the service object level
|
||||
rather than the DAO level; the above serves as an example to demonstrate the general idea.
|
||||
<note>You'll notice that the actual <literal>ContextSource</literal> instance gets an id with a
|
||||
"Target" suffix. The bean you will actually refer to is the Proxy that are created
|
||||
around the target; <literal>contextSource</literal>.</note>
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="jdbc-transaction-integration">
|
||||
<title>JDBC Transaction Integration</title>
|
||||
<para>A common use case when working against LDAP is that some of the data is stored in the LDAP tree, but
|
||||
other data is stored in a relational database. In this case, transaction support becomes even more important,
|
||||
since the update of the different resources should be synchronized.</para>
|
||||
<para>While actual XA transactions is not supported, support is provided to conceptually wrap JDBC and LDAP
|
||||
access within the same transaction using the <literal>ContextSourceAndDataSourceTransactionManager</literal>.
|
||||
A <literal>DataSource</literal> and a <literal>ContextSource</literal> is supplied to the
|
||||
<literal>ContextSourceAndDataSourceTransactionManager</literal>, which will then manage the two transactions,
|
||||
virtually as if they were one. When performing a commit, the LDAP part of the operation will always
|
||||
be performed first, allowing both transactions to be rolled back should the LDAP commit fail. The JDBC
|
||||
part of the transaction is managed exactly as in <literal>DataSourceTransactionManager</literal>, except that
|
||||
nested transactions is not supported.
|
||||
<note>Once again it should be noted that the provided support is all client side. The wrapped transaction is not
|
||||
an XA transaction. No two-phase as such commit is performed, as the LDAP server will be unable to vote on its outcome.
|
||||
Once again, however, for the majority of cases the supplied support will be sufficient.</note></para>
|
||||
<para>
|
||||
A common use case when working against LDAP is that some of the data is stored in the LDAP tree, but
|
||||
other data is stored in a relational database. In this case, transaction support becomes even more important,
|
||||
since the update of the different resources should be synchronized.
|
||||
</para>
|
||||
<para>
|
||||
While actual XA transactions is not supported, support is provided to conceptually wrap JDBC and LDAP
|
||||
access within the same transaction by supplying a <literal>data-source-ref</literal> attribute to the
|
||||
<literal><ldap:transaction-manager></literal> tag.
|
||||
This will create a <literal>ContextSourceAndDataSourceTransactionManager</literal>,
|
||||
which will then manage the two transactions, virtually as if they were one. When performing a commit,
|
||||
the LDAP part of the operation will always be performed first, allowing both transactions to be rolled
|
||||
back should the LDAP commit fail. The JDBC part of the transaction is managed exactly as in
|
||||
<literal>DataSourceTransactionManager</literal>, except that nested transactions is not supported:
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<ldap:transaction-manager data-source-ref="dataSource" >
|
||||
<ldap:default-renaming-strategy />
|
||||
<ldap:transaction-manager />
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
<note>
|
||||
Once again it should be noted that the provided support is all client side. The wrapped transaction is not
|
||||
an XA transaction. No two-phase as such commit is performed, as the LDAP server will be unable to vote on its outcome.
|
||||
Once again, however, for the majority of cases the supplied support will be sufficient.
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
The same thing can be accomplished for Hibernate integration by supplying a <literal>session-factory-ref</literal>
|
||||
attribute to the <literal><ldap:transaction-manager></literal> tag.
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<ldap:transaction-manager session-factory-ref="dataSource" >
|
||||
<ldap:default-renaming-strategy />
|
||||
<ldap:transaction-manager />
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="compensating-transactions-explained">
|
||||
<title>LDAP Compensating Transactions Explained</title>
|
||||
@@ -189,23 +198,35 @@
|
||||
javadocs.</para>
|
||||
<sect2 id="renaming-strategies">
|
||||
<title>Renaming Strategies</title>
|
||||
As described in the table above, the transaction management of some operations require the original entry affected
|
||||
by the operation to be temporarily renamed before the actual modification can be made in the commit.
|
||||
The manner in which the temporary DN of the entry is calculated is managed by a <literal>TempEntryRenamingStrategy</literal>
|
||||
supplied to the <literal>ContextSourceTransactionManager</literal>. Two implementations are supplied with Spring LDAP,
|
||||
but if specific behaviour is required a custom implementation can easily be implemented by the user. The
|
||||
provided <literal>TempEntryRenamingStrategy</literal> implementations are:
|
||||
<para>
|
||||
As described in the table above, the transaction management of some operations require the original entry affected
|
||||
by the operation to be temporarily renamed before the actual modification can be made in the commit.
|
||||
The manner in which the temporary DN of the entry is calculated is managed by a <literal>TempEntryRenamingStrategy</literal>
|
||||
specified in a sub-element to the <literal><ldap:transaction-manager ></literal> declaration
|
||||
in the configuration. Two implementations are supplied with Spring LDAP:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem><para><literal>DefaultTempEntryRenamingStrategy</literal> (the default). Adds a suffix to the least significant
|
||||
part of the entry DN. E.g. for the DN <literal>cn=john doe, ou=users</literal>, this strategy would return the
|
||||
temporary DN <literal>cn=john doe_temp, ou=users</literal>. The suffix is configurable using the <literal>tempSuffix</literal>
|
||||
property</para></listitem>
|
||||
<listitem><para><literal>DifferentSubtreeTempEntryRenamingStrategy</literal>. Takes the least significant part of the DN
|
||||
and appends a subtree DN to this. This makes all temporary entries be placed at a specific location in the LDAP tree.
|
||||
The temporary subtree DN is configured using the <literal>subtreeNode</literal> property. E.g., if
|
||||
<literal>subtreeNode</literal> is <literal>ou=tempEntries</literal> and the original DN of the entry is
|
||||
<literal>cn=john doe, ou=users</literal>, the temporary DN will be <literal>cn=john doe, ou=tempEntries</literal>.
|
||||
Note that the configured subtree node needs to be present in the LDAP tree.</para></listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>DefaultTempEntryRenamingStrategy</literal> (the default). Specified using a
|
||||
<literal><ldap:default-renaming-strategy /></literal> element. Adds a suffix to the least significant
|
||||
part of the entry DN. E.g. for the DN <literal>cn=john doe, ou=users</literal>, this strategy would return the
|
||||
temporary DN <literal>cn=john doe_temp, ou=users</literal>.
|
||||
The suffix is configurable using the <literal>temp-suffix</literal> attribute.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>DifferentSubtreeTempEntryRenamingStrategy</literal>. Specified using a
|
||||
<literal><ldap:different-subtree-renaming-strategy /></literal> element.
|
||||
Takes the least significant part of the DN and appends a subtree DN to this.
|
||||
This makes all temporary entries be placed at a specific location in the LDAP tree.
|
||||
The temporary subtree DN is configured using the <literal>subtree-node</literal> attribute. E.g., if
|
||||
<literal>subtree-node</literal> is <literal>ou=tempEntries</literal> and the original DN of the entry is
|
||||
<literal>cn=john doe, ou=users</literal>, the temporary DN will be <literal>cn=john doe, ou=tempEntries</literal>.
|
||||
Note that the configured subtree node needs to be present in the LDAP tree.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<note>
|
||||
There are some situations where the <literal>DefaultTempEntryRenamingStrategy</literal> will not work. E.g. if your are planning
|
||||
|
||||
Reference in New Issue
Block a user