001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.jdbc; 018 019import javax.sql.DataSource; 020import javax.sql.XADataSource; 021import javax.transaction.TransactionManager; 022 023import org.springframework.beans.BeanUtils; 024import org.springframework.beans.factory.BeanClassLoaderAware; 025import org.springframework.beans.factory.ObjectProvider; 026import org.springframework.boot.autoconfigure.AutoConfigureBefore; 027import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 028import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 029import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 030import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 031import org.springframework.boot.context.properties.EnableConfigurationProperties; 032import org.springframework.boot.context.properties.bind.Bindable; 033import org.springframework.boot.context.properties.bind.Binder; 034import org.springframework.boot.context.properties.source.ConfigurationPropertyName; 035import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases; 036import org.springframework.boot.context.properties.source.ConfigurationPropertySource; 037import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; 038import org.springframework.boot.jdbc.DatabaseDriver; 039import org.springframework.boot.jdbc.XADataSourceWrapper; 040import org.springframework.context.annotation.Bean; 041import org.springframework.context.annotation.Configuration; 042import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 043import org.springframework.util.Assert; 044import org.springframework.util.ClassUtils; 045import org.springframework.util.StringUtils; 046 047/** 048 * {@link EnableAutoConfiguration Auto-configuration} for {@link DataSource} with XA. 049 * 050 * @author Phillip Webb 051 * @author Josh Long 052 * @author Madhura Bhave 053 * @since 1.2.0 054 */ 055@Configuration 056@AutoConfigureBefore(DataSourceAutoConfiguration.class) 057@EnableConfigurationProperties(DataSourceProperties.class) 058@ConditionalOnClass({ DataSource.class, TransactionManager.class, 059 EmbeddedDatabaseType.class }) 060@ConditionalOnBean(XADataSourceWrapper.class) 061@ConditionalOnMissingBean(DataSource.class) 062public class XADataSourceAutoConfiguration implements BeanClassLoaderAware { 063 064 private final XADataSourceWrapper wrapper; 065 066 private final DataSourceProperties properties; 067 068 private final XADataSource xaDataSource; 069 070 private ClassLoader classLoader; 071 072 public XADataSourceAutoConfiguration(XADataSourceWrapper wrapper, 073 DataSourceProperties properties, ObjectProvider<XADataSource> xaDataSource) { 074 this.wrapper = wrapper; 075 this.properties = properties; 076 this.xaDataSource = xaDataSource.getIfAvailable(); 077 } 078 079 @Bean 080 public DataSource dataSource() throws Exception { 081 XADataSource xaDataSource = this.xaDataSource; 082 if (xaDataSource == null) { 083 xaDataSource = createXaDataSource(); 084 } 085 return this.wrapper.wrapDataSource(xaDataSource); 086 } 087 088 @Override 089 public void setBeanClassLoader(ClassLoader classLoader) { 090 this.classLoader = classLoader; 091 } 092 093 private XADataSource createXaDataSource() { 094 String className = this.properties.getXa().getDataSourceClassName(); 095 if (!StringUtils.hasLength(className)) { 096 className = DatabaseDriver.fromJdbcUrl(this.properties.determineUrl()) 097 .getXaDataSourceClassName(); 098 } 099 Assert.state(StringUtils.hasLength(className), 100 "No XA DataSource class name specified"); 101 XADataSource dataSource = createXaDataSourceInstance(className); 102 bindXaProperties(dataSource, this.properties); 103 return dataSource; 104 } 105 106 private XADataSource createXaDataSourceInstance(String className) { 107 try { 108 Class<?> dataSourceClass = ClassUtils.forName(className, this.classLoader); 109 Object instance = BeanUtils.instantiateClass(dataSourceClass); 110 Assert.isInstanceOf(XADataSource.class, instance); 111 return (XADataSource) instance; 112 } 113 catch (Exception ex) { 114 throw new IllegalStateException( 115 "Unable to create XADataSource instance from '" + className + "'"); 116 } 117 } 118 119 private void bindXaProperties(XADataSource target, 120 DataSourceProperties dataSourceProperties) { 121 Binder binder = new Binder(getBinderSource(dataSourceProperties)); 122 binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(target)); 123 } 124 125 private ConfigurationPropertySource getBinderSource( 126 DataSourceProperties dataSourceProperties) { 127 MapConfigurationPropertySource source = new MapConfigurationPropertySource(); 128 source.put("user", this.properties.determineUsername()); 129 source.put("password", this.properties.determinePassword()); 130 source.put("url", this.properties.determineUrl()); 131 source.putAll(dataSourceProperties.getXa().getProperties()); 132 ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases(); 133 aliases.addAliases("user", "username"); 134 return source.withAliases(aliases); 135 } 136 137}