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

org.dbunit.database.DatabaseConfig Maven / Gradle / Ivy

Go to download

dbUnit is a JUnit extension (also usable from Ant and Maven) targeted for database-driven projects that, among other things, puts your database into a known state between test runs. This is an excellent way to avoid the myriad of problems that can occur when one test case corrupts the database and causes subsequent tests to fail or exacerbate the damage.

There is a newer version: 2.8.0
Show newest version
/*
 *
 * The DbUnit Database Testing Framework
 * Copyright (C)2002-2004, DbUnit.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
package org.dbunit.database;

import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import org.dbunit.DatabaseUnitException;
import org.dbunit.database.statement.IStatementFactory;
import org.dbunit.database.statement.PreparedStatementFactory;
import org.dbunit.dataset.datatype.DefaultDataTypeFactory;
import org.dbunit.dataset.datatype.IDataTypeFactory;
import org.dbunit.dataset.filter.IColumnFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Configuration used by the {@link DatabaseConnection}.
 * 
 * @author manuel.laflamme
 * @author gommma (gommma AT users.sourceforge.net)
 * @author Last changed by: $Author: gommma $
 * @version $Revision: 1193 $ $Date: 2010-06-29 16:01:46 -0500 (Tue, 29 Jun 2010) $
 * @since 2.0
 */
public class DatabaseConfig
{

    /**
     * Logger for this class
     */
    private static final Logger logger = LoggerFactory.getLogger(DatabaseConfig.class);

    public static final String PROPERTY_STATEMENT_FACTORY =
            "http://www.dbunit.org/properties/statementFactory";
    public static final String PROPERTY_RESULTSET_TABLE_FACTORY =
            "http://www.dbunit.org/properties/resultSetTableFactory";
    public static final String PROPERTY_DATATYPE_FACTORY =
            "http://www.dbunit.org/properties/datatypeFactory";
    public static final String PROPERTY_ESCAPE_PATTERN =
            "http://www.dbunit.org/properties/escapePattern";
    public static final String PROPERTY_TABLE_TYPE =
            "http://www.dbunit.org/properties/tableType";
    public static final String PROPERTY_PRIMARY_KEY_FILTER =
            "http://www.dbunit.org/properties/primaryKeyFilter";
    public static final String PROPERTY_BATCH_SIZE =
    		"http://www.dbunit.org/properties/batchSize";
	public static final String PROPERTY_FETCH_SIZE = 
			"http://www.dbunit.org/properties/fetchSize";
	public static final String PROPERTY_METADATA_HANDLER =
	        "http://www.dbunit.org/properties/metadataHandler";

    public static final String FEATURE_CASE_SENSITIVE_TABLE_NAMES =
        "http://www.dbunit.org/features/caseSensitiveTableNames";
    public static final String FEATURE_QUALIFIED_TABLE_NAMES =
        "http://www.dbunit.org/features/qualifiedTableNames";
    public static final String FEATURE_BATCHED_STATEMENTS =
        "http://www.dbunit.org/features/batchedStatements";
    public static final String FEATURE_DATATYPE_WARNING =
        "http://www.dbunit.org/features/datatypeWarning";
    public static final String FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES =
        "http://www.dbunit.org/features/skipOracleRecycleBinTables";

    /**
     * A list of all properties as {@link ConfigProperty} objects. 
     * The objects contain the allowed java type and whether or not a property is nullable.
     */
    public static final ConfigProperty[] ALL_PROPERTIES = new ConfigProperty[] {
        new ConfigProperty(PROPERTY_STATEMENT_FACTORY, IStatementFactory.class, false),
        new ConfigProperty(PROPERTY_RESULTSET_TABLE_FACTORY, IResultSetTableFactory.class, false),
        new ConfigProperty(PROPERTY_DATATYPE_FACTORY, IDataTypeFactory.class, false),
        new ConfigProperty(PROPERTY_ESCAPE_PATTERN, String.class, true),
        new ConfigProperty(PROPERTY_TABLE_TYPE, String[].class, false),
        new ConfigProperty(PROPERTY_PRIMARY_KEY_FILTER, IColumnFilter.class, true),
        new ConfigProperty(PROPERTY_BATCH_SIZE, Integer.class, false),
        new ConfigProperty(PROPERTY_FETCH_SIZE, Integer.class, false),
        new ConfigProperty(PROPERTY_METADATA_HANDLER, IMetadataHandler.class, false),
        new ConfigProperty(FEATURE_CASE_SENSITIVE_TABLE_NAMES, Boolean.class, false),
        new ConfigProperty(FEATURE_QUALIFIED_TABLE_NAMES, Boolean.class, false),
        new ConfigProperty(FEATURE_BATCHED_STATEMENTS, Boolean.class, false),
        new ConfigProperty(FEATURE_DATATYPE_WARNING, Boolean.class, false),
        new ConfigProperty(FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES, Boolean.class, false),
    };

    /**
     * A list of all features as strings
     * @deprecated since 2.4.7 Use the {@link #ALL_PROPERTIES} where features are listed now as well
     */
    public static final String[] ALL_FEATURES = new String[] {
        FEATURE_CASE_SENSITIVE_TABLE_NAMES,
        FEATURE_QUALIFIED_TABLE_NAMES,
        FEATURE_BATCHED_STATEMENTS,
        FEATURE_DATATYPE_WARNING,
        FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES
    };
    
    private static final DefaultDataTypeFactory DEFAULT_DATA_TYPE_FACTORY =
            new DefaultDataTypeFactory();
    private static final PreparedStatementFactory PREPARED_STATEMENT_FACTORY =
            new PreparedStatementFactory();
    private static final CachedResultSetTableFactory RESULT_SET_TABLE_FACTORY =
            new CachedResultSetTableFactory();
    private static final String DEFAULT_ESCAPE_PATTERN = null;
    private static final String[] DEFAULT_TABLE_TYPE = {"TABLE"};
    private static final Integer DEFAULT_BATCH_SIZE = new Integer(100);
    private static final Integer DEFAULT_FETCH_SIZE = new Integer(100);



    private Map _propertyMap = new HashMap();
    
    private final Configurator configurator;

    public DatabaseConfig()
    {
        setFeature(FEATURE_BATCHED_STATEMENTS, false);
        setFeature(FEATURE_QUALIFIED_TABLE_NAMES, false);
        setFeature(FEATURE_CASE_SENSITIVE_TABLE_NAMES, false);
        setFeature(FEATURE_DATATYPE_WARNING, true);

        setProperty(PROPERTY_STATEMENT_FACTORY, PREPARED_STATEMENT_FACTORY);
        setProperty(PROPERTY_RESULTSET_TABLE_FACTORY, RESULT_SET_TABLE_FACTORY);
        setProperty(PROPERTY_DATATYPE_FACTORY, DEFAULT_DATA_TYPE_FACTORY);
        setProperty(PROPERTY_ESCAPE_PATTERN, DEFAULT_ESCAPE_PATTERN);
        setProperty(PROPERTY_TABLE_TYPE, DEFAULT_TABLE_TYPE);
        setProperty(PROPERTY_BATCH_SIZE, DEFAULT_BATCH_SIZE);
        setProperty(PROPERTY_FETCH_SIZE, DEFAULT_FETCH_SIZE);
        setProperty(PROPERTY_METADATA_HANDLER, new DefaultMetadataHandler());

        this.configurator = new Configurator(this);
    }

    /**
     * @return The configurator of this database config
     */
    protected Configurator getConfigurator() 
    {
        return configurator;
    }

    /**
     * Set the value of a feature flag.
     *
     * @param name the feature id
     * @param value the feature status
     * @deprecated since 2.4.7 Use the {@link #setProperty(String, Object)} also for features
     */
    public void setFeature(String name, boolean value)
    {
        logger.trace("setFeature(name={}, value={}) - start", name, String.valueOf(value));

        setProperty(name, Boolean.valueOf(value));
    }

    /**
     * Look up the value of a feature flag.
     *
     * @param name the feature id
     * @return the feature status
     * @deprecated since 2.4.7 Use the {@link #getProperty(String)} where features are listed now as well
     */
    public boolean getFeature(String name)
    {
        logger.trace("getFeature(name={}) - start", name);
        
        Object property = getProperty(name);
        if(property == null)
        {
            return false;
        }
        else if(property instanceof Boolean)
        {
            Boolean feature = (Boolean) property;
            return feature.booleanValue();
        }
        else
        {
            String propString = String.valueOf(property);
            Boolean feature = Boolean.valueOf(propString);
            return feature.booleanValue();
        }
    }

    /**
     * Set the value of a property.
     *
     * @param name the property id
     * @param value the property value
     */
    public void setProperty(String name, Object value)
    {
        logger.trace("setProperty(name={}, value={}) - start", name, value);
        
        value = convertIfNeeded(name, value);
        
        // Validate if the type of the given object is correct
        checkObjectAllowed(name, value);
        
        // If we get here the type is allowed (no exception was thrown)
        _propertyMap.put(name, value);
    }

    /**
     * Look up the value of a property.
     *
     * @param name the property id
     * @return the property value
     */
    public Object getProperty(String name)
    {
        logger.trace("getProperty(name={}) - start", name);

        return _propertyMap.get(name);
    }

    private Object convertIfNeeded(String property, Object value) 
    {
        logger.trace("convertIfNeeded(property={}, value={}) - start", property, value);

        ConfigProperty prop = findByName(property);
        if(prop==null) {
            throw new IllegalArgumentException("Did not find property with name '" + property + "'");
        }
        Class allowedPropType = prop.getPropertyType();

        if(allowedPropType == Boolean.class || allowedPropType == boolean.class)
        {
            // String -> Boolean is a special mapping which is allowed
            if(value instanceof String)
            {
                return Boolean.valueOf((String)value);
            }
        }
        
        return value;
    }

    /**
     * Checks whether the given value has the correct java type for the given property.
     * If the value is not allowed for the given property an {@link IllegalArgumentException} is thrown.
     * @param property The property to be set
     * @param value The value to which the property should be set
     */
    protected void checkObjectAllowed(String property, Object value)
    {
        logger.trace("checkObjectAllowed(property={}, value={}) - start", property, value);

        ConfigProperty prop = findByName(property);
        
        if(prop != null)
        {
            // First check for null
            if(value == null)
            {
                if(prop.isNullable())
                {
                    // All right. No class check is needed
                    return;
                }
                else
                {
                    throw new IllegalArgumentException("The property '" + property + "' is not nullable.");
                }
            }
            else
            {
                Class allowedPropType = prop.getPropertyType();
                if(!allowedPropType.isAssignableFrom(value.getClass()))
                {
                    throw new IllegalArgumentException("Cannot cast object of type '" + value.getClass() + 
                            "' to allowed type '" + allowedPropType + "'.");
                }
            }
        }
        else
        {
            logger.info("Unknown property '" + property + "'. Cannot validate the type of the object to be set." +
                    " Please notify a developer to update the list of properties.");
        }
    }
    
    /**
     * Sets the given properties on the {@link DatabaseConfig} instance using the given String values.
     * This is useful to set properties configured as strings by a build tool like ant or maven. 
     * If the required property type is an object it uses reflection to create an instance of the class
     * specified as string.
     * @param stringProperties The properties as strings. The key of the properties can be either the long or
     * the short name.
     * @throws DatabaseUnitException 
     */
    public void setPropertiesByString(Properties stringProperties) throws DatabaseUnitException
    {
        for (Iterator iterator = stringProperties.entrySet().iterator(); iterator.hasNext();) {
            Map.Entry entry = (Map.Entry) iterator.next();
            
            String propKey = (String)entry.getKey();
            String propValue = (String)entry.getValue();

            ConfigProperty dbunitProp = DatabaseConfig.findByName(propKey);
            if(dbunitProp == null)
            {
                logger.debug("Did not find long name property {} - trying short name...", entry);
                dbunitProp = DatabaseConfig.findByShortName(propKey);
            }

            if(dbunitProp == null)
            {
                logger.info("Could not set property '" + entry + "' - not found in the list of known properties.");
            }
            else
            {
                String fullPropName = dbunitProp.getProperty();
                Object obj = createObjectFromString(dbunitProp, propValue);
                this.setProperty(fullPropName, obj);
            }
        }
    }
    
    private Object createObjectFromString(ConfigProperty dbunitProp, String propValue) 
    throws DatabaseUnitException 
    {
        if (dbunitProp == null) {
            throw new NullPointerException(
                    "The parameter 'dbunitProp' must not be null");
        }
        if (propValue == null) {
            // Null must not be casted
            return null;
        }
        
        Class targetClass = dbunitProp.getPropertyType();
        if(targetClass == String.class)
        {
            return propValue;
        }
        else if(targetClass == Boolean.class)
        {
            return Boolean.valueOf(propValue);
        }
        else if(targetClass == String[].class)
        {
            String[] result = propValue.split(",");
            for (int i = 0; i < result.length; i++) {
                result[i] = result[i].trim();
            }
            return result;
        }
        else if(targetClass == Integer.class)
        {
            return new Integer(propValue);
        }
        else
        {
            // Try via reflection
            return createInstance(propValue);
        }
    }

    private Object createInstance(String className) throws DatabaseUnitException 
    {
        // Setup data type factory for example.
        try
        {
            Object o = Class.forName(className).newInstance();
            return o;
        }
        catch (ClassNotFoundException e)
        {
            throw new DatabaseUnitException(
                    "Class Not Found: '" + className + "' could not be loaded", e);
        }
        catch (IllegalAccessException e)
        {
            throw new DatabaseUnitException(
                    "Illegal Access: '" + className + "' could not be loaded", e);
        }
        catch (InstantiationException e)
        {
            throw new DatabaseUnitException(
                    "Instantiation Exception: '" + className + "' could not be loaded", e);
        }
    }

    /**
     * Searches the {@link ConfigProperty} object for the property with the given name
     * @param property The property for which the enumerated object should be resolved
     * @return The property object or null if it was not found.
     */
    public static final ConfigProperty findByName(String property) 
    {
        for (int i = 0; i < ALL_PROPERTIES.length; i++) {
            if(ALL_PROPERTIES[i].getProperty().equals(property))
            {
                return ALL_PROPERTIES[i];
            }
        }
        // property not found.
        return null;
    }
    
    /**
     * Searches the {@link ConfigProperty} object for the property with the given name
     * @param propShortName The property short name for which the enumerated object should be resolved.
     * Example: the short name of {@value #PROPERTY_FETCH_SIZE} is fetchSize which is the
     * last part of the fully qualified URL.
     * @return The property object or null if it was not found.
     */
    public static final ConfigProperty findByShortName(String propShortName) 
    {
        for (int i = 0; i < DatabaseConfig.ALL_PROPERTIES.length; i++) {
            String fullProperty = DatabaseConfig.ALL_PROPERTIES[i].getProperty();
            if(fullProperty.endsWith(propShortName))
            {
                return DatabaseConfig.ALL_PROPERTIES[i];
            }
        }
        // Property not found
        logger.info("The property ending with '" + propShortName + "' was not found. " +
                "Please notify a dbunit developer to add the property to the " + DatabaseConfig.class);
        return null;
    }

    public String toString()
    {
    	StringBuffer sb = new StringBuffer();
    	sb.append(getClass().getName()).append("[");
    	sb.append(", _propertyMap=").append(_propertyMap);
    	sb.append("]");
    	return sb.toString();
    }
    

    
    
    /**
     * @author gommma (gommma AT users.sourceforge.net)
     * @author Last changed by: $Author: gommma $
     * @version $Revision: 1193 $ $Date: 2010-06-29 16:01:46 -0500 (Tue, 29 Jun 2010) $
     * @since 2.4.0
     */
    public static class ConfigProperty
    {
        private String property;
        private Class propertyType;
        private boolean nullable;
        
        public ConfigProperty(String property, Class propertyType, boolean nullable) {
            super();
            
            if (property == null) {
                throw new NullPointerException(
                        "The parameter 'property' must not be null");
            }
            if (propertyType == null) {
                throw new NullPointerException(
                        "The parameter 'propertyType' must not be null");
            }
            
            this.property = property;
            this.propertyType = propertyType;
            this.nullable = nullable;
        }
        
        public String getProperty() {
            return property;
        }

        public Class getPropertyType() {
            return propertyType;
        }

        public boolean isNullable() {
            return nullable;
        }

        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result
                    + ((property == null) ? 0 : property.hashCode());
            return result;
        }

        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ConfigProperty other = (ConfigProperty) obj;
            if (property == null) {
                if (other.property != null)
                    return false;
            } else if (!property.equals(other.property))
                return false;
            return true;
        }

        public String toString()
        {
            StringBuffer sb = new StringBuffer();
            sb.append(getClass().getName()).append("[");
            sb.append("property=").append(property);
            sb.append(", propertyType=").append(propertyType);
            sb.append(", nullable=").append(nullable);
            sb.append("]");
            return sb.toString();
        }
    }
    
    
    
    /**
     * Sets parameters stored in the {@link DatabaseConfig} on specific java objects like {@link Statement}.
     * Is mainly there to avoid code duplication where {@link DatabaseConfig} parameters are used.
     * @author gommma (gommma AT users.sourceforge.net)
     * @author Last changed by: $Author: gommma $
     * @version $Revision: 1193 $ $Date: 2010-06-29 16:01:46 -0500 (Tue, 29 Jun 2010) $
     * @since 2.4.4
     */
    protected static class Configurator
    {
        /**
         * Logger for this class
         */
        private static final Logger logger = LoggerFactory.getLogger(Configurator.class);

        private DatabaseConfig config;
        
        /**
         * @param config The configuration to be used by this configurator
         * @since 2.4.4
         */
        public Configurator(DatabaseConfig config)
        {
            if (config == null) {
                throw new NullPointerException(
                        "The parameter 'config' must not be null");
            }
            this.config = config;
        }
        /**
         * Configures the given statement so that it has the properties that are configured in this {@link DatabaseConfig}.
         * @param stmt The statement to be configured.
         * @throws SQLException
         * @since 2.4.4
         */
        void configureStatement(Statement stmt) throws SQLException 
        {
            logger.trace("configureStatement(stmt={}) - start", stmt);
            Integer fetchSize = (Integer) config.getProperty(DatabaseConfig.PROPERTY_FETCH_SIZE);
            stmt.setFetchSize(fetchSize.intValue());
            logger.debug("Statement fetch size set to {}",fetchSize);
        }
        
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy