Update DataSource auto-configuration to support XA
Update DataSource and JPA auto-configuration to consider XA datasources. See gh-947
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import javax.sql.XADataSource;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
@@ -74,7 +75,7 @@ public class DataSourceAutoConfiguration {
|
||||
}
|
||||
|
||||
@Conditional(DataSourceAutoConfiguration.EmbeddedDataSourceCondition.class)
|
||||
@ConditionalOnMissingBean(DataSource.class)
|
||||
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
|
||||
@Import(EmbeddedDataSourceConfiguration.class)
|
||||
protected static class EmbeddedConfiguration {
|
||||
|
||||
@@ -92,7 +93,7 @@ public class DataSourceAutoConfiguration {
|
||||
}
|
||||
|
||||
@Conditional(DataSourceAutoConfiguration.NonEmbeddedDataSourceCondition.class)
|
||||
@ConditionalOnMissingBean(DataSource.class)
|
||||
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
|
||||
protected static class NonEmbeddedConfiguration {
|
||||
|
||||
@Autowired
|
||||
@@ -196,7 +197,8 @@ public class DataSourceAutoConfiguration {
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
if (hasBean(context, DataSource.class)) {
|
||||
if (hasBean(context, DataSource.class)
|
||||
|| hasBean(context, XADataSource.class)) {
|
||||
return ConditionOutcome
|
||||
.match("existing bean configured database detected");
|
||||
}
|
||||
@@ -210,6 +212,7 @@ public class DataSourceAutoConfiguration {
|
||||
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
context.getBeanFactory(), type, true, false).length > 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,8 +49,6 @@ public class DataSourceBuilder {
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private DriverClassNameProvider driverClassNameProvider = new DriverClassNameProvider();
|
||||
|
||||
private Map<String, String> properties = new HashMap<String, String>();
|
||||
|
||||
public static DataSourceBuilder create() {
|
||||
@@ -76,9 +74,9 @@ public class DataSourceBuilder {
|
||||
private void maybeGetDriverClassName() {
|
||||
if (!this.properties.containsKey("driverClassName")
|
||||
&& this.properties.containsKey("url")) {
|
||||
String cls = this.driverClassNameProvider.getDriverClassName(this.properties
|
||||
.get("url"));
|
||||
this.properties.put("driverClassName", cls);
|
||||
String url = this.properties.get("url");
|
||||
String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
|
||||
this.properties.put("driverClassName", driverClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -64,7 +67,7 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB
|
||||
|
||||
private EmbeddedDatabaseConnection embeddedDatabaseConnection = EmbeddedDatabaseConnection.NONE;
|
||||
|
||||
private DriverClassNameProvider driverClassNameProvider = new DriverClassNameProvider();
|
||||
private Xa xa = new Xa();
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
@@ -86,7 +89,7 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB
|
||||
String driverClassName = null;
|
||||
|
||||
if (StringUtils.hasText(this.url)) {
|
||||
driverClassName = this.driverClassNameProvider.getDriverClassName(this.url);
|
||||
driverClassName = DatabaseDriver.fromJdbcUrl(this.url).getDriverClassName();
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(driverClassName)) {
|
||||
@@ -113,7 +116,7 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB
|
||||
"Cannot determine embedded database url for database type "
|
||||
+ this.embeddedDatabaseConnection
|
||||
+ ". If you want an embedded "
|
||||
+ "database please put a supported on on the classpath.");
|
||||
+ "database please put a supported one on the classpath.");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@@ -228,4 +231,39 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB
|
||||
return this.classLoader;
|
||||
}
|
||||
|
||||
public Xa getXa() {
|
||||
return this.xa;
|
||||
}
|
||||
|
||||
public void setXa(Xa xa) {
|
||||
this.xa = xa;
|
||||
}
|
||||
|
||||
/**
|
||||
* XA Specific datasource settings.
|
||||
*/
|
||||
public static class Xa {
|
||||
|
||||
private String dataSourceClassName;
|
||||
|
||||
private Map<String, String> properties = new LinkedHashMap<String, String>();
|
||||
|
||||
public String getDataSourceClassName() {
|
||||
return this.dataSourceClassName;
|
||||
}
|
||||
|
||||
public void setDataSourceClassName(String dataSourceClassName) {
|
||||
this.dataSourceClassName = dataSourceClassName;
|
||||
}
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
public void setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Enumeration of common database drivers.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Maciej Walkowiak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
enum DatabaseDriver {
|
||||
|
||||
/**
|
||||
* Unknown type.
|
||||
*/
|
||||
UNKNOWN(null),
|
||||
|
||||
/**
|
||||
* Apache Derby.
|
||||
*/
|
||||
DERBY("org.apache.derby.jdbc.EmbeddedDriver"),
|
||||
|
||||
/**
|
||||
* H2.
|
||||
*/
|
||||
H2("org.h2.Driver", "org.h2.jdbcx.JdbcDataSource"),
|
||||
|
||||
/**
|
||||
* HyperSQL DataBase.
|
||||
*/
|
||||
HSQLDB("org.hsqldb.jdbc.JDBCDriver", "org.hsqldb.jdbc.pool.JDBCXADataSource"),
|
||||
|
||||
/**
|
||||
* SQL Lite.
|
||||
*/
|
||||
SQLITE("org.sqlite.JDBC"),
|
||||
|
||||
/**
|
||||
* MySQL.
|
||||
*/
|
||||
MYSQL("com.mysql.jdbc.Driver", "org.mysql.jdbc.MySQLDataSource"),
|
||||
|
||||
/**
|
||||
* Maria DB.
|
||||
*/
|
||||
MARIADB("org.mariadb.jdbc.Driver", "org.mariadb.jdbc.MySQLDataSource"),
|
||||
|
||||
/**
|
||||
* Google App Engine.
|
||||
*/
|
||||
GOOGLE("com.google.appengine.api.rdbms.AppEngineDriver"),
|
||||
|
||||
/**
|
||||
* Oracle
|
||||
*/
|
||||
ORACLE("oracle.jdbc.OracleDriver", "oracle.jdbc.xa.OracleXADataSource"),
|
||||
|
||||
/**
|
||||
* Postres
|
||||
*/
|
||||
POSTGRESQL("org.postgresql.Driver", "org.postgresql.xa.PGXADataSource"),
|
||||
|
||||
/**
|
||||
* JTDS
|
||||
*/
|
||||
JTDS("net.sourceforge.jtds.jdbc.Driver"),
|
||||
|
||||
/**
|
||||
* SQL Server
|
||||
*/
|
||||
SQLSERVER("com.microsoft.sqlserver.jdbc.SQLServerDriver");
|
||||
|
||||
private final String driverClassName;
|
||||
|
||||
private final String xaDataSourceClassName;
|
||||
|
||||
private DatabaseDriver(String driverClassName) {
|
||||
this(driverClassName, null);
|
||||
}
|
||||
|
||||
private DatabaseDriver(String driverClassName, String xaDataSourceClassName) {
|
||||
this.driverClassName = driverClassName;
|
||||
this.xaDataSourceClassName = xaDataSourceClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the driverClassName or {@code null}
|
||||
*/
|
||||
public String getDriverClassName() {
|
||||
return this.driverClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the xaDataSourceClassName or {@code null}
|
||||
*/
|
||||
public String getXaDataSourceClassName() {
|
||||
return this.xaDataSourceClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a {@link DatabaseDriver} for the given URL.
|
||||
* @param url JDBC URL
|
||||
* @return driver class name or {@link #UNKNOWN} if not found
|
||||
*/
|
||||
public static DatabaseDriver fromJdbcUrl(String url) {
|
||||
if (StringUtils.hasLength(url)) {
|
||||
Assert.isTrue(url.startsWith("jdbc"), "URL must start with 'jdbc'");
|
||||
String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase();
|
||||
for (DatabaseDriver driver : values()) {
|
||||
String prefix = ":" + driver.name().toLowerCase() + ":";
|
||||
if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) {
|
||||
return driver;
|
||||
}
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,8 @@ import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
|
||||
@AutoConfigureBefore({ XADataSourceAutoConfiguration.class,
|
||||
DataSourceAutoConfiguration.class })
|
||||
@ConditionalOnClass(DataSource.class)
|
||||
@ConditionalOnProperty(prefix = DataSourceProperties.PREFIX, name = "jndi-name")
|
||||
@EnableConfigurationProperties(DataSourceProperties.class)
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import javax.sql.XADataSource;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.jta.JtaAutoConfiguration;
|
||||
import org.springframework.boot.bind.RelaxedDataBinder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.jta.XADataSourceWrapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link DataSource} with XA.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Josh Long
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
|
||||
@AutoConfigureAfter(JtaAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(DataSourceProperties.class)
|
||||
@ConditionalOnClass({ DataSource.class, TransactionManager.class })
|
||||
@ConditionalOnBean(XADataSourceWrapper.class)
|
||||
@ConditionalOnMissingBean(DataSource.class)
|
||||
public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
|
||||
|
||||
@Autowired
|
||||
private XADataSourceWrapper wrapper;
|
||||
|
||||
@Autowired
|
||||
private DataSourceProperties properties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private XADataSource xaDataSource;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
|
||||
public DataSource dataSource() throws Exception {
|
||||
XADataSource xaDataSource = this.xaDataSource;
|
||||
if (xaDataSource == null) {
|
||||
xaDataSource = createXaDataSource();
|
||||
}
|
||||
return this.wrapper.wrapDataSource(xaDataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
private XADataSource createXaDataSource() {
|
||||
String className = this.properties.getXa().getDataSourceClassName();
|
||||
if (!StringUtils.hasLength(className)) {
|
||||
className = DatabaseDriver.fromJdbcUrl(this.properties.getUrl())
|
||||
.getXaDataSourceClassName();
|
||||
}
|
||||
Assert.state(StringUtils.hasLength(className),
|
||||
"No XA DataSource class name specified");
|
||||
XADataSource dataSource = createXaDataSourceInstance(className);
|
||||
bindXaProperties(dataSource, this.properties);
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
private XADataSource createXaDataSourceInstance(String className) {
|
||||
try {
|
||||
Class<?> dataSourceClass = ClassUtils.forName(className, this.classLoader);
|
||||
Object instance = BeanUtils.instantiate(dataSourceClass);
|
||||
Assert.isInstanceOf(XADataSource.class, instance);
|
||||
return (XADataSource) instance;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to create XADataSource instance from '" + className + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void bindXaProperties(XADataSource target, DataSourceProperties properties) {
|
||||
MutablePropertyValues values = new MutablePropertyValues();
|
||||
values.add("user", this.properties.getUsername());
|
||||
values.add("password", this.properties.getPassword());
|
||||
values.add("url", this.properties.getUrl());
|
||||
values.addPropertyValues(properties.getXa().getProperties());
|
||||
new RelaxedDataBinder(target).withAlias("user", "username").bind(values);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -89,6 +89,8 @@ public class EntityManagerFactoryBuilder {
|
||||
|
||||
private Map<String, Object> properties = new HashMap<String, Object>();
|
||||
|
||||
private boolean jta;
|
||||
|
||||
private Builder(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
@@ -142,6 +144,21 @@ public class EntityManagerFactoryBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure if using a JTA {@link DataSource}, i.e. if
|
||||
* {@link LocalContainerEntityManagerFactoryBean#setDataSource(DataSource)
|
||||
* setDataSource} or
|
||||
* {@link LocalContainerEntityManagerFactoryBean#setJtaDataSource(DataSource)
|
||||
* setJtaDataSource} should be called on the
|
||||
* {@link LocalContainerEntityManagerFactoryBean}.
|
||||
* @param jta if the data source is JTA
|
||||
* @return the builder for fluent usage
|
||||
*/
|
||||
public Builder jta(boolean jta) {
|
||||
this.jta = jta;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LocalContainerEntityManagerFactoryBean build() {
|
||||
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
|
||||
if (EntityManagerFactoryBuilder.this.persistenceUnitManager != null) {
|
||||
@@ -153,7 +170,14 @@ public class EntityManagerFactoryBuilder {
|
||||
}
|
||||
entityManagerFactoryBean
|
||||
.setJpaVendorAdapter(EntityManagerFactoryBuilder.this.jpaVendorAdapter);
|
||||
entityManagerFactoryBean.setDataSource(this.dataSource);
|
||||
|
||||
if (this.jta) {
|
||||
entityManagerFactoryBean.setJtaDataSource(this.dataSource);
|
||||
}
|
||||
else {
|
||||
entityManagerFactoryBean.setDataSource(this.dataSource);
|
||||
}
|
||||
|
||||
entityManagerFactoryBean.setPackagesToScan(this.packagesToScan);
|
||||
entityManagerFactoryBean.getJpaPropertyMap().putAll(
|
||||
EntityManagerFactoryBuilder.this.properties.getProperties());
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Map;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -29,8 +30,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jta.JtaAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.boot.orm.jpa.hibernate.SpringJtaPlatform;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -39,29 +41,30 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Hibernate JPA.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Josh Long
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class,
|
||||
EnableTransactionManagement.class, EntityManager.class })
|
||||
@Conditional(HibernateEntityManagerCondition.class)
|
||||
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
|
||||
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, JtaAutoConfiguration.class })
|
||||
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
private static final String JTA_PLATFORM = "hibernate.transaction.jta.platform";
|
||||
|
||||
@Autowired
|
||||
private JpaProperties properties;
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
|
||||
return new HibernateJpaVendorAdapter();
|
||||
@@ -74,6 +77,21 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
|
||||
return vendorProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
|
||||
super.customizeVendorProperties(vendorProperties);
|
||||
if (!vendorProperties.containsKey(JTA_PLATFORM)) {
|
||||
JtaTransactionManager jtaTransactionManager = getJtaTransactionManager();
|
||||
if (jtaTransactionManager != null) {
|
||||
vendorProperties.put(JTA_PLATFORM, new SpringJtaPlatform(
|
||||
jtaTransactionManager));
|
||||
}
|
||||
else {
|
||||
vendorProperties.put(JTA_PLATFORM, NoJtaPlatform.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class HibernateEntityManagerCondition extends SpringBootCondition {
|
||||
|
||||
private static String[] CLASS_NAMES = {
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
|
||||
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
|
||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
@@ -71,6 +72,9 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
|
||||
@Autowired
|
||||
private JpaProperties jpaProperties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private JtaTransactionManager jtaTransactionManager;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(PlatformTransactionManager.class)
|
||||
public PlatformTransactionManager transactionManager() {
|
||||
@@ -103,14 +107,24 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
|
||||
@ConditionalOnMissingBean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
|
||||
EntityManagerFactoryBuilder factoryBuilder) {
|
||||
Map<String, Object> vendorProperties = getVendorProperties();
|
||||
customizeVendorProperties(vendorProperties);
|
||||
return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan())
|
||||
.properties(getVendorProperties()).build();
|
||||
.properties(vendorProperties).jta(isJta()).build();
|
||||
}
|
||||
|
||||
protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter();
|
||||
|
||||
protected abstract Map<String, Object> getVendorProperties();
|
||||
|
||||
/**
|
||||
* Customize vendor properties before they are used. Allows for post processing (for
|
||||
* example to configure JTA specific settings).
|
||||
* @param vendorProperties the vendor properties to customize
|
||||
*/
|
||||
protected void customizeVendorProperties(Map<String, Object> vendorProperties) {
|
||||
}
|
||||
|
||||
protected EntityManagerFactoryBuilder.EntityManagerFactoryBeanCallback getVendorCallback() {
|
||||
return null;
|
||||
}
|
||||
@@ -127,6 +141,20 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware {
|
||||
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the jtaTransactionManager or {@code null}
|
||||
*/
|
||||
protected JtaTransactionManager getJtaTransactionManager() {
|
||||
return this.jtaTransactionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a JTA {@link PlatformTransactionManager} is being used.
|
||||
*/
|
||||
protected final boolean isJta() {
|
||||
return (this.jtaTransactionManager != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
|
||||
@@ -20,6 +20,7 @@ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
|
||||
|
||||
@@ -20,47 +20,46 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DriverClassNameProvider}.
|
||||
* Tests for {@link DatabaseDriver}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Maciej Walkowiak
|
||||
*/
|
||||
public class DriverClassNameProviderTests {
|
||||
|
||||
private DriverClassNameProvider provider = new DriverClassNameProvider();
|
||||
public class DatabaseDriverTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void classNameForKnownDatabase() {
|
||||
String driverClassName = this.provider
|
||||
.getDriverClassName("jdbc:postgresql://hostname/dbname");
|
||||
String driverClassName = DatabaseDriver.fromJdbcUrl(
|
||||
"jdbc:postgresql://hostname/dbname").getDriverClassName();
|
||||
assertEquals("org.postgresql.Driver", driverClassName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullForUnknownDatabase() {
|
||||
String driverClassName = this.provider
|
||||
.getDriverClassName("jdbc:unknowndb://hostname/dbname");
|
||||
public void nullClassNameForUnknownDatabase() {
|
||||
String driverClassName = DatabaseDriver.fromJdbcUrl(
|
||||
"jdbc:unknowndb://hostname/dbname").getDriverClassName();
|
||||
assertNull(driverClassName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureOnNullJdbcUrl() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("JdbcUrl must not be null");
|
||||
this.provider.getDriverClassName(null);
|
||||
public void unknownOnNullJdbcUrl() {
|
||||
assertThat(DatabaseDriver.fromJdbcUrl(null), equalTo(DatabaseDriver.UNKNOWN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureOnMalformedJdbcUrl() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("JdbcUrl must start with");
|
||||
this.provider.getDriverClassName("malformed:url");
|
||||
this.thrown.expectMessage("URL must start with");
|
||||
DatabaseDriver.fromJdbcUrl("malformed:url");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import javax.sql.XADataSource;
|
||||
|
||||
import org.hsqldb.jdbc.pool.JDBCXADataSource;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.jta.XADataSourceWrapper;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link XADataSourceAutoConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class XADataSourceAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void wrapExistingXaDataSource() throws Exception {
|
||||
ApplicationContext context = createContext(WrapExisting.class);
|
||||
context.getBean(DataSource.class);
|
||||
XADataSource source = context.getBean(XADataSource.class);
|
||||
MockXADataSourceWrapper wrapper = context.getBean(MockXADataSourceWrapper.class);
|
||||
assertThat(wrapper.getXaDataSource(), equalTo(source));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromUrl() throws Exception {
|
||||
ApplicationContext context = createContext(FromProperties.class,
|
||||
"spring.datasource.url:jdbc:hsqldb:mem:test",
|
||||
"spring.datasource.username:un");
|
||||
context.getBean(DataSource.class);
|
||||
MockXADataSourceWrapper wrapper = context.getBean(MockXADataSourceWrapper.class);
|
||||
JDBCXADataSource dataSource = (JDBCXADataSource) wrapper.getXaDataSource();
|
||||
assertNotNull(dataSource);
|
||||
assertThat(dataSource.getUrl(), equalTo("jdbc:hsqldb:mem:test"));
|
||||
assertThat(dataSource.getUser(), equalTo("un"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFromClass() throws Exception {
|
||||
ApplicationContext context = createContext(
|
||||
FromProperties.class,
|
||||
"spring.datasource.xa.data-source-class:org.hsqldb.jdbc.pool.JDBCXADataSource",
|
||||
"spring.datasource.xa.properties.database-name:test");
|
||||
context.getBean(DataSource.class);
|
||||
MockXADataSourceWrapper wrapper = context.getBean(MockXADataSourceWrapper.class);
|
||||
JDBCXADataSource dataSource = (JDBCXADataSource) wrapper.getXaDataSource();
|
||||
assertNotNull(dataSource);
|
||||
assertThat(dataSource.getDatabaseName(), equalTo("test"));
|
||||
|
||||
}
|
||||
|
||||
private ApplicationContext createContext(Class<?> configuration, String... env) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(context, env);
|
||||
context.register(configuration, XADataSourceAutoConfiguration.class);
|
||||
context.refresh();
|
||||
return context;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class WrapExisting {
|
||||
|
||||
@Bean
|
||||
public MockXADataSourceWrapper wrapper() {
|
||||
return new MockXADataSourceWrapper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public XADataSource xaDataSource() {
|
||||
return mock(XADataSource.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class FromProperties {
|
||||
|
||||
@Bean
|
||||
public MockXADataSourceWrapper wrapper() {
|
||||
return new MockXADataSourceWrapper();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MockXADataSourceWrapper implements XADataSourceWrapper {
|
||||
|
||||
private XADataSource dataSource;
|
||||
|
||||
@Override
|
||||
public DataSource wrapDataSource(XADataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
return mock(DataSource.class);
|
||||
}
|
||||
|
||||
public XADataSource getXaDataSource() {
|
||||
return this.dataSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user