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

org.springframework.ldap.config.ContextSourceParser Maven / Gradle / Ivy

/*
 * Copyright 2005-2015 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
 *
 *      https://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 java.util.HashSet;
import java.util.Set;

import org.w3c.dom.Element;

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.CommunicationException;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.pool.PoolExhaustedAction;
import org.springframework.ldap.pool.factory.PoolingContextSource;
import org.springframework.ldap.pool.validation.DefaultDirContextValidator;
import org.springframework.ldap.pool2.factory.PoolConfig;
import org.springframework.ldap.pool2.factory.PooledContextSource;
import org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;

/**
 * @author Mattias Hellborg Arthursson
 * @author Eddu Melendez
 */
public class ContextSourceParser implements BeanDefinitionParser {

	private static final String ATT_ANONYMOUS_READ_ONLY = "anonymous-read-only";

	private static final String ATT_AUTHENTICATION_SOURCE_REF = "authentication-source-ref";

	private static final String ATT_AUTHENTICATION_STRATEGY_REF = "authentication-strategy-ref";

	private static final String ATT_BASE = "base";

	private static final String ATT_PASSWORD = "password";

	private static final String ATT_NATIVE_POOLING = "native-pooling";

	private static final String ATT_REFERRAL = "referral";

	private static final String ATT_URL = "url";

	private static final String ATT_BASE_ENV_PROPS_REF = "base-env-props-ref";

	// pooling attributes
	private static final String ATT_MAX_ACTIVE = "max-active";

	private static final String ATT_MAX_TOTAL = "max-total";

	private static final String ATT_MAX_IDLE = "max-idle";

	private static final String ATT_MIN_IDLE = "min-idle";

	private static final String ATT_MAX_WAIT = "max-wait";

	private static final String ATT_WHEN_EXHAUSTED = "when-exhausted";

	private static final String ATT_TEST_ON_BORROW = "test-on-borrow";

	private static final String ATT_TEST_ON_RETURN = "test-on-return";

	private static final String ATT_TEST_WHILE_IDLE = "test-while-idle";

	private static final String ATT_EVICTION_RUN_MILLIS = "eviction-run-interval-millis";

	private static final String ATT_TESTS_PER_EVICTION_RUN = "tests-per-eviction-run";

	private static final String ATT_EVICTABLE_TIME_MILLIS = "min-evictable-time-millis";

	private static final String ATT_VALIDATION_QUERY_BASE = "validation-query-base";

	private static final String ATT_VALIDATION_QUERY_FILTER = "validation-query-filter";

	private static final String ATT_VALIDATION_QUERY_SEARCH_CONTROLS_REF = "validation-query-search-controls-ref";

	private static final String ATT_NON_TRANSIENT_EXCEPTIONS = "non-transient-exceptions";

	private static final String ATT_MAX_IDLE_PER_KEY = "max-idle-per-key";

	private static final String ATT_MIN_IDLE_PER_KEY = "min-idle-per-key";

	private static final String ATT_MAX_TOTAL_PER_KEY = "max-total-per-key";

	private static final String ATT_EVICTION_POLICY_CLASS = "eviction-policy-class";

	private static final String ATT_FAIRNESS = "fairness";

	private static final String ATT_JMX_ENABLE = "jmx-enable";

	private static final String ATT_JMX_NAME_BASE = "jmx-name-base";

	private static final String ATT_JMX_NAME_PREFIX = "jmx-name-prefix";

	private static final String ATT_LIFO = "lifo";

	private static final String ATT_BLOCK_WHEN_EXHAUSTED = "block-when-exhausted";

	private static final String ATT_TEST_ON_CREATE = "test-on-create";

	private static final String ATT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = "soft-min-evictable-idle-time-millis";

	private static final String ATT_USERNAME = "username";
	static final String DEFAULT_ID = "contextSource";

	private static final int DEFAULT_MAX_ACTIVE = 8;

	private static final int DEFAULT_MAX_TOTAL = -1;

	private static final int DEFAULT_MAX_IDLE = 8;

	private static final int DEFAULT_MIN_IDLE = 0;

	private static final int DEFAULT_MAX_WAIT = -1;

	private static final int DEFAULT_EVICTION_RUN_MILLIS = -1;

	private static final int DEFAULT_TESTS_PER_EVICTION_RUN = 3;

	private static final int DEFAULT_EVICTABLE_MILLIS = 1000 * 60 * 30;

	private static final int DEFAULT_MAX_TOTAL_PER_KEY = 8;

	private static final int DEFAULT_MAX_IDLE_PER_KEY = 8;

	private static final int DEFAULT_MIN_IDLE_PER_KEY = 0;

	private static final String DEFAULT_EVICTION_POLICY_CLASS_NAME = "org.apache.commons.pool2.impl.DefaultEvictionPolicy";

	private static final boolean DEFAULT_FAIRNESS = false;

	private static final boolean DEFAULT_JMX_ENABLE = true;

	private static final String DEFAULT_JMX_NAME_BASE = null;

	private static final String DEFAULT_JMX_NAME_PREFIX = "ldap-pool";

	private static final boolean DEFAULT_LIFO = true;

	private static final int DEFAULT_MAX_WAIT_MILLIS = -1;

	private static final boolean DEFAULT_BLOCK_WHEN_EXHAUSTED = true;

	private static final int DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1;

	@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(url, "url attribute must be specified");

		builder.addPropertyValue("userDn", username);
		builder.addPropertyValue("password", password);

		BeanDefinitionBuilder urlsBuilder = BeanDefinitionBuilder.rootBeanDefinition(UrlsFactory.class)
				.setFactoryMethod("urls").addConstructorArgValue(url);

		builder.addPropertyValue("urls", urlsBuilder.getBeanDefinition());
		builder.addPropertyValue("base", ParserUtils.getString(element, ATT_BASE, ""));
		builder.addPropertyValue("referral", ParserUtils.getString(element, ATT_REFERRAL, null));

		boolean anonymousReadOnly = ParserUtils.getBoolean(element, ATT_ANONYMOUS_READ_ONLY, false);
		builder.addPropertyValue("anonymousReadOnly", anonymousReadOnly);
		boolean nativePooling = ParserUtils.getBoolean(element, ATT_NATIVE_POOLING, false);
		builder.addPropertyValue("pooled", nativePooling);

		String authStrategyRef = element.getAttribute(ATT_AUTHENTICATION_STRATEGY_REF);
		if (StringUtils.hasText(authStrategyRef)) {
			builder.addPropertyReference("authenticationStrategy", authStrategyRef);
		}

		String authSourceRef = element.getAttribute(ATT_AUTHENTICATION_SOURCE_REF);
		if (StringUtils.hasText(authSourceRef)) {
			builder.addPropertyReference("authenticationSource", authSourceRef);
		}
		else {
			Assert.hasText(username,
					"username attribute must be specified unless an authentication-source-ref explicitly configured");
			Assert.hasText(password,
					"password attribute must be specified unless an authentication-source-ref explicitly configured");
		}

		String baseEnvPropsRef = element.getAttribute(ATT_BASE_ENV_PROPS_REF);
		if (StringUtils.hasText(baseEnvPropsRef)) {
			builder.addPropertyReference("baseEnvironmentProperties", baseEnvPropsRef);
		}

		BeanDefinition targetContextSourceDefinition = builder.getBeanDefinition();
		targetContextSourceDefinition = applyPoolingIfApplicable(targetContextSourceDefinition, element, nativePooling);

		BeanDefinition actualContextSourceDefinition = targetContextSourceDefinition;
		if (!anonymousReadOnly) {
			BeanDefinitionBuilder proxyBuilder = BeanDefinitionBuilder
					.rootBeanDefinition(TransactionAwareContextSourceProxy.class);
			proxyBuilder.addConstructorArgValue(targetContextSourceDefinition);
			actualContextSourceDefinition = proxyBuilder.getBeanDefinition();
		}

		String id = ParserUtils.getString(element, AbstractBeanDefinitionParser.ID_ATTRIBUTE, DEFAULT_ID);
		parserContext.registerBeanComponent(new BeanComponentDefinition(actualContextSourceDefinition, id));

		return actualContextSourceDefinition;
	}

	private BeanDefinition applyPoolingIfApplicable(BeanDefinition targetContextSourceDefinition, Element element,
			boolean nativePooling) {

		Element poolingElement = DomUtils.getChildElementByTagName(element, Elements.POOLING);
		Element pooling2Element = DomUtils.getChildElementByTagName(element, Elements.POOLING2);

		if (pooling2Element != null && poolingElement != null) {
			throw new IllegalArgumentException(
					String.format("%s cannot be enabled together with %s.", Elements.POOLING2, Elements.POOLING));
		}
		else if (poolingElement == null && pooling2Element == null) {
			return targetContextSourceDefinition;
		}

		if (nativePooling) {
			throw new IllegalArgumentException(
					String.format("%s cannot be enabled together with %s", ATT_NATIVE_POOLING, Elements.POOLING));
		}

		if (pooling2Element != null) {
			BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PooledContextSource.class);
			builder.addPropertyValue("contextSource", targetContextSourceDefinition);

			populatePoolConfigProperties(builder, pooling2Element);

			boolean testOnBorrow = ParserUtils.getBoolean(pooling2Element, ATT_TEST_ON_BORROW, false);
			boolean testOnReturn = ParserUtils.getBoolean(pooling2Element, ATT_TEST_ON_RETURN, false);
			boolean testWhileIdle = ParserUtils.getBoolean(pooling2Element, ATT_TEST_WHILE_IDLE, false);
			boolean testOnCreate = ParserUtils.getBoolean(pooling2Element, ATT_TEST_ON_CREATE, false);

			if (testOnBorrow || testOnCreate || testWhileIdle || testOnReturn) {
				populatePoolValidationProperties(builder, pooling2Element);
			}

			return builder.getBeanDefinition();
		}
		else {
			BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PoolingContextSource.class);
			builder.addPropertyValue("contextSource", targetContextSourceDefinition);

			builder.addPropertyValue("maxActive",
					ParserUtils.getString(poolingElement, ATT_MAX_ACTIVE, String.valueOf(DEFAULT_MAX_ACTIVE)));
			builder.addPropertyValue("maxTotal",
					ParserUtils.getString(poolingElement, ATT_MAX_TOTAL, String.valueOf(DEFAULT_MAX_TOTAL)));
			builder.addPropertyValue("maxIdle",
					ParserUtils.getString(poolingElement, ATT_MAX_IDLE, String.valueOf(DEFAULT_MAX_IDLE)));
			builder.addPropertyValue("minIdle",
					ParserUtils.getString(poolingElement, ATT_MIN_IDLE, String.valueOf(DEFAULT_MIN_IDLE)));
			builder.addPropertyValue("maxWait",
					ParserUtils.getString(poolingElement, ATT_MAX_WAIT, String.valueOf(DEFAULT_MAX_WAIT)));
			String whenExhausted = ParserUtils.getString(poolingElement, ATT_WHEN_EXHAUSTED,
					PoolExhaustedAction.BLOCK.name());
			builder.addPropertyValue("whenExhaustedAction", PoolExhaustedAction.valueOf(whenExhausted).getValue());
			builder.addPropertyValue("timeBetweenEvictionRunsMillis", ParserUtils.getString(poolingElement,
					ATT_EVICTION_RUN_MILLIS, String.valueOf(DEFAULT_EVICTION_RUN_MILLIS)));
			builder.addPropertyValue("minEvictableIdleTimeMillis", ParserUtils.getString(poolingElement,
					ATT_EVICTABLE_TIME_MILLIS, String.valueOf(DEFAULT_EVICTABLE_MILLIS)));
			builder.addPropertyValue("numTestsPerEvictionRun", ParserUtils.getString(poolingElement,
					ATT_TESTS_PER_EVICTION_RUN, String.valueOf(DEFAULT_TESTS_PER_EVICTION_RUN)));

			boolean testOnBorrow = ParserUtils.getBoolean(poolingElement, ATT_TEST_ON_BORROW, false);
			boolean testOnReturn = ParserUtils.getBoolean(poolingElement, ATT_TEST_ON_RETURN, false);
			boolean testWhileIdle = ParserUtils.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", ParserUtils.getString(element, ATT_VALIDATION_QUERY_BASE, ""));
		validatorBuilder.addPropertyValue("filter",
				ParserUtils.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",
				ParserUtils.getString(element, ATT_EVICTION_RUN_MILLIS, String.valueOf(DEFAULT_EVICTION_RUN_MILLIS)));
		builder.addPropertyValue("numTestsPerEvictionRun",
				ParserUtils.getInt(element, ATT_TESTS_PER_EVICTION_RUN, DEFAULT_TESTS_PER_EVICTION_RUN));
		builder.addPropertyValue("minEvictableIdleTimeMillis",
				ParserUtils.getString(element, ATT_EVICTABLE_TIME_MILLIS, String.valueOf(DEFAULT_EVICTABLE_MILLIS)));

		String nonTransientExceptions = ParserUtils.getString(element, ATT_NON_TRANSIENT_EXCEPTIONS,
				CommunicationException.class.getName());
		String[] strings = StringUtils.commaDelimitedListToStringArray(nonTransientExceptions);
		Set> nonTransientExceptionClasses = new HashSet>();
		for (String className : strings) {
			try {
				nonTransientExceptionClasses.add(ClassUtils.getDefaultClassLoader().loadClass(className));
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalArgumentException(String.format("%s is not a valid class name", className), ex);
			}
		}

		builder.addPropertyValue("nonTransientExceptions", nonTransientExceptionClasses);
	}

	private void populatePoolValidationProperties(BeanDefinitionBuilder builder, Element element) {

		BeanDefinitionBuilder validatorBuilder = BeanDefinitionBuilder
				.rootBeanDefinition(org.springframework.ldap.pool2.validation.DefaultDirContextValidator.class);
		validatorBuilder.addPropertyValue("base", ParserUtils.getString(element, ATT_VALIDATION_QUERY_BASE, ""));
		validatorBuilder.addPropertyValue("filter", ParserUtils.getString(element, ATT_VALIDATION_QUERY_FILTER,
				org.springframework.ldap.pool2.validation.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());

		String nonTransientExceptions = ParserUtils.getString(element, ATT_NON_TRANSIENT_EXCEPTIONS,
				CommunicationException.class.getName());
		String[] strings = StringUtils.commaDelimitedListToStringArray(nonTransientExceptions);
		Set> nonTransientExceptionClasses = new HashSet>();
		for (String className : strings) {
			try {
				nonTransientExceptionClasses.add(ClassUtils.getDefaultClassLoader().loadClass(className));
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalArgumentException(String.format("%s is not a valid class name", className), ex);
			}
		}

		builder.addPropertyValue("nonTransientExceptions", nonTransientExceptionClasses);
	}

	private void populatePoolConfigProperties(BeanDefinitionBuilder builder, Element element) {
		BeanDefinitionBuilder configBuilder = BeanDefinitionBuilder.rootBeanDefinition(PoolConfig.class);

		configBuilder.addPropertyValue("maxTotal",
				ParserUtils.getString(element, ATT_MAX_TOTAL, String.valueOf(DEFAULT_MAX_TOTAL)));
		configBuilder.addPropertyValue("maxTotalPerKey",
				ParserUtils.getString(element, ATT_MAX_TOTAL_PER_KEY, String.valueOf(DEFAULT_MAX_TOTAL_PER_KEY)));
		configBuilder.addPropertyValue("maxIdlePerKey",
				ParserUtils.getString(element, ATT_MAX_IDLE_PER_KEY, String.valueOf(DEFAULT_MAX_IDLE_PER_KEY)));
		configBuilder.addPropertyValue("minIdlePerKey",
				ParserUtils.getString(element, ATT_MIN_IDLE_PER_KEY, String.valueOf(DEFAULT_MIN_IDLE_PER_KEY)));
		configBuilder.addPropertyValue("evictionPolicyClassName",
				ParserUtils.getString(element, ATT_EVICTION_POLICY_CLASS, DEFAULT_EVICTION_POLICY_CLASS_NAME));
		configBuilder.addPropertyValue("fairness", ParserUtils.getBoolean(element, ATT_FAIRNESS, DEFAULT_FAIRNESS));
		configBuilder.addPropertyValue("jmxEnabled",
				ParserUtils.getBoolean(element, ATT_JMX_ENABLE, DEFAULT_JMX_ENABLE));
		configBuilder.addPropertyValue("jmxNameBase",
				ParserUtils.getString(element, ATT_JMX_NAME_BASE, DEFAULT_JMX_NAME_BASE));
		configBuilder.addPropertyValue("jmxNamePrefix",
				ParserUtils.getString(element, ATT_JMX_NAME_PREFIX, DEFAULT_JMX_NAME_PREFIX));
		configBuilder.addPropertyValue("lifo", ParserUtils.getBoolean(element, ATT_LIFO, DEFAULT_LIFO));
		configBuilder.addPropertyValue("maxWaitMillis",
				ParserUtils.getString(element, ATT_MAX_WAIT, String.valueOf(DEFAULT_MAX_WAIT_MILLIS)));
		configBuilder.addPropertyValue("blockWhenExhausted", Boolean.valueOf(ParserUtils.getString(element,
				ATT_BLOCK_WHEN_EXHAUSTED, String.valueOf(DEFAULT_BLOCK_WHEN_EXHAUSTED))));
		configBuilder.addPropertyValue("testOnBorrow", ParserUtils.getBoolean(element, ATT_TEST_ON_BORROW, false));
		configBuilder.addPropertyValue("testOnCreate", ParserUtils.getBoolean(element, ATT_TEST_ON_CREATE, false));
		configBuilder.addPropertyValue("testOnReturn", ParserUtils.getBoolean(element, ATT_TEST_ON_RETURN, false));
		configBuilder.addPropertyValue("testWhileIdle", ParserUtils.getBoolean(element, ATT_TEST_WHILE_IDLE, false));
		configBuilder.addPropertyValue("timeBetweenEvictionRunsMillis",
				ParserUtils.getString(element, ATT_EVICTION_RUN_MILLIS, String.valueOf(DEFAULT_EVICTION_RUN_MILLIS)));
		configBuilder.addPropertyValue("numTestsPerEvictionRun", ParserUtils.getString(element,
				ATT_TESTS_PER_EVICTION_RUN, String.valueOf(DEFAULT_TESTS_PER_EVICTION_RUN)));
		configBuilder.addPropertyValue("minEvictableIdleTimeMillis",
				ParserUtils.getString(element, ATT_EVICTABLE_TIME_MILLIS, String.valueOf(DEFAULT_EVICTABLE_MILLIS)));
		configBuilder.addPropertyValue("softMinEvictableIdleTimeMillis", ParserUtils.getString(element,
				ATT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS, String.valueOf(DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS)));

		builder.addConstructorArgValue(configBuilder.getBeanDefinition());
	}

	static class UrlsFactory {

		static String[] urls(String value) {
			return StringUtils.commaDelimitedListToStringArray(value);
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy