Package org.apache.myfaces.extensions.cdi.jpa.impl.datasource

Source Code of org.apache.myfaces.extensions.cdi.jpa.impl.datasource.ConfigurableDataSource

/*
* 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.
*/
package org.apache.myfaces.extensions.cdi.jpa.impl.datasource;

import org.apache.myfaces.extensions.cdi.core.api.provider.BeanManagerProvider;
import org.apache.myfaces.extensions.cdi.core.impl.util.JndiUtils;
import org.apache.myfaces.extensions.cdi.jpa.api.datasource.DataSourceConfig;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

/**
* <p>This class can be used instead of a real DataSource.
* It is a simple wrapper to hide any database configuration details
* and make it configurable via CDI.</p>
* <p>See {@link DataSourceConfig} on how to configure it!</p>
*
* <p>The configuration itself will be provided via CDI mechanics.
* To distinguish different databases, users can specify a
* <code>connectionId</code>. If no <code>connectionId</code> is set,
* the String <code>default</code> will be used</p>
*/
public class ConfigurableDataSource implements DataSource
{
    /**
     * config and settings are loaded only once.
     */
    private volatile boolean loaded;

    /**
     * The connectionId allows to configure multiple databases.
     * This can e.g. be used to distinguish between a 'customer' and 'admin'
     * database.
     */
    private String connectionId = "default";

    /**
     * The underlying configuration of the datasource
     */
    private DataSourceConfig dataSourceConfig;

    /**
     * In case of an underlying JDBC connection, we need to provide the connection URL;
     */
    private String jdbcConnectionURL;

    /**
     * In case of an underlying JDBC connection, we need to provide some configured properties
     */
    private Properties connectionProperties;

    /**
     *  The underlying 'real' DataSource if we got a DataSource either via JNDI
     *  or as class name.
     */
    private DataSource wrappedDataSource = null;

    /**
     *  The underlying jdbcDriver if configured.
     */
    private Driver wrappedJdbcDriver = null;


    public ConfigurableDataSource()
    {
        loaded = false;
        dataSourceConfig = BeanManagerProvider.getInstance().getContextualReference(DataSourceConfig.class);
    }

    public void setConnectionId(String connectionId)
    {
        if (loaded)
        {
            throw new IllegalStateException("connectionId must not get changed after the DataSource was established");
        }
        this.connectionId = connectionId;
    }

    public Connection getConnection() throws SQLException
    {
        return getConnection(null, null);
    }

    public Connection getConnection(String userName, String password) throws SQLException
    {
        if (!loaded)
        {
            initDataSource();
        }

        if (wrappedDataSource != null)
        {
            // if we got a DataSource as underlying connector
            if (userName == null && password == null )
            {
                return wrappedDataSource.getConnection();
            }
            return wrappedDataSource.getConnection(userName, password);
        }
        else if (wrappedJdbcDriver != null)
        {
            // if we got a native JDBC Driver class as underlying connector
            return wrappedJdbcDriver.connect(jdbcConnectionURL, connectionProperties);
        }

        return null;
    }


    public PrintWriter getLogWriter() throws SQLException
    {
        return null;
    }

    public void setLogWriter(PrintWriter printWriter) throws SQLException
    {
    }

    public void setLoginTimeout(int loginTimeout) throws SQLException
    {
    }

    public int getLoginTimeout() throws SQLException
    {
        return 0;
    }

    public <T> T unwrap(Class<T> iface) throws SQLException
    {
        if (isWrapperFor(iface))
        {
            return (T) this;
        }
        else
        {
            return null;
        }
    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException
    {
        return iface.isAssignableFrom(ConfigurableDataSource.class);
    }

    /**
     * NEW JDK1.7 signature.
     * This makes sure that CODI can also get compiled using java-7.
     * This method is not actively used though.
     */
    public Logger getParentLogger() throws SQLFeatureNotSupportedException
    {
        throw new SQLFeatureNotSupportedException();
    }

    /**
     *  Initialize the DataSource either from JNDI or via JDBC Driver.
     *  This method does not actually create a connection yet.
     */
    protected synchronized void initDataSource() throws SQLException
    {
        // double check lock idiom on volatile member is ok as of Java5
        if (loaded)
        {
            return;
        }
        loaded = true;

        String jndiLookupName = dataSourceConfig.getJndiResourceName(connectionId);
        if (jndiLookupName != null && jndiLookupName.length() > 0)
        {
            wrappedDataSource = JndiUtils.lookup(jndiLookupName, DataSource.class);
            return;
        }

        // no JNDI, so we take the direct JDBC route.
        String dataSourceClass = dataSourceConfig.getConnectionClassName(connectionId);
        if (dataSourceClass == null && dataSourceClass.length() == 0)
        {
            throw new SQLException("Neither a JNDI location nor a JDBC driver class name is configured!");
        }


        connectionProperties = dataSourceConfig.getConnectionProperties(connectionId);

        try
        {
            // we explicitely use class.forName and NOT the ThreadContextClassLoader!
            Class clazz =  Class.forName(dataSourceClass);

            // the given driver classname must be a DataSource
            if (DataSource.class.isAssignableFrom(clazz))
            {
                wrappedDataSource = (DataSource) clazz.newInstance();

                for (Map.Entry configOption : connectionProperties.entrySet())
                {
                    String name = (String) configOption.getKey();
                    String value = (String) configOption.getValue();
                    setProperty(wrappedDataSource, name, value);
                }
            }
            else if(Driver.class.isAssignableFrom(clazz))
            {
                // if we have a javax.sql.Driver then we also need an explicite connection URL
                jdbcConnectionURL = dataSourceConfig.getJdbcConnectionUrl(connectionId);
                if (jdbcConnectionURL == null)
                {
                    throw new SQLException("Neither a JNDI location nor a JDBC connection URL is configured!");
                }

                wrappedJdbcDriver = (Driver) clazz.newInstance();
            }
            else
            {
                throw new SQLException("Configured DriverClassName is not a javax.sql.DataSource "
                                       + "nor a javax.sql.Driver: "
                                       + dataSourceClass);
            }
        }
        catch (RuntimeException e)
        {
            wrappedDataSource = null;
            throw e;
        }
        catch (SQLException e)
        {
            wrappedDataSource = null;
            throw e;
        }
        catch (Exception e)
        {
            wrappedDataSource = null;
            throw new RuntimeException(e);
        }
    }

    protected void setProperty(Object instance, String key, String value)
            throws InvocationTargetException, IllegalAccessException
    {
        if (key.length()== 0)
        {
            throw new IllegalArgumentException("property name must not be empty!");
        }

        String setterName = "set" + Character.toUpperCase(key.charAt(0)) + key.substring(1);
        Method setter = null;
        try
        {
            setter = instance.getClass().getMethod(setterName, String.class);
        }
        catch (NoSuchMethodException e)
        {
            try
            {
                setter = instance.getClass().getMethod(setterName, Object.class);
            }
            catch (NoSuchMethodException e1)
            {
                //X TODO probably search for fields to set

                //

            }
        }

        if (!setter.isAccessible())
        {
            setter.setAccessible(true);
        }

        setter.invoke(instance, value);
    }

}
TOP

Related Classes of org.apache.myfaces.extensions.cdi.jpa.impl.datasource.ConfigurableDataSource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.