com.jhonyu.framework.frame.datasource.RoutingDataSource.java Source code

Java tutorial

Introduction

Here is the source code for com.jhonyu.framework.frame.datasource.RoutingDataSource.java

Source

package com.jhonyu.framework.frame.datasource;

/*
 * Copyright 2002-2012 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.
 */

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.util.Assert;

/**
 * Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()} calls
 * to one of various target DataSources based on a lookup key. The latter is usually (but not
 * necessarily) determined through some thread-bound transaction context.
 *
 * @author Jitaiping
 * @since 2.0.1
 * @see #setTargetDataSources
 * @see #setDefaultTargetDataSource
 * @see #determineCurrentLookupKey()
 */
public abstract class RoutingDataSource extends AbstractDataSource implements InitializingBean {

    private Map<Object, Object> targetDataSources;

    private Object defaultTargetDataSource;

    private boolean lenientFallback = true;

    private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();

    private Map<Object, DataSource> resolvedDataSources;

    private DataSource resolvedDefaultDataSource;

    /**
     * ??????key???
     * 
     * @author jitaiping
     * @since 2015-7-27
     * @param key
     *            ???
     * @param ds
     *            ???
     */
    /*
    public void addDataSource(String key, DataSource ds)
    {
    if (null == key || "".equals(key))
    {
        return;
    }
        
    if (null != ds)
    {
        targetDataSources.put(key, ds);
        resolvedDataSources.put(key, ds);
    }
    }
    */
    /**
     * add by jitaiping 2015-7-27
     */
    protected Map<Object, Object> getTargetDataSources() {
        return targetDataSources;
    }

    /**
     * add by jitaiping 2015-7-27
     */
    protected Map<Object, DataSource> getResolvedDataSources() {
        return resolvedDataSources;
    }

    /**
     * add by jitaiping 2015-7-27
     * 
     * @param key
     *            DataSource key
     * @param ds
     *            DataSource
     */
    protected abstract void addDataSource(String key, DataSource ds);

    /**
     * Specify the map of target DataSources, with the lookup key as key. The mapped value can
     * either be a corresponding {@link javax.sql.DataSource} instance or a data source name String
     * (to be resolved via a {@link #setDataSourceLookup DataSourceLookup}). <p>The key can be of
     * arbitrary type; this class implements the generic lookup process only. The concrete key
     * representation will be handled by {@link #resolveSpecifiedLookupKey(Object)} and
     * {@link #determineCurrentLookupKey()}.
     */
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        this.targetDataSources = targetDataSources;
    }

    /**
     * Specify the default target DataSource, if any. <p>The mapped value can either be a
     * corresponding {@link javax.sql.DataSource} instance or a data source name String (to be
     * resolved via a {@link #setDataSourceLookup DataSourceLookup}). <p>This DataSource will be
     * used as target if none of the keyed {@link #setTargetDataSources targetDataSources} match
     * the {@link #determineCurrentLookupKey()} current lookup key.
     */
    public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
        this.defaultTargetDataSource = defaultTargetDataSource;
    }

    /**
     * Specify whether to apply a lenient fallback to the default DataSource if no specific
     * DataSource could be found for the current lookup key. <p>Default is "true", accepting lookup
     * keys without a corresponding entry in the target DataSource map - simply falling back to the
     * default DataSource in that case. <p>Switch this flag to "false" if you would prefer the
     * fallback to only apply if the lookup key was {@code null}. Lookup keys without a DataSource
     * entry will then lead to an IllegalStateException.
     * 
     * @see #setTargetDataSources
     * @see #setDefaultTargetDataSource
     * @see #determineCurrentLookupKey()
     */
    public void setLenientFallback(boolean lenientFallback) {
        this.lenientFallback = lenientFallback;
    }

    /**
     * Set the DataSourceLookup implementation to use for resolving data source name Strings in the
     * {@link #setTargetDataSources targetDataSources} map. <p>Default is a
     * {@link JndiDataSourceLookup}, allowing the JNDI names of application server DataSources to
     * be specified directly.
     */
    public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
        this.dataSourceLookup = (dataSourceLookup != null ? dataSourceLookup : new JndiDataSourceLookup());
    }

    public void afterPropertiesSet() {
        if (this.targetDataSources == null) {
            throw new IllegalArgumentException("Property 'targetDataSources' is required");
        }
        this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
        for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
            Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
            DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
            this.resolvedDataSources.put(lookupKey, dataSource);
        }
        if (this.defaultTargetDataSource != null) {
            this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
        }
    }

    /**
     * Resolve the given lookup key object, as specified in the {@link #setTargetDataSources
     * targetDataSources} map, into the actual lookup key to be used for matching with the
     * {@link #determineCurrentLookupKey() current lookup key}. <p>The default implementation
     * simply returns the given key as-is.
     * 
     * @param lookupKey
     *            the lookup key object as specified by the user
     * @return the lookup key as needed for matching
     */
    protected Object resolveSpecifiedLookupKey(Object lookupKey) {
        return lookupKey;
    }

    /**
     * Resolve the specified data source object into a DataSource instance. <p>The default
     * implementation handles DataSource instances and data source names (to be resolved via a
     * {@link #setDataSourceLookup DataSourceLookup}).
     * 
     * @param dataSource
     *            the data source value object as specified in the {@link #setTargetDataSources
     *            targetDataSources} map
     * @return the resolved DataSource (never {@code null})
     * @throws IllegalArgumentException
     *             in case of an unsupported value type
     */
    protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
        if (dataSource instanceof DataSource) {
            return (DataSource) dataSource;
        } else if (dataSource instanceof String) {
            return this.dataSourceLookup.getDataSource((String) dataSource);
        } else {
            throw new IllegalArgumentException(
                    "Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
        }
    }

    public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
    }

    public Connection getConnection(String username, String password) throws SQLException {
        return determineTargetDataSource().getConnection(username, password);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T unwrap(Class<T> iface) throws SQLException {
        if (iface.isInstance(this)) {
            return (T) this;
        }
        return determineTargetDataSource().unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
    }

    /**
     * Retrieve the current target DataSource. Determines the {@link #determineCurrentLookupKey()
     * current lookup key}, performs a lookup in the {@link #setTargetDataSources
     * targetDataSources} map, falls back to the specified {@link #setDefaultTargetDataSource
     * default target DataSource} if necessary.
     * 
     * @see #determineCurrentLookupKey()
     */
    protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException(
                    "Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        }
        return dataSource;
    }

    /**
     * Determine the current lookup key. This will typically be implemented to check a thread-bound
     * transaction context. <p>Allows for arbitrary keys. The returned key needs to match the
     * stored lookup key type, as resolved by the {@link #resolveSpecifiedLookupKey} method.
     */
    protected abstract Object determineCurrentLookupKey();

}