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; 021 022import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 023import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; 024import org.springframework.boot.autoconfigure.condition.ConditionMessage; 025import org.springframework.boot.autoconfigure.condition.ConditionOutcome; 026import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 027import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 028import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 029import org.springframework.boot.autoconfigure.condition.SpringBootCondition; 030import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration; 031import org.springframework.boot.context.properties.EnableConfigurationProperties; 032import org.springframework.boot.jdbc.DataSourceBuilder; 033import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; 034import org.springframework.context.annotation.Condition; 035import org.springframework.context.annotation.ConditionContext; 036import org.springframework.context.annotation.Conditional; 037import org.springframework.context.annotation.Configuration; 038import org.springframework.context.annotation.Import; 039import org.springframework.core.type.AnnotatedTypeMetadata; 040import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 041 042/** 043 * {@link EnableAutoConfiguration Auto-configuration} for {@link DataSource}. 044 * 045 * @author Dave Syer 046 * @author Phillip Webb 047 * @author Stephane Nicoll 048 * @author Kazuki Shimizu 049 */ 050@Configuration 051@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) 052@EnableConfigurationProperties(DataSourceProperties.class) 053@Import({ DataSourcePoolMetadataProvidersConfiguration.class, 054 DataSourceInitializationConfiguration.class }) 055public class DataSourceAutoConfiguration { 056 057 @Configuration 058 @Conditional(EmbeddedDatabaseCondition.class) 059 @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) 060 @Import(EmbeddedDataSourceConfiguration.class) 061 protected static class EmbeddedDatabaseConfiguration { 062 063 } 064 065 @Configuration 066 @Conditional(PooledDataSourceCondition.class) 067 @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) 068 @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, 069 DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class, 070 DataSourceJmxConfiguration.class }) 071 protected static class PooledDataSourceConfiguration { 072 073 } 074 075 /** 076 * {@link AnyNestedCondition} that checks that either {@code spring.datasource.type} 077 * is set or {@link PooledDataSourceAvailableCondition} applies. 078 */ 079 static class PooledDataSourceCondition extends AnyNestedCondition { 080 081 PooledDataSourceCondition() { 082 super(ConfigurationPhase.PARSE_CONFIGURATION); 083 } 084 085 @ConditionalOnProperty(prefix = "spring.datasource", name = "type") 086 static class ExplicitType { 087 088 } 089 090 @Conditional(PooledDataSourceAvailableCondition.class) 091 static class PooledDataSourceAvailable { 092 093 } 094 095 } 096 097 /** 098 * {@link Condition} to test if a supported connection pool is available. 099 */ 100 static class PooledDataSourceAvailableCondition extends SpringBootCondition { 101 102 @Override 103 public ConditionOutcome getMatchOutcome(ConditionContext context, 104 AnnotatedTypeMetadata metadata) { 105 ConditionMessage.Builder message = ConditionMessage 106 .forCondition("PooledDataSource"); 107 if (getDataSourceClassLoader(context) != null) { 108 return ConditionOutcome 109 .match(message.foundExactly("supported DataSource")); 110 } 111 return ConditionOutcome 112 .noMatch(message.didNotFind("supported DataSource").atAll()); 113 } 114 115 /** 116 * Returns the class loader for the {@link DataSource} class. Used to ensure that 117 * the driver class can actually be loaded by the data source. 118 * @param context the condition context 119 * @return the class loader 120 */ 121 private ClassLoader getDataSourceClassLoader(ConditionContext context) { 122 Class<?> dataSourceClass = DataSourceBuilder 123 .findType(context.getClassLoader()); 124 return (dataSourceClass != null) ? dataSourceClass.getClassLoader() : null; 125 } 126 127 } 128 129 /** 130 * {@link Condition} to detect when an embedded {@link DataSource} type can be used. 131 * If a pooled {@link DataSource} is available, it will always be preferred to an 132 * {@code EmbeddedDatabase}. 133 */ 134 static class EmbeddedDatabaseCondition extends SpringBootCondition { 135 136 private final SpringBootCondition pooledCondition = new PooledDataSourceCondition(); 137 138 @Override 139 public ConditionOutcome getMatchOutcome(ConditionContext context, 140 AnnotatedTypeMetadata metadata) { 141 ConditionMessage.Builder message = ConditionMessage 142 .forCondition("EmbeddedDataSource"); 143 if (anyMatches(context, metadata, this.pooledCondition)) { 144 return ConditionOutcome 145 .noMatch(message.foundExactly("supported pooled data source")); 146 } 147 EmbeddedDatabaseType type = EmbeddedDatabaseConnection 148 .get(context.getClassLoader()).getType(); 149 if (type == null) { 150 return ConditionOutcome 151 .noMatch(message.didNotFind("embedded database").atAll()); 152 } 153 return ConditionOutcome.match(message.found("embedded database").items(type)); 154 } 155 156 } 157 158}