LDAP-267: Minor tweaks to XML namespace support more tests and reference documentation.

This commit is contained in:
Mattias Hellborg Arthursson
2013-10-08 17:02:40 +02:00
parent 795701fd58
commit a16646ca2c
24 changed files with 1034 additions and 523 deletions

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -665,6 +665,5 @@ public abstract class AbstractContextSource implements BaseLdapPathContextSource
public String getCredentials() {
return password;
}
}
}

View File

@@ -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() {

View File

@@ -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">

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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");
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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>

View 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>

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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>
&lt;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>&gt;
</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>&lt;ldap:context-source&gt;</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>&lt;beans&gt;
...
&lt;bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"&gt;
&lt;property name="url" value="ldap://localhost:389" /&gt;
&lt;property name="base" value="dc=example,dc=com" /&gt;
&lt;property name="authenticationSource" ref="springSecurityAuthenticationSource" /&gt;
&lt;/bean&gt;
<programlisting><![CDATA[
<beans>
...
<ldap:context-source
url="ldap://localhost:389"
authentication-source-ref="springSecurityAuthenticationSource/>
&lt;bean id="springSecurityAuthenticationSource"
class="org.springframework.security.ldap.SpringSecurityAuthenticationSource" /&gt;
...
&lt;/beans&gt;</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>&lt;beans&gt;
...
&lt;bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"&gt;
&lt;property name="url" value="ldap://localhost:389" /&gt;
&lt;property name="base" value="dc=example,dc=com" /&gt;
&lt;property name="authenticationSource" ref="authenticationSource" /&gt;
&lt;/bean&gt;
&lt;bean id="authenticationSource"
class="org.springframework.ldap.authentication.DefaultValuesAuthenticationSourceDecorator"&gt;
&lt;property name="target" ref="springSecurityAuthenticationSource" /&gt;
&lt;property name="defaultUser" value="cn=myDefaultUser" /&gt;
&lt;property name="defaultPassword" value="pass" /&gt;
&lt;/bean&gt;
&lt;bean id="springSecurityAuthenticationSource"
class="org.springframework.security.ldap.SpringSecurityAuthenticationSource" /&gt;
...
&lt;/beans&gt;</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>&lt;ldap:ldap-template&gt;</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>&lt;beans&gt;
...
&lt;bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"&gt;
&lt;property name="url" value="ldap://localhost:389" /&gt;
&lt;property name="base" value="dc=example,dc=com" /&gt;
&lt;property name="authenticationSource" ref="authenticationSource" /&gt;
&lt;/bean&gt;
&lt;ldap:context-source
username="cn=Administrator"
password="secret"
url="ldap://localhost:389"
base="dc=261consulting,dc=com" /&gt;
...
<emphasis role="bold">&lt;bean class="org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor" /&gt;</emphasis>
&lt;/beans&gt;
</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>

View File

@@ -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>

View File

@@ -222,16 +222,12 @@
&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="ldapTemplate"
class="org.springframework.ldap.core.LdapTemplate"&gt;
&lt;property name="objectDirectoryMapper"&gt;
&lt;bean class="org.springframework.ldap.odm.impl.DefaultObjectDirectoryMapper"&gt;
&lt;property name="converterManager" ref="converterManager" /&gt;
&lt;/bean&gt;
&lt;/property&gt;
&lt;!-- More configuration of LdapTemplate here --&gt;
&lt;ldap:ldap-template id="ldapTemplate" odm-ref="odm" /&gt;
&lt;bean id="odm" class="org.springframework.ldap.odm.impl.DefaultObjectDirectoryMapper"&gt;
&lt;property name="converterManager" ref="converterManager" /&gt;
&lt;/bean&gt;
</programlisting>
</example>
</sect1>

View File

@@ -138,23 +138,32 @@ public class PersonDaoImpl implements PersonDao {
defined in various ways, but the most common is through XML:</para>
<informalexample>
<programlisting>&lt;beans&gt;
&lt;bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"&gt;
&lt;property name="url" value="ldap://localhost:389" /&gt;
&lt;property name="base" value="dc=example,dc=com" /&gt;
&lt;property name="userDn" value="cn=Manager" /&gt;
&lt;property name="password" value="secret" /&gt;
&lt;/bean&gt;
<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">
&lt;bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate"&gt;
&lt;constructor-arg ref="contextSource" /&gt;
&lt;/bean&gt;
<ldap:context-source
url="ldap://localhost:389"
base="dc=example,dc=com"
username="cn=Manager"
password="secret" />
&lt;bean id="personDao" class="com.example.dao.PersonDaoImpl"&gt;
&lt;property name="ldapTemplate" ref="ldapTemplate" /&gt;
&lt;/bean&gt;
&lt;/beans&gt;</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

View File

@@ -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>&lt;ldap:pooling /&gt;</literal> sub-element
to the <literal>&lt;ldap:context-source /&gt;</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>&lt;ldap:pooling /&gt;</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>

View File

@@ -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>&lt;tx:annotation-driven&gt;</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>&lt;beans&gt;
<programlisting>
&lt;beans&gt;
...
&lt;bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource"&gt;
&lt;property name="url" value="ldap://localhost:389" /&gt;
&lt;property name="base" value="dc=example,dc=com" /&gt;
&lt;property name="userDn" value="cn=Manager" /&gt;
&lt;property name="password" value="secret" /&gt;
&lt;/bean&gt;
&lt;ldap:context-source
url="ldap://localhost:389"
base="dc=example,dc=com"
username="cn=Manager"
password="secret" /&gt;
&lt;bean id="contextSource"
class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy"&gt;
&lt;constructor-arg ref="contextSourceTarget" /&gt;
&lt;/bean&gt;
&lt;bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate"&gt;
&lt;constructor-arg ref="contextSource" /&gt;
&lt;/bean&gt;
&lt;bean id="transactionManager"
class="org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager"&gt;
&lt;property name="contextSource" ref="contextSource" /&gt;
&lt;property name="renamingStrategy"&gt;
&lt;ldap:ldap-template id="ldapTemplate" /&gt;
&lt;ldap:transaction-manager&gt;
&lt;!--
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.
--&gt;
&lt;bean class="org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy" /&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;ldap:default-renaming-strategy /&gt;
&lt;/ldap:transaction-manager&gt;
&lt;!--
The MyDataAccessObject class is annotated with <literal>@Transactional</literal>.
--&gt;
&lt;bean id="myDataAccessObject" class="com.example.MyDataAccessObject"&gt;
&lt;property name="ldapTemplate" ref="ldapTemplate" /&gt;
&lt;/bean&gt;
&lt;tx:annotation-driven /&gt;
...</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
&quot;Target&quot; 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>&lt;ldap:transaction-manager&gt;</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>
&lt;ldap:transaction-manager data-source-ref="dataSource" &gt;
&lt;ldap:default-renaming-strategy /&gt;
&lt;ldap:transaction-manager /&gt;
</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>&lt;ldap:transaction-manager&gt;</literal> tag.
<informalexample>
<programlisting>
&lt;ldap:transaction-manager session-factory-ref="dataSource" &gt;
&lt;ldap:default-renaming-strategy /&gt;
&lt;ldap:transaction-manager /&gt;
</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>&lt;ldap:transaction-manager &gt;</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>&lt;ldap:default-renaming-strategy /&gt;</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>&lt;ldap:different-subtree-renaming-strategy /&gt;</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