/* * Copyright 2005 Werner Guttmann, Ralf Joachim * * 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.castor.cpa.persistence.sql.connection; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.castor.core.util.Messages; import org.castor.jdo.conf.DataSource; import org.castor.jdo.conf.Param; import org.exolab.castor.mapping.MappingException; /** * @author <a href="mailto:werner DOT guttmann AT gmx DOT net">Werner Guttmann</a> * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a> * @version $Revision$ $Date: 2006-04-12 15:13:08 -0600 (Wed, 12 Apr 2006) $ * @since 0.9.9 */ public final class DataSourceConnectionFactory implements ConnectionFactory { //-------------------------------------------------------------------------- /** The <a href="http://jakarta.apache.org/commons/logging/">Jakarta * Commons Logging</a> instance used for all logging. */ private static final Log LOG = LogFactory.getLog(DataSourceConnectionFactory.class); //-------------------------------------------------------------------------- /** * Initialize JDBC DataSource instance with the given database configuration * instances and the given class loader. * * @param confDataSource DataSource configuration. * @param loader ClassLoader to use. * @return The initialized DataSource. * @throws MappingException Problem related to analysing the JDO configuration. */ public static javax.sql.DataSource loadDataSource( final DataSource confDataSource, final ClassLoader loader) throws MappingException { String className = confDataSource.getClassName(); ClassLoader classLoader = loader; if (classLoader == null) { classLoader = Thread.currentThread().getContextClassLoader(); } javax.sql.DataSource sqlDataSource; try { Class<?> dsClass = Class.forName(className, true, classLoader); sqlDataSource = (javax.sql.DataSource) dsClass.newInstance(); } catch (Exception e) { String msg = Messages.format("jdo.engine.classNotInstantiable", className); LOG.error(msg, e); throw new MappingException(msg, e); } Param[] parameters = confDataSource.getParam(); setParameters(sqlDataSource, parameters); return sqlDataSource; } /** * Set all the parameters of the given array at the given datasource by calling * one of the set methods of the datasource. * * @param dataSource The datasource to set the parameters on. * @param params The parameters to set on the datasource. * @throws MappingException If one of the parameters could not be set. */ public static void setParameters(final javax.sql.DataSource dataSource, final Param[] params) throws MappingException { Method[] methods = dataSource.getClass().getMethods(); for (int j = 0; j < params.length; j++) { String name = buildMethodName(params[j].getName()); String value = params[j].getValue(); boolean success = false; Exception cause = null; try { int i = 0; while (!success && (i < methods.length)) { Method method = methods[i]; Class<?>[] types = method.getParameterTypes(); if ((method.getName().equals(name)) && (types.length == 1)) { if (types[0] == String.class) { method.invoke(dataSource, new Object[] {value}); success = true; } else if (types[0] == int.class) { method.invoke(dataSource, new Object[] {new Integer(value)}); success = true; } else if (types[0] == long.class) { method.invoke(dataSource, new Object[] {new Long(value)}); success = true; } else if (types[0] == boolean.class) { method.invoke(dataSource, new Object[] {new Boolean(value)}); success = true; } } i++; } } catch (Exception e) { cause = e; } if (!success || (cause != null)) { String msg = Messages.format("jdo.engine.datasourceParaFail", params[j].getName(), value); LOG.error(msg, cause); throw new MappingException(msg, cause); } } } /** * Build the name of the method to set the parameter value of the given name. The * name of the method is build by preceding given parameter name with 'set' followed * by all letters of the name. In addition the first letter and all letters * following a '-' sign are converted to upper case. * * @param name The name of the parameter. * @return The name of the method to set the value of this parameter. */ public static String buildMethodName(final String name) { StringBuffer sb = new StringBuffer("set"); boolean first = true; for (int i = 0; i < name.length(); i++) { char chr = name.charAt(i); if (first && Character.isLowerCase(chr)) { sb.append(Character.toUpperCase(chr)); first = false; } else if (Character.isLetter(chr)) { sb.append(chr); first = false; } else if (chr == '-') { first = true; } } return sb.toString(); } //-------------------------------------------------------------------------- /** DataSource configuration. */ private final DataSource _confDataSource; /** Wrap JDBC connections by proxies? */ private final boolean _useProxies; /** ClassLoader to use. */ private final ClassLoader _loader; /** The data source when using a JDBC dataSource. */ private javax.sql.DataSource _sqlDataSource = null; //-------------------------------------------------------------------------- /** * Constructs a new DataSourceConnectionFactory with given name, engine, mapping * and datasource. Factory will be ready to use without calling initialize first. * * @param datasource The preconfigured datasource to use for creating connections. * @param useProxies Wrap JDBC connections by proxies? */ public DataSourceConnectionFactory(final javax.sql.DataSource datasource, final boolean useProxies) { _confDataSource = null; _useProxies = useProxies; _loader = null; _sqlDataSource = datasource; } /** * Constructs a new DataSourceConnectionFactory with given database and mapping. * Initialize needs to be called before using the factory to create connections. * * @param dataSource DataSouce configuration. * @param useProxies Wrap JDBC connections by proxies? * @param loader ClassLoader to use. */ public DataSourceConnectionFactory(final DataSource dataSource, final boolean useProxies, final ClassLoader loader) { _confDataSource = dataSource; _useProxies = useProxies; _loader = loader; } /** * {@inheritDoc} */ public void initializeFactory() throws MappingException { if (_sqlDataSource == null) { _sqlDataSource = loadDataSource(_confDataSource, _loader); if (LOG.isDebugEnabled()) { LOG.debug("Using DataSource: " + _confDataSource.getClassName()); } } } //-------------------------------------------------------------------------- /** * {@inheritDoc} */ public Connection createConnection () throws SQLException { Connection connection = _sqlDataSource.getConnection(); if (!_useProxies) { return connection; } return ConnectionProxyFactory.newConnectionProxy(connection, getClass().getName()); } //-------------------------------------------------------------------------- }