org.springframework.data.jdbc.config.oracle.PoolingDataSourceBeanDefinitionParser.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.data.jdbc.config.oracle.PoolingDataSourceBeanDefinitionParser.java

Source

/*
 * Copyright 2008-2014 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.data.jdbc.config.oracle;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;

/**
 * BeanDefinitionParser for the "pooling-data-source" element of the "orcl" name space
 *
 * @author Thomas Risberg
 * @since 1.0
 */
public class PoolingDataSourceBeanDefinitionParser extends AbstractBeanDefinitionParser {

    private static final String DEFAULT_PROPERTY_FILE_LOCATION = "classpath:orcl.properties";
    private static final String DEFAULT_PROPERTY_PREFIX = null;
    private static final String DEFAULT_CONNECTION_CACHING_ENABLED = "true";

    // Property file attributes
    private static final String CONNECTION_PROPERTIES_CHILD_ELEMENT = "connection-properties";
    private static final String CONNECTION_CACHE_PROPERTIES_CHILD_ELEMENT = "connection-cache-properties";
    private static final String USERNAME_CONNECTION_PROXY = "username-connection-proxy";
    private static final String CONNECTION_CONTEXT_PROVIDER = "connection-context-provider";
    private static final String PROPERTIES_LOCATION_ATTRIBUTE = "properties-location";
    private static final String CONNECTION_PROPERTIES_PREFIX_ATTRIBUTE = "connection-properties-prefix";
    private static final String CONNECTON_CACHE_PROPERTIS_PREFIX_ATTRIBUTE = "connection-cache-properties-prefix";

    // Required attributes
    private static final String URL_ATTRIBUTE = "url";

    // Optional attributes
    private static final String USERNAME_ATTRIBUTE = "username";
    private static final String PASSWORD_ATTRIBUTE = "password";
    private static final String ONS_CONFIGURATION_ATTRIBUTE = "ONS-configuration";
    private static final String FAST_CONNECTION_FAILOVER_ENABLED_ATTRIBUTE = "fast-connection-failover-enabled";
    private static final String CONNECTION_CACHING_ENABLED_ATTRIBUTE = "connection-caching-enabled";

    protected final Log logger = LogFactory.getLog(getClass());

    private Map<String, String> attributeToPropertyMap = new HashMap<String, String>();

    public PoolingDataSourceBeanDefinitionParser() {
        attributeToPropertyMap.put(URL_ATTRIBUTE, "url");
        attributeToPropertyMap.put(USERNAME_ATTRIBUTE, "user");
        attributeToPropertyMap.put(PASSWORD_ATTRIBUTE, "password");
        attributeToPropertyMap.put(CONNECTION_CACHING_ENABLED_ATTRIBUTE, "connectionCachingEnabled");
        attributeToPropertyMap.put(FAST_CONNECTION_FAILOVER_ENABLED_ATTRIBUTE, "fastConnectionFailoverEnabled");
        attributeToPropertyMap.put(ONS_CONFIGURATION_ATTRIBUTE, "ONSConfiguration");
    }

    protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        //ToDo look for username-connection-proxy
        boolean useWrapper = false;
        String connectionContextProviderRef = null;
        Element usernameConnectionProxyElement = DomUtils.getChildElementByTagName(element,
                USERNAME_CONNECTION_PROXY);
        if (usernameConnectionProxyElement != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Using username-connection-proxy");
            }
            if (usernameConnectionProxyElement.hasAttribute(CONNECTION_CONTEXT_PROVIDER)) {
                if (logger.isDebugEnabled()) {
                    logger.debug(CONNECTION_CONTEXT_PROVIDER + ": "
                            + usernameConnectionProxyElement.getAttribute(CONNECTION_CONTEXT_PROVIDER));
                }
                connectionContextProviderRef = usernameConnectionProxyElement
                        .getAttribute(CONNECTION_CONTEXT_PROVIDER);
            }
            useWrapper = true;
            //builder.addPropertyValue("connectionProperties", connProperties);
        }

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        builder.getRawBeanDefinition().setBeanClassName(getBeanClassName(element));
        builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
        builder.getRawBeanDefinition().setDestroyMethodName("close");
        if (parserContext.isNested()) {
            // Inner bean definition must receive same scope as containing bean.
            builder.setScope(parserContext.getContainingBeanDefinition().getScope());
        }
        if (parserContext.isDefaultLazyInit()) {
            // Default-lazy-init applies to custom bean definitions as well.
            builder.setLazyInit(true);
        }
        doParse(element, parserContext, builder);
        if (useWrapper) {
            BeanDefinitionBuilder wrapper = BeanDefinitionBuilder.genericBeanDefinition();
            wrapper.getRawBeanDefinition()
                    .setBeanClassName("org.springframework.data.jdbc.support.oracle.ProxyDataSource");
            wrapper.addConstructorArgValue(builder.getBeanDefinition());
            if (connectionContextProviderRef == null) {
                wrapper.addConstructorArgValue(null);
            } else {
                wrapper.addConstructorArgReference(connectionContextProviderRef);
            }
            return wrapper.getBeanDefinition();
        } else {
            return builder.getBeanDefinition();
        }
    }

    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        ResourceLoader rl = parserContext.getReaderContext().getResourceLoader();
        //attributes
        String propertyFileLocation = element.getAttribute(PROPERTIES_LOCATION_ATTRIBUTE);
        String connectionPropertyFileLocation = element.getAttribute(PROPERTIES_LOCATION_ATTRIBUTE);

        String connectionPropertyPrefix = element.getAttribute(CONNECTION_PROPERTIES_PREFIX_ATTRIBUTE);
        String cachingPropertyPrefix = element.getAttribute(CONNECTON_CACHE_PROPERTIS_PREFIX_ATTRIBUTE);
        String url = element.getAttribute(URL_ATTRIBUTE);
        String username = element.getAttribute(USERNAME_ATTRIBUTE);
        String password = element.getAttribute(PASSWORD_ATTRIBUTE);
        String onsConfiguration = element.getAttribute(ONS_CONFIGURATION_ATTRIBUTE);
        String fastConnectionFailoverEnabled = element.getAttribute(FAST_CONNECTION_FAILOVER_ENABLED_ATTRIBUTE);
        String connectionCachingEnabled = element.getAttribute(CONNECTION_CACHING_ENABLED_ATTRIBUTE);

        boolean propertyFileProvided = false;

        Map<String, Object> providedProperties = new HashMap<String, Object>();

        // defaults
        if (!StringUtils.hasText(propertyFileLocation) && !StringUtils.hasText(connectionPropertyFileLocation)) {
            propertyFileLocation = DEFAULT_PROPERTY_FILE_LOCATION;
        }
        if (!StringUtils.hasText(connectionPropertyPrefix)) {
            connectionPropertyPrefix = DEFAULT_PROPERTY_PREFIX;
        }

        // look for property files
        if (StringUtils.hasText(propertyFileLocation)) {
            logger.debug("Using properties location: " + propertyFileLocation);
            String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(propertyFileLocation);
            Resource r = rl.getResource(resolvedLocation);
            logger.debug("Loading properties from resource: " + r);
            PropertiesFactoryBean factoryBean = new PropertiesFactoryBean();
            factoryBean.setLocation(r);
            try {
                factoryBean.afterPropertiesSet();
                Properties resource = factoryBean.getObject();
                for (Map.Entry<Object, Object> entry : resource.entrySet()) {
                    providedProperties.put((String) entry.getKey(), entry.getValue());
                }
                propertyFileProvided = true;
            } catch (FileNotFoundException e) {
                propertyFileProvided = false;
                if (propertyFileLocation.equals(DEFAULT_PROPERTY_FILE_LOCATION)) {
                    logger.debug("Unable to find " + propertyFileLocation);
                } else {
                    parserContext.getReaderContext()
                            .error("pooling-datasource defined with attribute '" + PROPERTIES_LOCATION_ATTRIBUTE
                                    + "' but the property file was not found at location \"" + propertyFileLocation
                                    + "\"", element);
                }
            } catch (IOException e) {
                logger.warn("Error loading " + propertyFileLocation + ": " + e.getMessage());
            }
        } else {
            propertyFileProvided = false;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Using provided properties: " + providedProperties);
        }

        if (connectionPropertyPrefix == null) {
            connectionPropertyPrefix = "";
        }
        if (connectionPropertyPrefix.length() > 0 && !connectionPropertyPrefix.endsWith(".")) {
            connectionPropertyPrefix = connectionPropertyPrefix + ".";
        }
        logger.debug("Using connection properties prefix: " + connectionPropertyPrefix);

        if (cachingPropertyPrefix == null) {
            cachingPropertyPrefix = "";
        }
        if (cachingPropertyPrefix.length() > 0 && !cachingPropertyPrefix.endsWith(".")) {
            cachingPropertyPrefix = cachingPropertyPrefix + ".";
        }
        logger.debug("Using caching properties prefix: " + cachingPropertyPrefix);

        if (!(StringUtils.hasText(connectionCachingEnabled) || providedProperties
                .containsKey(attributeToPropertyMap.get(CONNECTION_CACHING_ENABLED_ATTRIBUTE)))) {
            connectionCachingEnabled = DEFAULT_CONNECTION_CACHING_ENABLED;
        }

        setRequiredAttribute(builder, parserContext, element, providedProperties, connectionPropertyPrefix,
                propertyFileProvided, url, URL_ATTRIBUTE, "URL");
        setOptionalAttribute(builder, providedProperties, connectionPropertyPrefix, username, USERNAME_ATTRIBUTE);
        setOptionalAttribute(builder, providedProperties, connectionPropertyPrefix, password, PASSWORD_ATTRIBUTE);
        setOptionalAttribute(builder, providedProperties, connectionPropertyPrefix, connectionCachingEnabled,
                CONNECTION_CACHING_ENABLED_ATTRIBUTE);
        setOptionalAttribute(builder, providedProperties, connectionPropertyPrefix, fastConnectionFailoverEnabled,
                FAST_CONNECTION_FAILOVER_ENABLED_ATTRIBUTE);
        setOptionalAttribute(builder, providedProperties, connectionPropertyPrefix, onsConfiguration,
                ONS_CONFIGURATION_ATTRIBUTE);

        Properties providedConnectionProperties = new Properties();
        Properties providedCachingProperties = new Properties();
        for (String key : providedProperties.keySet()) {
            if (StringUtils.hasText(connectionPropertyPrefix) && key.startsWith(connectionPropertyPrefix)) {
                String newKey = key.substring(connectionPropertyPrefix.length());
                providedConnectionProperties.put(newKey, providedProperties.get(key));
            } else {
                if (StringUtils.hasText(cachingPropertyPrefix) && key.startsWith(cachingPropertyPrefix)) {
                    String newKey = key.substring(cachingPropertyPrefix.length());
                    providedCachingProperties.put(newKey, providedProperties.get(key));
                } else {
                    providedConnectionProperties.put(key, providedProperties.get(key));
                }
            }
        }

        // look for connectionProperties
        Object connProperties = DomUtils.getChildElementValueByTagName(element,
                CONNECTION_PROPERTIES_CHILD_ELEMENT);
        if (connProperties != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Using connection-properties");
            }
            builder.addPropertyValue("connectionProperties", connProperties);
        } else {
            if (providedConnectionProperties.size() > 0) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Using provided connection properties: " + providedConnectionProperties);
                }
                builder.addPropertyValue("connectionProperties", providedConnectionProperties);
            }
        }

        // look for connectionCacheProperties
        Object cacheProperties = DomUtils.getChildElementValueByTagName(element,
                CONNECTION_CACHE_PROPERTIES_CHILD_ELEMENT);
        if (cacheProperties != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Using connection-cache-properties: [" + cacheProperties + "]");
            }
            builder.addPropertyValue("connectionCacheProperties", cacheProperties);
        } else {
            if (providedCachingProperties.size() > 0) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Using provided caching properties: " + providedCachingProperties);
                }
                builder.addPropertyValue("connectionCacheProperties", providedCachingProperties);
            }
        }

        builder.setRole(BeanDefinition.ROLE_SUPPORT);
    }

    protected String getBeanClassName(Element element) {
        return "oracle.jdbc.pool.OracleDataSource";
    }

    protected boolean shouldGenerateId() {
        return false;
    }

    private void setRequiredAttribute(BeanDefinitionBuilder builder, ParserContext parserContext, Element element,
            Map<String, Object> providedProperties, String propertyPrefix, boolean propertyFileProvided,
            String attributeValue, String attributeName, String orclPropertyName) {

        String propertyToRemove = null;
        String propertyKey = propertyPrefix != null ? propertyPrefix + attributeName : attributeName;
        String orclKey = propertyPrefix != null ? propertyPrefix + orclPropertyName : orclPropertyName;
        if (StringUtils.hasText(attributeValue)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Registering required attribute " + orclPropertyName + " with attribute value "
                        + attributeValue);
            }
            builder.addPropertyValue(orclPropertyName, attributeValue);
        } else if (providedProperties.containsKey(propertyKey) || providedProperties.containsKey(orclKey)) {
            Object value;
            if (providedProperties.containsKey(propertyKey)) {
                value = providedProperties.get(propertyKey);
                propertyToRemove = propertyKey;
            } else {
                value = providedProperties.get(orclKey);
                propertyToRemove = orclKey;
            }
            if (logger.isDebugEnabled()) {
                logger.debug(
                        "Registering required attribute " + orclPropertyName + " with property value " + value);
            }
            builder.addPropertyValue(orclPropertyName, value);
        } else {
            if (propertyFileProvided) {
                parserContext.getReaderContext()
                        .error("pooling-datasource defined without the required '" + attributeName
                                + "' attribute and the property file does not contain a \""
                                + attributeToPropertyMap.get(attributeName) + "\" entry", element);
            } else {
                parserContext.getReaderContext()
                        .error("pooling-datasource defined without the required '" + attributeName
                                + "' attribute and a property file was not found at location \""
                                + DEFAULT_PROPERTY_FILE_LOCATION + "\"", element);
            }
        }
        if (propertyToRemove != null) {
            removeProvidedProperty(providedProperties, propertyToRemove);
        }
    }

    private void setOptionalAttribute(BeanDefinitionBuilder builder, Map<String, Object> providedProperties,
            String propertyPrefix, String attributeValue, String attributeName) {
        String propertyKey;
        if ("username".equals(attributeName)) {
            String userKey = (propertyPrefix != null ? propertyPrefix + "user" : "user");
            if (providedProperties.containsKey(userKey)) {
                propertyKey = userKey;
            } else {
                propertyKey = (propertyPrefix != null ? propertyPrefix + attributeName : attributeName);
            }
        } else {
            propertyKey = (propertyPrefix != null ? propertyPrefix + attributeToPropertyMap.get(attributeName)
                    : attributeToPropertyMap.get(attributeName));
        }

        if (StringUtils.hasText(attributeValue)) {
            if (logger.isDebugEnabled()) {
                if ("password".equals(attributeName)) {
                    logger.debug("Registering optional attribute " + attributeToPropertyMap.get(attributeName)
                            + " with attribute value ******");
                } else {
                    logger.debug("Registering optional attribute " + attributeToPropertyMap.get(attributeName)
                            + " with attribute value " + attributeValue);
                }
            }
            builder.addPropertyValue(attributeToPropertyMap.get(attributeName), attributeValue);
        } else if (providedProperties.containsKey(propertyKey)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Registering optional attribute " + attributeToPropertyMap.get(attributeName)
                        + " with property value "
                        + ("password".equals(attributeName) ? "******" : providedProperties.get(propertyKey)));
            }
            builder.addPropertyValue(attributeToPropertyMap.get(attributeName),
                    providedProperties.get(propertyKey));
        }
        removeProvidedProperty(providedProperties, propertyKey);
    }

    private void removeProvidedProperty(Map<String, Object> providedProperties, String attributeName) {
        if (providedProperties.containsKey(attributeName)) {
            providedProperties.remove(attributeName);
        }
    }

    protected void registerBeanDefinition(BeanDefinitionHolder beanDefinitionHolder,
            BeanDefinitionRegistry beanDefinitionRegistry) {
        //register other beans here if necessary
        super.registerBeanDefinition(beanDefinitionHolder, beanDefinitionRegistry);
    }

}