From 0606f373884cf734acb5f1801cb2f6ea8f1468a1 Mon Sep 17 00:00:00 2001 From: Mattias Hellborg Arthursson Date: Mon, 7 Oct 2013 15:48:09 +0200 Subject: [PATCH] LDAP-267: Initial version of ldap namespace. --- .../ldap/config/ContextSourceParser.java | 165 +++++++ .../config/DefaultRenamingStrategyParser.java | 49 ++ .../springframework/ldap/config/Elements.java | 29 ++ .../ldap/config/LdapNamespaceHandler.java | 31 ++ .../ldap/config/LdapTemplateParser.java | 74 +++ .../ldap/config/ParserUtils.java | 61 +++ .../ldap/config/TransactionManagerParser.java | 107 +++++ .../ldap/pool/PoolExhaustedAction.java | 20 + .../DefaultDirContextValidator.java | 4 +- .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 2 + .../ldap/config/spring-ldap-2.0.xsd | 432 ++++++++++++++++++ .../LdapTemplateNamespaceHandlerTest.java | 234 ++++++++++ .../ldap-namespace-config-defaults.xml | 10 + ...ace-config-pooling-configured-poolsize.xml | 13 + ...ldap-namespace-config-pooling-defaults.xml | 13 + ...amespace-config-pooling-test-specified.xml | 24 + ...fig-transactional-defaults-with-suffix.xml | 14 + ...amespace-config-transactional-defaults.xml | 14 + ...config-transactional-different-subtree.xml | 14 + .../ldap-namespace-config-values.xml | 29 ++ 21 files changed, 1339 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/springframework/ldap/config/ContextSourceParser.java create mode 100644 core/src/main/java/org/springframework/ldap/config/DefaultRenamingStrategyParser.java create mode 100644 core/src/main/java/org/springframework/ldap/config/Elements.java create mode 100644 core/src/main/java/org/springframework/ldap/config/LdapNamespaceHandler.java create mode 100644 core/src/main/java/org/springframework/ldap/config/LdapTemplateParser.java create mode 100644 core/src/main/java/org/springframework/ldap/config/ParserUtils.java create mode 100644 core/src/main/java/org/springframework/ldap/config/TransactionManagerParser.java create mode 100644 core/src/main/java/org/springframework/ldap/pool/PoolExhaustedAction.java create mode 100644 core/src/main/resources/META-INF/spring.handlers create mode 100644 core/src/main/resources/META-INF/spring.schemas create mode 100644 core/src/main/resources/org/springframework/ldap/config/spring-ldap-2.0.xsd create mode 100644 core/src/test/java/org/springframework/ldap/config/LdapTemplateNamespaceHandlerTest.java create mode 100644 core/src/test/resources/ldap-namespace-config-defaults.xml create mode 100644 core/src/test/resources/ldap-namespace-config-pooling-configured-poolsize.xml create mode 100644 core/src/test/resources/ldap-namespace-config-pooling-defaults.xml create mode 100644 core/src/test/resources/ldap-namespace-config-pooling-test-specified.xml create mode 100644 core/src/test/resources/ldap-namespace-config-transactional-defaults-with-suffix.xml create mode 100644 core/src/test/resources/ldap-namespace-config-transactional-defaults.xml create mode 100644 core/src/test/resources/ldap-namespace-config-transactional-different-subtree.xml create mode 100644 core/src/test/resources/ldap-namespace-config-values.xml 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