org.nuxeo.runtime.datasource.BasicManagedDataSourceFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.runtime.datasource.BasicManagedDataSourceFactory.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 *
 * This code is derived from BasicDataSourceFactory (DBCP 3.0 RC1)
 *
 * Contributors:
 *     Florent Guillaume, Nuxeo
 */

package org.nuxeo.runtime.datasource;

import java.io.ByteArrayInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import javax.sql.DataSource;
import javax.sql.XADataSource;

import org.apache.commons.dbcp.managed.BasicManagedDataSource;

/**
 * JNDI object factory for DBCP that creates an instance of
 * <code>BasicManagedDataSource</code> that has been configured based on the
 * <code>RefAddr</code> values of the specified <code>Reference</code>, which
 * must match the names and data types of the
 * <code>BasicManagedDataSource</code> bean properties.
 *
 * @author Craig R. McClanahan
 * @author Dirk Verbeeck
 * @author Florent Guillaume
 */
public class BasicManagedDataSourceFactory implements ObjectFactory {

    private static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";

    private static final String PROP_DEFAULTREADONLY = "defaultReadOnly";

    private static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";

    private static final String PROP_DEFAULTCATALOG = "defaultCatalog";

    private static final String PROP_DRIVERCLASSNAME = "driverClassName";

    private static final String PROP_MAXACTIVE = "maxActive";

    private static final String PROP_MAXIDLE = "maxIdle";

    private static final String PROP_MINIDLE = "minIdle";

    private static final String PROP_INITIALSIZE = "initialSize";

    private static final String PROP_MAXWAIT = "maxWait";

    private static final String PROP_TESTONBORROW = "testOnBorrow";

    private static final String PROP_TESTONRETURN = "testOnReturn";

    private static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";

    private static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";

    private static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";

    private static final String PROP_TESTWHILEIDLE = "testWhileIdle";

    private static final String PROP_PASSWORD = "password";

    private static final String PROP_URL = "url";

    private static final String PROP_USERNAME = "username";

    private static final String PROP_VALIDATIONQUERY = "validationQuery";

    private static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout";

    private static final String PROP_INITCONNECTIONSQLS = "initConnectionSqls";

    private static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";

    private static final String PROP_REMOVEABANDONED = "removeAbandoned";

    private static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";

    private static final String PROP_LOGABANDONED = "logAbandoned";

    private static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";

    private static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";

    protected static final String PROP_CONNECTIONPROPERTIES = "connectionProperties";

    // Managed:

    public static final String PROP_XADATASOURCE = "xaDataSource";

    private static final String[] ALL_PROPERTIES = { //
            PROP_DEFAULTAUTOCOMMIT, //
            PROP_DEFAULTREADONLY, //
            PROP_DEFAULTTRANSACTIONISOLATION, //
            PROP_DEFAULTCATALOG, //
            PROP_DRIVERCLASSNAME, //
            PROP_MAXACTIVE, //
            PROP_MAXIDLE, //
            PROP_MINIDLE, //
            PROP_INITIALSIZE, //
            PROP_MAXWAIT, //
            PROP_TESTONBORROW, //
            PROP_TESTONRETURN, //
            PROP_TIMEBETWEENEVICTIONRUNSMILLIS, //
            PROP_NUMTESTSPEREVICTIONRUN, //
            PROP_MINEVICTABLEIDLETIMEMILLIS, //
            PROP_TESTWHILEIDLE, //
            PROP_PASSWORD, //
            PROP_URL, //
            PROP_USERNAME, //
            PROP_VALIDATIONQUERY, //
            PROP_VALIDATIONQUERY_TIMEOUT, //
            PROP_INITCONNECTIONSQLS, //
            PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, //
            PROP_REMOVEABANDONED, //
            PROP_REMOVEABANDONEDTIMEOUT, //
            PROP_LOGABANDONED, //
            PROP_POOLPREPAREDSTATEMENTS, //
            PROP_MAXOPENPREPAREDSTATEMENTS, //
            PROP_CONNECTIONPROPERTIES, //
            PROP_XADATASOURCE //
    };

    // copied from PoolableConnectionFactory
    private static final int UNKNOWN_TRANSACTIONISOLATION = -1;

    // -------------------------------------------------- ObjectFactory Methods

    /**
     * Create and return a new <code>BasicManagedDataSource</code> instance. If
     * no instance can be created, return <code>null</code> instead.
     *
     * @param obj The possibly null object containing location or reference
     *            information that can be used in creating an object
     * @param name The name of this object relative to <code>nameCtx</code>
     * @param nameCtx The context relative to which the <code>name</code>
     *            parameter is specified, or <code>null</code> if
     *            <code>name</code> is relative to the default initial context
     * @param environment The possibly null environment that is used in creating
     *            this object
     *
     * @exception Exception if an exception occurs creating the instance
     */
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
            throws Exception {

        // We only know how to deal with <code>javax.naming.Reference</code>s
        // that specify a class name of "javax.sql.DataSource"
        if ((obj == null) || !(obj instanceof Reference)) {
            return null;
        }
        Reference ref = (Reference) obj;
        if (!"javax.sql.DataSource".equals(ref.getClassName())) {
            return null;
        }

        Properties properties = new Properties();
        for (String propertyName : ALL_PROPERTIES) {
            RefAddr ra = ref.get(propertyName);
            if (ra != null) {
                String propertyValue = ra.getContent().toString();
                properties.setProperty(propertyName, propertyValue);
            }
        }

        return createDataSource(properties);
    }

    /**
     * Creates and configures a {@link BasicManagedDataSource} instance based on
     * the given properties.
     *
     * @param properties the datasource configuration properties
     * @throws Exception if an error occurs creating the data source
     */
    public static DataSource createDataSource(Properties properties) throws Exception {
        BasicManagedDataSource dataSource = new BasicManagedDataSource();

        String value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT);
        if (value != null) {
            dataSource.setDefaultAutoCommit(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_DEFAULTREADONLY);
        if (value != null) {
            dataSource.setDefaultReadOnly(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION);
        if (value != null) {
            int level = UNKNOWN_TRANSACTIONISOLATION;
            if ("NONE".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_NONE;
            } else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_READ_COMMITTED;
            } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_READ_UNCOMMITTED;
            } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_REPEATABLE_READ;
            } else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
                level = Connection.TRANSACTION_SERIALIZABLE;
            } else {
                try {
                    level = Integer.parseInt(value);
                } catch (NumberFormatException e) {
                    System.err.println("Could not parse defaultTransactionIsolation: " + value);
                    System.err.println("WARNING: defaultTransactionIsolation not set");
                    System.err.println("using default value of database driver");
                    level = UNKNOWN_TRANSACTIONISOLATION;
                }
            }
            dataSource.setDefaultTransactionIsolation(level);
        }

        value = properties.getProperty(PROP_DEFAULTCATALOG);
        if (value != null) {
            dataSource.setDefaultCatalog(value);
        }

        value = properties.getProperty(PROP_DRIVERCLASSNAME);
        if (value != null) {
            dataSource.setDriverClassName(value);
        }

        value = properties.getProperty(PROP_MAXACTIVE);
        if (value != null) {
            dataSource.setMaxActive(Integer.parseInt(value));
        }

        value = properties.getProperty(PROP_MAXIDLE);
        if (value != null) {
            dataSource.setMaxIdle(Integer.parseInt(value));
        }

        value = properties.getProperty(PROP_MINIDLE);
        if (value != null) {
            dataSource.setMinIdle(Integer.parseInt(value));
        }

        value = properties.getProperty(PROP_INITIALSIZE);
        if (value != null) {
            dataSource.setInitialSize(Integer.parseInt(value));
        }

        value = properties.getProperty(PROP_MAXWAIT);
        if (value != null) {
            dataSource.setMaxWait(Long.parseLong(value));
        }

        value = properties.getProperty(PROP_TESTONBORROW);
        if (value != null) {
            dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_TESTONRETURN);
        if (value != null) {
            dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS);
        if (value != null) {
            dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
        }

        value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN);
        if (value != null) {
            dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
        }

        value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS);
        if (value != null) {
            dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
        }

        value = properties.getProperty(PROP_TESTWHILEIDLE);
        if (value != null) {
            dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_PASSWORD);
        if (value != null) {
            dataSource.setPassword(value);
        }

        value = properties.getProperty(PROP_URL);
        if (value != null) {
            dataSource.setUrl(value);
        }

        value = properties.getProperty(PROP_USERNAME);
        if (value != null) {
            dataSource.setUsername(value);
        }

        value = properties.getProperty(PROP_VALIDATIONQUERY);
        if (value != null) {
            dataSource.setValidationQuery(value);
        }

        value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT);
        if (value != null) {
            dataSource.setValidationQueryTimeout(Integer.parseInt(value));
        }

        value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
        if (value != null) {
            dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_REMOVEABANDONED);
        if (value != null) {
            dataSource.setRemoveAbandoned(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT);
        if (value != null) {
            dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
        }

        value = properties.getProperty(PROP_LOGABANDONED);
        if (value != null) {
            dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS);
        if (value != null) {
            dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
        }

        value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS);
        if (value != null) {
            dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
        }

        value = properties.getProperty(PROP_INITCONNECTIONSQLS);
        if (value != null) {
            StringTokenizer tokenizer = new StringTokenizer(value, ";");
            dataSource.setConnectionInitSqls(Collections.list(tokenizer));
        }

        value = properties.getProperty(PROP_CONNECTIONPROPERTIES);
        if (value != null) {
            Properties p = getProperties(value);
            Enumeration<?> e = p.propertyNames();
            while (e.hasMoreElements()) {
                String propertyName = (String) e.nextElement();
                dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName));
            }
        }

        // Managed: initialize XADataSource

        value = properties.getProperty(PROP_XADATASOURCE);
        if (value != null) {
            Class<?> xaDataSourceClass;
            try {
                xaDataSourceClass = Class.forName(value);
            } catch (Throwable t) {
                throw (SQLException) new SQLException("Cannot load XA data source class '" + value + "'")
                        .initCause(t);
            }
            XADataSource xaDataSource;
            try {
                xaDataSource = (XADataSource) xaDataSourceClass.newInstance();
            } catch (Throwable t) {
                throw (SQLException) new SQLException("Cannot create XA data source of class '" + value + "'")
                        .initCause(t);
            }
            dataSource.setXaDataSourceInstance(xaDataSource);
        }

        // DBCP-215
        // Trick to make sure that initialSize connections are created
        if (dataSource.getInitialSize() > 0) {
            dataSource.getLogWriter();
        }

        // Return the configured DataSource instance
        return dataSource;
    }

    /**
     * Parse properties from the string. Format of the string must be
     * [propertyName=property;]*
     *
     * @param propText
     * @return Properties
     * @throws Exception
     */
    private static Properties getProperties(String propText) throws Exception {
        Properties p = new Properties();
        if (propText != null) {
            p.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes()));
        }
        return p;
    }
}