001/* 002 * Copyright 2012-2016 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 java.util.HashMap; 020import java.util.Map; 021 022import javax.sql.DataSource; 023 024import org.springframework.beans.BeanUtils; 025import org.springframework.beans.MutablePropertyValues; 026import org.springframework.boot.bind.RelaxedDataBinder; 027import org.springframework.boot.jdbc.DatabaseDriver; 028import org.springframework.util.ClassUtils; 029 030/** 031 * Convenience class for building a {@link DataSource} with common implementations and 032 * properties. If Tomcat, HikariCP or Commons DBCP are on the classpath one of them will 033 * be selected (in that order with Tomcat first). In the interest of a uniform interface, 034 * and so that there can be a fallback to an embedded database if one can be detected on 035 * the classpath, only a small set of common configuration properties are supported. To 036 * inject additional properties into the result you can downcast it, or use 037 * {@code @ConfigurationProperties}. 038 * 039 * @author Dave Syer 040 * @since 1.1.0 041 */ 042public class DataSourceBuilder { 043 044 private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] { 045 "org.apache.tomcat.jdbc.pool.DataSource", 046 "com.zaxxer.hikari.HikariDataSource", 047 "org.apache.commons.dbcp.BasicDataSource", // deprecated 048 "org.apache.commons.dbcp2.BasicDataSource" }; 049 050 private Class<? extends DataSource> type; 051 052 private ClassLoader classLoader; 053 054 private Map<String, String> properties = new HashMap<String, String>(); 055 056 public static DataSourceBuilder create() { 057 return new DataSourceBuilder(null); 058 } 059 060 public static DataSourceBuilder create(ClassLoader classLoader) { 061 return new DataSourceBuilder(classLoader); 062 } 063 064 public DataSourceBuilder(ClassLoader classLoader) { 065 this.classLoader = classLoader; 066 } 067 068 public DataSource build() { 069 Class<? extends DataSource> type = getType(); 070 DataSource result = BeanUtils.instantiate(type); 071 maybeGetDriverClassName(); 072 bind(result); 073 return result; 074 } 075 076 private void maybeGetDriverClassName() { 077 if (!this.properties.containsKey("driverClassName") 078 && this.properties.containsKey("url")) { 079 String url = this.properties.get("url"); 080 String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName(); 081 this.properties.put("driverClassName", driverClass); 082 } 083 } 084 085 private void bind(DataSource result) { 086 MutablePropertyValues properties = new MutablePropertyValues(this.properties); 087 new RelaxedDataBinder(result).withAlias("url", "jdbcUrl") 088 .withAlias("username", "user").bind(properties); 089 } 090 091 public DataSourceBuilder type(Class<? extends DataSource> type) { 092 this.type = type; 093 return this; 094 } 095 096 public DataSourceBuilder url(String url) { 097 this.properties.put("url", url); 098 return this; 099 } 100 101 public DataSourceBuilder driverClassName(String driverClassName) { 102 this.properties.put("driverClassName", driverClassName); 103 return this; 104 } 105 106 public DataSourceBuilder username(String username) { 107 this.properties.put("username", username); 108 return this; 109 } 110 111 public DataSourceBuilder password(String password) { 112 this.properties.put("password", password); 113 return this; 114 } 115 116 @SuppressWarnings("unchecked") 117 public Class<? extends DataSource> findType() { 118 if (this.type != null) { 119 return this.type; 120 } 121 for (String name : DATA_SOURCE_TYPE_NAMES) { 122 try { 123 return (Class<? extends DataSource>) ClassUtils.forName(name, 124 this.classLoader); 125 } 126 catch (Exception ex) { 127 // Swallow and continue 128 } 129 } 130 return null; 131 } 132 133 private Class<? extends DataSource> getType() { 134 Class<? extends DataSource> type = findType(); 135 if (type != null) { 136 return type; 137 } 138 throw new IllegalStateException("No supported DataSource type found"); 139 } 140 141}