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

org.springframework.jdbc.datasource.lookup.RoutingDataSource Maven / Gradle / Ivy

package org.springframework.jdbc.datasource.lookup;

import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.sql.DataSource;

import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;

/**
 * 
 * PropertiesConfigurationFactory propertiesConfigurationFactory = new PropertiesConfigurationFactory(DataSourceProperties.class);
 * propertiesConfigurationFactory.setPropertySources(new PropertySourcesBinder(new ResourcePropertySource(resource)).getPropertySources());
 * DataSource dataSource = propertiesConfigurationFactory.getObject().initializeDataSourceBuilder().build();
 * 
* * @see net.ttddyy.dsproxy.listener.AbstractQueryLoggingListener * @see net.ttddyy.dsproxy.listener.CommonsQueryLoggingListener * @see net.ttddyy.dsproxy.listener.SLF4JQueryLoggingListener * @see net.ttddyy.dsproxy.listener.SLF4JQueryLoggingListener * @see org.springframework.beans.BeanUtils#instantiate(Class) * @see org.springframework.util.ReflectionUtils#findMethod(Class, String) * @see org.springframework.util.ReflectionUtils#invokeMethod(java.lang.reflect.Method, * Object, Object...) * @see org.springframework.core.Constants#asString(String) */ public final class RoutingDataSource extends org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource { private final MultiValueMap readOnly = CollectionUtils.toMultiValueMap(new HashMap>()); private Class proxy; private final int size; /** * @see java.util.Collections#addAll(Collection, Object...) * @see java.util.Collections#synchronizedList(List) * @see org.springframework.core.io.ContextResource#getPathWithinContext() */ public RoutingDataSource(Map dataSources) { Assert.notEmpty(dataSources, "'dataSources' must have entries"); this.size = dataSources.size(); for (Entry entry : dataSources.entrySet()) { String key = String.valueOf(entry.getKey()); this.readOnly.add(key.toLowerCase().startsWith(org.springframework.transaction.support.DefaultTransactionDefinition.READ_ONLY_MARKER.toLowerCase()), entry.getKey()); } setTargetDataSources(dataSources); setLenientFallback(false); } /** * @see org.springframework.util.CollectionUtils#findCommonElementType(Collection) * @see org.springframework.transaction.annotation.Isolation#DEFAULT */ @Override protected Object determineCurrentLookupKey() { if (this.size == 1) { return null; } boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); List list = ((list = this.readOnly.get(readOnly)) != null) || this.readOnly.containsKey(readOnly) ? list : this.readOnly.get(!readOnly); Assert.notEmpty(list, "'dataSource' must have elements"); Collections.shuffle(list); Object key = list.iterator().next(); if (logger.isTraceEnabled()) { logger.trace("key: " + key); logger.trace("readOnly: " + readOnly); logger.trace("name: " + TransactionSynchronizationManager.getCurrentTransactionName()); Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); if (isolationLevel != null) { for (Isolation isolation : Isolation.values()) { if (isolation.value() == isolationLevel.intValue()) { logger.trace("isolationLevel: " + isolation + "(" + isolationLevel + ")"); } } } } return key; } /** * @see org.springframework.beans.BeanUtils#instantiate(Class) */ @Override protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException { DataSource result = super.resolveSpecifiedDataSource(dataSource); Class proxy = determineProxy(); if (proxy != null) { Constructor constructor = ClassUtils.getConstructorIfAvailable(proxy, DataSource.class); if (constructor == null) { throw new IllegalStateException("No constructor accepting real DataSource in proxy class " + proxy); } try { result = BeanUtils.instantiateClass(constructor, result); } catch (RuntimeException e) { if (proxy == this.proxy) { throw e; } if (logger.isWarnEnabled()) { logger.warn("Can't wrap data source: " + result + " in proxy data source of type: " + proxy + " due to thrown exception: " + e + ". " + "Please consider fix exception in data source implementation" + " or explicitly set 'datasource.proxy' with" + " appropriate list of proxy data source providers."); } } } return result; } public Class determineProxy() { Class proxy = this.proxy; if (proxy == null) { proxy = forName("com.p6spy.engine.spy.P6DataSource"); } if (proxy == null) { proxy = forName("net.ttddyy.dsproxy.support.ProxyDataSource"); } if (proxy == null) { proxy = forName("com.vladmihalcea.flexypool.FlexyPoolDataSource"); } return proxy; } @SuppressWarnings("unchecked") public Class forName(String name) { if (ClassUtils.isPresent(name, null)) { try { Class type = ClassUtils.forName(name, null); Assert.isAssignable(DataSource.class, type); return (Class) type; } catch (ClassNotFoundException | LinkageError e) { throw new IllegalArgumentException(e); } } return null; } public Class getProxy() { return proxy; } public void setProxy(Class proxy) { this.proxy = proxy; } }