diff --git a/core/src/main/java/org/springframework/ldap/config/ContextSourceParser.java b/core/src/main/java/org/springframework/ldap/config/ContextSourceParser.java
new file mode 100644
index 00000000..06a46c29
--- /dev/null
+++ b/core/src/main/java/org/springframework/ldap/config/ContextSourceParser.java
@@ -0,0 +1,165 @@
+/*
+ * 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.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.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.StringUtils;
+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.getBoolean;
+import static org.springframework.ldap.config.ParserUtils.getInt;
+import static org.springframework.ldap.config.ParserUtils.getString;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+public class ContextSourceParser implements BeanDefinitionParser {
+ private final static String ATT_ANONYMOUS_READ_ONLY = "anonymous-read-only";
+ 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";
+
+ // pooling attributes
+ private final static String ATT_MAX_ACTIVE = "max-active";
+ private final static String ATT_MAX_TOTAL = "max-total";
+ private final static String ATT_MAX_IDLE = "max-idle";
+ private final static String ATT_MIN_IDLE = "min-idle";
+ private final static String ATT_MAX_WAIT = "max-wait";
+ private final static String ATT_WHEN_EXHAUSTED = "when-exhausted";
+ private final static String ATT_TEST_ON_BORROW = "test-on-borrow";
+ private final static String ATT_TEST_ON_RETURN = "test-on-return";
+ private final static String ATT_TEST_WHILE_IDLE = "test-while-idle";
+ private final static String ATT_EVICTION_RUN_MILLIS = "eviction-run-interval-millis";
+ private final static String ATT_TESTS_PER_EVICTION_RUN = "tests-per-eviction-run";
+ private final static String ATT_EVICTABLE_TIME_MILLIS = "min-evictable-time-millis";
+ 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_USERNAME = "username";
+ static final String DEFAULT_ID = "contextSource";
+
+ @Override
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LdapContextSource.class);
+
+ String username = element.getAttribute(ATT_USERNAME);
+ 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);
+ builder.addPropertyValue("password", password);
+ String[] urls = StringUtils.commaDelimitedListToStringArray(url);
+ builder.addPropertyValue("urls", urls);
+ 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));
+
+ String authStrategyRef = element.getAttribute(ATT_AUTHENTICATION_STRATEGY_REF);
+ if(StringUtils.hasText(authStrategyRef)) {
+ builder.addPropertyReference("authenticationStrategy", authStrategyRef);
+ }
+
+ BeanDefinition targetContextSourceDefinition = builder.getBeanDefinition();
+ targetContextSourceDefinition = applyPoolingIfApplicable(targetContextSourceDefinition, element);
+
+
+ BeanDefinitionBuilder proxyBuilder = BeanDefinitionBuilder.rootBeanDefinition(TransactionAwareContextSourceProxy.class);
+ proxyBuilder.addConstructorArgValue(targetContextSourceDefinition);
+ AbstractBeanDefinition proxyBeanDefinition = proxyBuilder.getBeanDefinition();
+
+ String id = getString(element, AbstractBeanDefinitionParser.ID_ATTRIBUTE, DEFAULT_ID);
+ parserContext.registerBeanComponent(new BeanComponentDefinition(proxyBeanDefinition, id));
+
+ return proxyBeanDefinition;
+ }
+
+ private BeanDefinition applyPoolingIfApplicable(BeanDefinition targetContextSourceDefinition, Element element) {
+ NodeList poolingChildren = element.getElementsByTagNameNS(NAMESPACE, Elements.POOLING);
+ if(poolingChildren.getLength() == 0) {
+ return targetContextSourceDefinition;
+ }
+
+ Element poolingElement = (Element) poolingChildren.item(0);
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PoolingContextSource.class);
+ builder.addPropertyValue("contextSource", targetContextSourceDefinition);
+
+ builder.addPropertyValue("maxActive", getInt(poolingElement, ATT_MAX_ACTIVE, 8));
+ builder.addPropertyValue("maxTotal", getInt(poolingElement, ATT_MAX_TOTAL, -1));
+ builder.addPropertyValue("maxIdle", getInt(poolingElement, ATT_MAX_IDLE, 8));
+ builder.addPropertyValue("minIdle", getInt(poolingElement, ATT_MIN_IDLE, 0));
+ builder.addPropertyValue("maxWait", getInt(poolingElement, ATT_MAX_WAIT, -1));
+ String whenExhausted = getString(poolingElement, ATT_WHEN_EXHAUSTED, PoolExhaustedAction.BLOCK.name());
+ builder.addPropertyValue("whenExhaustedAction", PoolExhaustedAction.valueOf(whenExhausted).getValue());
+
+ boolean testOnBorrow = getBoolean(poolingElement, ATT_TEST_ON_BORROW, false);
+ boolean testOnReturn = getBoolean(poolingElement, ATT_TEST_ON_RETURN, false);
+ boolean testWhileIdle = getBoolean(poolingElement, ATT_TEST_WHILE_IDLE, false);
+
+ if(testOnBorrow || testOnReturn || testWhileIdle) {
+ populatePoolValidationProperties(builder, poolingElement, testOnBorrow, testOnReturn, testWhileIdle);
+ }
+
+ return builder.getBeanDefinition();
+ }
+
+ private void populatePoolValidationProperties(BeanDefinitionBuilder builder, Element element,
+ boolean testOnBorrow, boolean testOnReturn, boolean testWhileIdle) {
+
+ builder.addPropertyValue("testOnBorrow", testOnBorrow);
+ builder.addPropertyValue("testOnReturn", testOnReturn);
+ builder.addPropertyValue("testWhileIdle", testWhileIdle);
+
+ BeanDefinitionBuilder validatorBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefaultDirContextValidator.class);
+ validatorBuilder.addPropertyValue("base", getString(element, ATT_VALIDATION_QUERY_BASE, ""));
+ validatorBuilder.addPropertyValue("filter",
+ getString(element, ATT_VALIDATION_QUERY_FILTER, DefaultDirContextValidator.DEFAULT_FILTER));
+ String searchControlsRef = element.getAttribute(ATT_VALIDATION_QUERY_SEARCH_CONTROLS_REF);
+ if(StringUtils.hasText(searchControlsRef)) {
+ validatorBuilder.addPropertyReference("searchControls", searchControlsRef);
+ }
+ builder.addPropertyValue("dirContextValidator", validatorBuilder.getBeanDefinition());
+
+ 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));
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/ldap/config/DefaultRenamingStrategyParser.java b/core/src/main/java/org/springframework/ldap/config/DefaultRenamingStrategyParser.java
new file mode 100644
index 00000000..92ad0faa
--- /dev/null
+++ b/core/src/main/java/org/springframework/ldap/config/DefaultRenamingStrategyParser.java
@@ -0,0 +1,49 @@
+/*
+ * 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.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy;
+import org.w3c.dom.Element;
+
+import static org.springframework.ldap.config.ParserUtils.getString;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+public class DefaultRenamingStrategyParser implements BeanDefinitionParser {
+ private final static String ATT_TEMP_SUFFIX = "temp-suffix";
+
+ @Override
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DefaultTempEntryRenamingStrategy.class);
+
+ builder.addPropertyValue("tempSuffix",
+ getString(element, ATT_TEMP_SUFFIX,
+ DefaultTempEntryRenamingStrategy.DEFAULT_TEMP_SUFFIX));
+
+ AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
+ parserContext.getContainingBeanDefinition().getPropertyValues()
+ .addPropertyValue("renamingStrategy", beanDefinition);
+
+ return beanDefinition;
+ }
+}
diff --git a/core/src/main/java/org/springframework/ldap/config/Elements.java b/core/src/main/java/org/springframework/ldap/config/Elements.java
new file mode 100644
index 00000000..5f490712
--- /dev/null
+++ b/core/src/main/java/org/springframework/ldap/config/Elements.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+public abstract class Elements {
+ public static final String CONTEXT_SOURCE = "context-source";
+ public static final String POOLING = "pooling";
+ public static final String LDAP_TEMPLATE = "ldap-template";
+ public static final String TRANSACTION_MANAGER = "transaction-manager";
+ public static final String DEFAULT_RENAMING_STRATEGY = "default-renaming-strategy";
+ public static final String DIFFERENT_SUBTREE_RENAMING_STRATEGY = "different-subtree-renaming-strategy";
+}
diff --git a/core/src/main/java/org/springframework/ldap/config/LdapNamespaceHandler.java b/core/src/main/java/org/springframework/ldap/config/LdapNamespaceHandler.java
new file mode 100644
index 00000000..5bd9da1b
--- /dev/null
+++ b/core/src/main/java/org/springframework/ldap/config/LdapNamespaceHandler.java
@@ -0,0 +1,31 @@
+/*
+ * 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.beans.factory.xml.NamespaceHandlerSupport;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+public class LdapNamespaceHandler extends NamespaceHandlerSupport {
+ @Override
+ public void init() {
+ registerBeanDefinitionParser(Elements.CONTEXT_SOURCE, new ContextSourceParser());
+ registerBeanDefinitionParser(Elements.LDAP_TEMPLATE, new LdapTemplateParser());
+ registerBeanDefinitionParser(Elements.TRANSACTION_MANAGER, new TransactionManagerParser());
+ }
+}
diff --git a/core/src/main/java/org/springframework/ldap/config/LdapTemplateParser.java b/core/src/main/java/org/springframework/ldap/config/LdapTemplateParser.java
new file mode 100644
index 00000000..577cb329
--- /dev/null
+++ b/core/src/main/java/org/springframework/ldap/config/LdapTemplateParser.java
@@ -0,0 +1,74 @@
+/*
+ * 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.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+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.core.LdapTemplate;
+import org.springframework.ldap.query.SearchScope;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+
+import static org.springframework.ldap.config.ParserUtils.getBoolean;
+import static org.springframework.ldap.config.ParserUtils.getInt;
+import static org.springframework.ldap.config.ParserUtils.getString;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+public class LdapTemplateParser implements BeanDefinitionParser {
+ private final static String ATT_COUNT_LIMIT = "count-limit";
+ private final static String ATT_TIME_LIMIT = "time-limit";
+ private final static String ATT_SEARCH_SCOPE = "search-scope";
+ private final static String ATT_IGNORE_PARTIAL_RESULT = "ignore-partial-result";
+ private final static String ATT_IGNORE_NAME_NOT_FOUND = "ignore-name-not-found";
+ private final static String ATT_ODM_REF = "odm-ref";
+ private final static String ATT_CONTEXT_SOURCE_REF = "context-source-ref";
+
+ private final static String DEFAULT_ID = "ldapTemplate";
+
+ @Override
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LdapTemplate.class);
+
+ String contextSourceRef = getString(element, ATT_CONTEXT_SOURCE_REF, ContextSourceParser.DEFAULT_ID);
+ builder.addPropertyReference("contextSource", contextSourceRef);
+ builder.addPropertyValue("defaultCountLimit", getInt(element, ATT_COUNT_LIMIT, 0));
+ builder.addPropertyValue("defaultTimeLimit", getInt(element, ATT_TIME_LIMIT, 0));
+
+ String searchScope = getString(element, ATT_SEARCH_SCOPE, SearchScope.SUBTREE.toString());
+ builder.addPropertyValue("defaultSearchScope", SearchScope.valueOf(searchScope).getId());
+ builder.addPropertyValue("ignorePartialResultException", getBoolean(element, ATT_IGNORE_PARTIAL_RESULT, false));
+ builder.addPropertyValue("ignoreNameNotFoundException", getBoolean(element, ATT_IGNORE_NAME_NOT_FOUND, false));
+
+ String odmRef = element.getAttribute(ATT_ODM_REF);
+ if(StringUtils.hasText(odmRef)) {
+ builder.addPropertyReference("objectDirectoryMapper", odmRef);
+ }
+
+ String id = getString(element, AbstractBeanDefinitionParser.ID_ATTRIBUTE, DEFAULT_ID);
+
+ BeanDefinition beanDefinition = builder.getBeanDefinition();
+ parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, id));
+
+ return beanDefinition;
+ }
+}
diff --git a/core/src/main/java/org/springframework/ldap/config/ParserUtils.java b/core/src/main/java/org/springframework/ldap/config/ParserUtils.java
new file mode 100644
index 00000000..c63026a8
--- /dev/null
+++ b/core/src/main/java/org/springframework/ldap/config/ParserUtils.java
@@ -0,0 +1,61 @@
+/*
+ * 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.util.StringUtils;
+import org.w3c.dom.Element;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+class ParserUtils {
+ static final String NAMESPACE = "http://www.springframework.org/schema/ldap";
+
+ /**
+ * Not to be instantiated
+ */
+ private ParserUtils() {
+
+ }
+
+ static boolean getBoolean(Element element, String attribute, boolean defaultValue) {
+ String theValue = element.getAttribute(attribute);
+ if (StringUtils.hasText(theValue)) {
+ return Boolean.valueOf(theValue);
+ }
+
+ return defaultValue;
+ }
+
+ static String getString(Element element, String attribute, String defaultValue) {
+ String theValue = element.getAttribute(attribute);
+ if (StringUtils.hasText(theValue)) {
+ return theValue;
+ }
+
+ return defaultValue;
+ }
+
+ static int getInt(Element element, String attribute, int defaultValue) {
+ String theValue = element.getAttribute(attribute);
+ if (StringUtils.hasText(theValue)) {
+ return Integer.parseInt(theValue);
+ }
+
+ return defaultValue;
+ }
+}
diff --git a/core/src/main/java/org/springframework/ldap/config/TransactionManagerParser.java b/core/src/main/java/org/springframework/ldap/config/TransactionManagerParser.java
new file mode 100644
index 00000000..0863070f
--- /dev/null
+++ b/core/src/main/java/org/springframework/ldap/config/TransactionManagerParser.java
@@ -0,0 +1,107 @@
+/*
+ * 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.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+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.transaction.compensating.manager.ContextSourceTransactionManager;
+import org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy;
+import org.springframework.ldap.transaction.compensating.support.DifferentSubtreeTempEntryRenamingStrategy;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+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;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+public class TransactionManagerParser implements BeanDefinitionParser {
+ private final static String ATT_CONTEXT_SOURCE_REF = "context-source-ref";
+ private final static String ATT_DATA_SOURCE_REF = "data-source-ref";
+ private final static String ATT_SESSION_FACTORY_REF = "session-factory-ref";
+
+ private final static String ATT_TEMP_SUFFIX = "temp-suffix";
+ private final static String ATT_SUBTREE_NODE = "subtree-node";
+
+ private final static String DEFAULT_ID = "transactionManager";
+
+ @Override
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+
+ String contextSourceRef = getString(element, ATT_CONTEXT_SOURCE_REF, ContextSourceParser.DEFAULT_ID);
+ String dataSourceRef = element.getAttribute(ATT_DATA_SOURCE_REF);
+ String sessionFactoryRef = element.getAttribute(ATT_SESSION_FACTORY_REF);
+
+ if(StringUtils.hasText(dataSourceRef) && StringUtils.hasText(sessionFactoryRef)) {
+ throw new IllegalArgumentException(
+ String.format("Only one of %s and %s can be specified",
+ ATT_DATA_SOURCE_REF, ATT_SESSION_FACTORY_REF));
+ }
+
+ 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);
+
+ if(defaultStrategyChildren.getLength() == 1) {
+ builder.addPropertyValue("renamingStrategy", parseDefaultRenamingStrategy((Element) defaultStrategyChildren.item(0)));
+ }
+
+ if(differentSubtreeChildren.getLength() == 1) {
+ builder.addPropertyValue("renamingStrategy", parseDifferentSubtreeRenamingStrategy((Element) differentSubtreeChildren.item(0)));
+ }
+
+ String id = getString(element, AbstractBeanDefinitionParser.ID_ATTRIBUTE, DEFAULT_ID);
+
+ BeanDefinition beanDefinition = builder.getBeanDefinition();
+ parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, id));
+
+ return beanDefinition;
+ }
+
+ private BeanDefinition parseDifferentSubtreeRenamingStrategy(Element element) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DifferentSubtreeTempEntryRenamingStrategy.class);
+
+ String subtreeNode = element.getAttribute(ATT_SUBTREE_NODE);
+ Assert.hasText(subtreeNode, ATT_SUBTREE_NODE + " must be specified");
+
+ builder.addConstructorArgValue(subtreeNode);
+
+ return builder.getBeanDefinition();
+ }
+
+ public BeanDefinition parseDefaultRenamingStrategy(Element element) {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DefaultTempEntryRenamingStrategy.class);
+
+ builder.addPropertyValue("tempSuffix",
+ getString(element, ATT_TEMP_SUFFIX,
+ DefaultTempEntryRenamingStrategy.DEFAULT_TEMP_SUFFIX));
+
+ return builder.getBeanDefinition();
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/ldap/pool/PoolExhaustedAction.java b/core/src/main/java/org/springframework/ldap/pool/PoolExhaustedAction.java
new file mode 100644
index 00000000..cd4f7a4b
--- /dev/null
+++ b/core/src/main/java/org/springframework/ldap/pool/PoolExhaustedAction.java
@@ -0,0 +1,20 @@
+package org.springframework.ldap.pool;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+public enum PoolExhaustedAction {
+ FAIL((byte)0),
+ BLOCK((byte)1),
+ GROW((byte)2);
+
+ private final byte value;
+
+ private PoolExhaustedAction(byte value) {
+ this.value = value;
+ }
+
+ public byte getValue() {
+ return value;
+ }
+}
diff --git a/core/src/main/java/org/springframework/ldap/pool/validation/DefaultDirContextValidator.java b/core/src/main/java/org/springframework/ldap/pool/validation/DefaultDirContextValidator.java
index 12440d99..d3b2287f 100644
--- a/core/src/main/java/org/springframework/ldap/pool/validation/DefaultDirContextValidator.java
+++ b/core/src/main/java/org/springframework/ldap/pool/validation/DefaultDirContextValidator.java
@@ -74,6 +74,8 @@ import javax.naming.directory.SearchResult;
* @author Eric Dalquist
*/
public class DefaultDirContextValidator implements DirContextValidator {
+ public static final String DEFAULT_FILTER = "objectclass=*";
+
/**
* Logger for this class and sub-classes
*/
@@ -107,7 +109,7 @@ public class DefaultDirContextValidator implements DirContextValidator {
this.base = "";
- this.filter = "objectclass=*";
+ this.filter = DEFAULT_FILTER;
}
/**
diff --git a/core/src/main/resources/META-INF/spring.handlers b/core/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 00000000..6f6acd26
--- /dev/null
+++ b/core/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1 @@
+http\://www.springframework.org/schema/ldap=org.springframework.ldap.config.LdapNamespaceHandler
\ No newline at end of file
diff --git a/core/src/main/resources/META-INF/spring.schemas b/core/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 00000000..574610e2
--- /dev/null
+++ b/core/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,2 @@
+http\://www.springframework.org/schema/ldap/spring-ldap.xsd=org/springframework/ldap/config/spring-ldap-2.0.xsd
+http\://www.springframework.org/schema/ldap/spring-ldap-2.0.xsd=org/springframework/ldap/config/spring-ldap-2.0.xsd
\ No newline at end of file
diff --git a/core/src/main/resources/org/springframework/ldap/config/spring-ldap-2.0.xsd b/core/src/main/resources/org/springframework/ldap/config/spring-ldap-2.0.xsd
new file mode 100644
index 00000000..e4371965
--- /dev/null
+++ b/core/src/main/resources/org/springframework/ldap/config/spring-ldap-2.0.xsd
@@ -0,0 +1,432 @@
+
+
+
+
+
+
+
+ A bean identifier, used for referring to the bean elsewhere in the context.
+ "contextSource".
+
+
+
+
+
+
+ Defines whether read-only operations will be performed using an anonymous (unauthenticated) context.
+
+
+
+
+
+
+ Id of the AuthenticationSource instance to use. If not specified, a SimpleAuthenticationSource will
+ be used.
+
+
+
+
+
+
+ Id of the DirContextAuthenticationStrategy instance to use. If not specified, a SimpleDirContextAuthenticationStrategy
+ will be used.
+
+
+
+
+
+
+ The base DN. If specified, 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).
+
+
+
+
+
+
+ The password to use for authentication.
+
+
+
+
+
+
+ Specify whether native Java LDAP connection pooling should be used. Default is false.
+
+
+
+
+
+
+ Defines the strategy to handle referrals, as described on http://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html.
+ Default is null.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ URL of the LDAP server to use. If fail-over functionality is desired, more than one URL can
+ be specified, separated using comma (,).
+
+
+
+
+
+
+ The username (principal) to use for authentication. This will normally be the distinguished name
+ of an admin user.
+
+
+
+
+
+
+
+
+
+ The maximum number of active connections of each type (read-only|read-write)
+ that can be allocated from the pool at the same time, or non-positive for no limit.
+ Default is 8.
+
+
+
+
+
+
+ The overall maximum number of active connections (for all types) that can be allocated from
+ this pool at the same time, or non-positive for no limit. Default is -1 (no limit).
+
+
+
+
+
+
+ The maximum number of active connections of each type (read-only|read-write) that can remain idle in the pool,
+ without extra ones being released, or non-positive for no limit. Default is 8.
+
+
+
+
+
+
+ The minimum number of active connections of each type (read-only|read-write) that can remain
+ idle in the pool, without extra ones being created, or zero to create none. Default is 0.
+
+
+
+
+
+
+ The maximum number of milliseconds that the pool will wait (when there are no available connections)
+ for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.
+ Default is -1.
+
+
+
+
+
+
+ Specifies the behaviour when the pool is exhausted.
+
+
+
+
+
+
+
+ Throw a NoSuchElementException when the pool is exhausted
+
+
+
+
+
+
+ Wait until a new object is available. If max-wait is positive a NoSuchElementException
+ is thrown if no new object is available after the maxWait time expires.
+
+
+
+
+
+
+ Create and return a new object (essentially making maxActive meaningless).
+
+
+
+
+
+
+
+
+
+ The indication of whether objects will be validated before being borrowed from the pool.
+ If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.
+ Default is false.
+
+
+
+
+
+
+ The indication of whether objects will be validated before being returned to the pool.
+ Default is false.
+
+
+
+
+
+
+ The indication of whether objects will be validated by the idle object evictor (if any).
+ If an object fails to validate, it will be dropped from the pool.
+ Default is false.
+
+
+
+
+
+
+ The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,
+ no idle object evictor thread will be run. Default is -1.
+
+
+
+
+
+
+ The number of objects to examine during each run of the idle object evictor thread (if any).
+ Default is 3.
+
+
+
+
+
+
+ The minimum amount of time an object may sit idle in the pool before it is eligible
+ for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.
+
+
+
+
+
+
+ The base dn to use for validation searches. Default is LdapUtils.emptyPath().
+
+
+
+
+
+
+ The filter to use for validation queries. Default is (objectclass=*).
+
+
+
+
+
+
+ Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;
+ countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].
+
+
+
+
+
+
+
+
+ Creates a ContextSource instance to be used to get LdapContexts for communicating with an LDAP server.
+
+
+
+
+
+
+
+ Defines the settings to use for the Spring LDAP connection pooling support.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A bean identifier, used for referring to the bean elsewhere in the context.
+ Default is "ldapTemplate".
+
+
+
+
+
+
+ Id of the ContextSource instance to use. Default is "contextSource".
+
+
+
+
+
+
+ The default count limit for searches. Default is 0 (no limit).
+
+
+
+
+
+
+ The default time limit for searches. Default is 0 (no limit).
+
+
+
+
+
+
+ The default search scope for searches. Default is SUBTREE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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.
+ Default is false.
+
+
+
+
+
+
+ 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. Default is false.
+
+
+
+
+
+
+ Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper.
+
+
+
+
+
+
+
+
+ Creates an LdapTemplate instance.
+
+
+
+
+
+
+
+
+
+
+
+ Id of this instance. Default is "transactionManager".
+
+
+
+
+
+
+ Id of the ContextSource instance to use. "contextSource".
+
+
+
+
+
+
+ Id of the DataSource instance to use.
+
+
+
+
+
+
+ Id of the Hibernate SessionFactory instance to use.
+
+
+
+
+
+
+
+
+ Creates an ContextSourceTransactionManager. If data-source-ref or session-factory-ref is specified,
+ a DataSourceAndContextSourceTransactionManager/HibernateAndContextSourceTransactionManager will be
+ created.
+
+
+
+
+
+
+
+ The default (simplistic) TempEntryRenamingStrategy. Please note that this
+ strategy will not work for more advanced scenarios. See reference documentation
+ for details.
+
+
+
+
+
+
+ The default suffix that will be added to modified entries.
+ Default is "_temp".
+
+
+
+
+
+
+
+
+ TempEntryRenamingStrategy that moves the entry to a different subtree than
+ the original entry.
+
+
+
+
+
+
+ The subtree base where changed entries should be moved.
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/java/org/springframework/ldap/config/LdapTemplateNamespaceHandlerTest.java b/core/src/test/java/org/springframework/ldap/config/LdapTemplateNamespaceHandlerTest.java
new file mode 100644
index 00000000..1018c8e3
--- /dev/null
+++ b/core/src/test/java/org/springframework/ldap/config/LdapTemplateNamespaceHandlerTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.apache.commons.pool.impl.GenericKeyedObjectPool;
+import org.junit.Test;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.ldap.core.ContextSource;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
+import org.springframework.ldap.pool.factory.PoolingContextSource;
+import org.springframework.ldap.pool.validation.DefaultDirContextValidator;
+import org.springframework.ldap.support.LdapUtils;
+import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy;
+import org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager;
+import org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy;
+import org.springframework.ldap.transaction.compensating.support.DefaultTempEntryRenamingStrategy;
+import org.springframework.ldap.transaction.compensating.support.DifferentSubtreeTempEntryRenamingStrategy;
+import org.springframework.transaction.PlatformTransactionManager;
+
+import javax.naming.directory.SearchControls;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.internal.util.reflection.Whitebox.getInternalState;
+
+/**
+ * @author Mattias Hellborg Arthursson
+ */
+public class LdapTemplateNamespaceHandlerTest {
+
+ @Test
+ public void verifyParseWithDefaultValues() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-defaults.xml");
+ ContextSource outerContextSource = ctx.getBean(ContextSource.class);
+ LdapTemplate ldapTemplate = ctx.getBean(LdapTemplate.class);
+
+ assertNotNull(outerContextSource);
+ assertNotNull(ldapTemplate);
+
+ assertTrue(outerContextSource instanceof TransactionAwareContextSourceProxy);
+ ContextSource contextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
+
+ assertEquals(LdapUtils.emptyLdapName(), getInternalState(contextSource, "base"));
+ assertEquals("uid=admin", getInternalState(contextSource, "userDn"));
+ assertEquals("apassword", getInternalState(contextSource, "password"));
+ assertArrayEquals(new String[]{"ldap://localhost:389"}, (Object[]) getInternalState(contextSource, "urls"));
+ assertEquals(Boolean.FALSE, getInternalState(contextSource, "pooled"));
+ assertEquals(Boolean.FALSE, getInternalState(contextSource, "anonymousReadOnly"));
+ assertNull(getInternalState(contextSource, "referral"));
+
+ assertSame(outerContextSource, getInternalState(ldapTemplate, "contextSource"));
+ assertEquals(Boolean.FALSE, getInternalState(ldapTemplate, "ignorePartialResultException"));
+ assertEquals(Boolean.FALSE, getInternalState(ldapTemplate, "ignoreNameNotFoundException"));
+ assertEquals(0, getInternalState(ldapTemplate, "defaultCountLimit"));
+ assertEquals(0, getInternalState(ldapTemplate, "defaultTimeLimit"));
+ assertEquals(SearchControls.SUBTREE_SCOPE, getInternalState(ldapTemplate, "defaultSearchScope"));
+ }
+
+ @Test
+ public void verifyParseWithCustomValues() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-values.xml");
+ ContextSource outerContextSource = ctx.getBean(ContextSource.class);
+ LdapTemplate ldapTemplate = ctx.getBean(LdapTemplate.class);
+ DirContextAuthenticationStrategy authenticationStrategy = ctx.getBean(DirContextAuthenticationStrategy.class);
+
+ assertNotNull(outerContextSource);
+ assertNotNull(ldapTemplate);
+
+ assertTrue(outerContextSource instanceof TransactionAwareContextSourceProxy);
+ ContextSource contextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
+
+ assertEquals(LdapUtils.newLdapName("dc=261consulting,dc=com"), getInternalState(contextSource, "base"));
+ assertEquals("uid=admin", getInternalState(contextSource, "userDn"));
+ 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("follow", getInternalState(contextSource, "referral"));
+ assertSame(authenticationStrategy, getInternalState(contextSource, "authenticationStrategy"));
+
+ assertSame(outerContextSource, getInternalState(ldapTemplate, "contextSource"));
+ assertEquals(Boolean.TRUE, getInternalState(ldapTemplate, "ignorePartialResultException"));
+ assertEquals(Boolean.TRUE, getInternalState(ldapTemplate, "ignoreNameNotFoundException"));
+ assertEquals(100, getInternalState(ldapTemplate, "defaultCountLimit"));
+ assertEquals(200, getInternalState(ldapTemplate, "defaultTimeLimit"));
+ assertEquals(SearchControls.OBJECT_SCOPE, getInternalState(ldapTemplate, "defaultSearchScope"));
+ }
+
+ @Test
+ public void verifyParseWithDefaultTransactions() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-transactional-defaults.xml");
+
+ ContextSource outerContextSource = ctx.getBean(ContextSource.class);
+ PlatformTransactionManager transactionManager = ctx.getBean(PlatformTransactionManager.class);
+
+ assertNotNull(outerContextSource);
+ assertNotNull(transactionManager);
+
+ assertTrue(outerContextSource instanceof TransactionAwareContextSourceProxy);
+ ContextSource contextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
+
+ assertTrue(transactionManager instanceof ContextSourceTransactionManager);
+
+ Object delegate = getInternalState(transactionManager, "delegate");
+ assertSame(contextSource, getInternalState(delegate, "contextSource"));
+ TempEntryRenamingStrategy renamingStrategy =
+ (TempEntryRenamingStrategy) getInternalState(delegate, "renamingStrategy");
+
+ assertTrue(renamingStrategy instanceof DefaultTempEntryRenamingStrategy);
+ assertEquals("_temp", getInternalState(renamingStrategy, "tempSuffix"));
+ }
+
+ @Test
+ public void verifyParseTransactionsWithDefaultStrategyAndSuffix() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-transactional-defaults-with-suffix.xml");
+
+ PlatformTransactionManager transactionManager = ctx.getBean(PlatformTransactionManager.class);
+
+ assertNotNull(transactionManager);
+ assertTrue(transactionManager instanceof ContextSourceTransactionManager);
+
+ Object delegate = getInternalState(transactionManager, "delegate");
+ TempEntryRenamingStrategy renamingStrategy =
+ (TempEntryRenamingStrategy) getInternalState(delegate, "renamingStrategy");
+
+ assertTrue(renamingStrategy instanceof DefaultTempEntryRenamingStrategy);
+ assertEquals("_thisisthesuffix", getInternalState(renamingStrategy, "tempSuffix"));
+ }
+
+ @Test
+ public void verifyParseTransactionsWithDifferentSubtreeStrategy() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-transactional-different-subtree.xml");
+
+ PlatformTransactionManager transactionManager = ctx.getBean(PlatformTransactionManager.class);
+
+ assertNotNull(transactionManager);
+ assertTrue(transactionManager instanceof ContextSourceTransactionManager);
+
+ Object delegate = getInternalState(transactionManager, "delegate");
+ TempEntryRenamingStrategy renamingStrategy =
+ (TempEntryRenamingStrategy) getInternalState(delegate, "renamingStrategy");
+
+ assertTrue(renamingStrategy instanceof DifferentSubtreeTempEntryRenamingStrategy);
+ assertEquals(LdapUtils.newLdapName("ou=temp"), getInternalState(renamingStrategy, "subtreeNode"));
+ }
+
+ @Test
+ public void verifyParsePoolingDefaults() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-pooling-defaults.xml");
+
+ ContextSource outerContextSource = ctx.getBean(ContextSource.class);
+ assertNotNull(outerContextSource);
+ assertTrue(outerContextSource instanceof TransactionAwareContextSourceProxy);
+
+ ContextSource pooledContextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
+ assertNotNull(pooledContextSource);
+ assertTrue(pooledContextSource instanceof PoolingContextSource);
+
+ Object objectFactory = getInternalState(pooledContextSource, "dirContextPoolableObjectFactory");
+ assertNotNull(getInternalState(objectFactory, "contextSource"));
+ assertNull(getInternalState(objectFactory, "dirContextValidator"));
+
+ GenericKeyedObjectPool objectPool = (GenericKeyedObjectPool) getInternalState(pooledContextSource, "keyedObjectPool");
+ assertEquals(8, objectPool.getMaxActive());
+ assertEquals(-1, objectPool.getMaxTotal());
+ assertEquals(8, objectPool.getMaxIdle());
+ assertEquals(-1, objectPool.getMaxWait());
+ assertEquals(0, objectPool.getMinIdle());
+ assertEquals(1, objectPool.getWhenExhaustedAction());
+ }
+
+ @Test
+ public void verifyParsePoolingSizeSet() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-pooling-configured-poolsize.xml");
+
+ ContextSource outerContextSource = ctx.getBean(ContextSource.class);
+ assertNotNull(outerContextSource);
+
+ ContextSource pooledContextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
+ assertNotNull(pooledContextSource);
+
+ GenericKeyedObjectPool objectPool = (GenericKeyedObjectPool) getInternalState(pooledContextSource, "keyedObjectPool");
+ assertEquals(10, objectPool.getMaxActive());
+ assertEquals(12, objectPool.getMaxTotal());
+ assertEquals(11, objectPool.getMaxIdle());
+ assertEquals(13, objectPool.getMaxWait());
+ assertEquals(14, objectPool.getMinIdle());
+ assertEquals(0, objectPool.getWhenExhaustedAction());
+ }
+
+ @Test
+ public void verifyParsePoolingValidationSet() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-pooling-test-specified.xml");
+
+ ContextSource outerContextSource = ctx.getBean(ContextSource.class);
+ assertNotNull(outerContextSource);
+
+ ContextSource pooledContextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
+ assertNotNull(pooledContextSource);
+
+ GenericKeyedObjectPool objectPool = (GenericKeyedObjectPool) getInternalState(pooledContextSource, "keyedObjectPool");
+ assertEquals(123, objectPool.getMinEvictableIdleTimeMillis());
+ assertEquals(321, objectPool.getTimeBetweenEvictionRunsMillis());
+ assertEquals(22, objectPool.getNumTestsPerEvictionRun());
+
+ Object objectFactory = getInternalState(pooledContextSource, "dirContextPoolableObjectFactory");
+ DefaultDirContextValidator validator = (DefaultDirContextValidator) getInternalState(objectFactory, "dirContextValidator");
+ assertEquals("ou=test", validator.getBase());
+ assertEquals("objectclass=person", validator.getFilter());
+
+ SearchControls searchControls = ctx.getBean(SearchControls.class);
+ assertEquals("objectclass=person", validator.getFilter());
+ assertSame(searchControls, validator.getSearchControls());
+ }
+}
diff --git a/core/src/test/resources/ldap-namespace-config-defaults.xml b/core/src/test/resources/ldap-namespace-config-defaults.xml
new file mode 100644
index 00000000..54ddf74d
--- /dev/null
+++ b/core/src/test/resources/ldap-namespace-config-defaults.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/ldap-namespace-config-pooling-configured-poolsize.xml b/core/src/test/resources/ldap-namespace-config-pooling-configured-poolsize.xml
new file mode 100644
index 00000000..4dfc1fc8
--- /dev/null
+++ b/core/src/test/resources/ldap-namespace-config-pooling-configured-poolsize.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/ldap-namespace-config-pooling-defaults.xml b/core/src/test/resources/ldap-namespace-config-pooling-defaults.xml
new file mode 100644
index 00000000..de3517b1
--- /dev/null
+++ b/core/src/test/resources/ldap-namespace-config-pooling-defaults.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/ldap-namespace-config-pooling-test-specified.xml b/core/src/test/resources/ldap-namespace-config-pooling-test-specified.xml
new file mode 100644
index 00000000..704c4a0c
--- /dev/null
+++ b/core/src/test/resources/ldap-namespace-config-pooling-test-specified.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/ldap-namespace-config-transactional-defaults-with-suffix.xml b/core/src/test/resources/ldap-namespace-config-transactional-defaults-with-suffix.xml
new file mode 100644
index 00000000..0e76d6a9
--- /dev/null
+++ b/core/src/test/resources/ldap-namespace-config-transactional-defaults-with-suffix.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/ldap-namespace-config-transactional-defaults.xml b/core/src/test/resources/ldap-namespace-config-transactional-defaults.xml
new file mode 100644
index 00000000..44a5ea82
--- /dev/null
+++ b/core/src/test/resources/ldap-namespace-config-transactional-defaults.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/ldap-namespace-config-transactional-different-subtree.xml b/core/src/test/resources/ldap-namespace-config-transactional-different-subtree.xml
new file mode 100644
index 00000000..78cde2a5
--- /dev/null
+++ b/core/src/test/resources/ldap-namespace-config-transactional-different-subtree.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/src/test/resources/ldap-namespace-config-values.xml b/core/src/test/resources/ldap-namespace-config-values.xml
new file mode 100644
index 00000000..93463353
--- /dev/null
+++ b/core/src/test/resources/ldap-namespace-config-values.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file