001/**
002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 * <p>
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 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
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 */
016package com.mybatisflex.core.datasource;
017
018import com.mybatisflex.core.exception.FlexExceptions;
019import com.mybatisflex.core.util.ConvertUtil;
020import com.mybatisflex.core.util.StringUtil;
021import org.apache.ibatis.reflection.Reflector;
022import org.apache.ibatis.reflection.invoker.Invoker;
023
024import javax.sql.DataSource;
025import java.util.HashMap;
026import java.util.Map;
027
028public class DataSourceBuilder {
029
030    private static Map<String, String> dataSourceAlias = new HashMap<>();
031
032    static {
033        dataSourceAlias.put("druid", "com.alibaba.druid.pool.DruidDataSource");
034        dataSourceAlias.put("hikari", "com.zaxxer.hikari.HikariDataSource");
035        dataSourceAlias.put("hikaricp", "com.zaxxer.hikari.HikariDataSource");
036        dataSourceAlias.put("bee", "cn.beecp.BeeDataSource");
037        dataSourceAlias.put("beecp", "cn.beecp.BeeDataSource");
038        dataSourceAlias.put("dbcp", "org.apache.commons.dbcp2.BasicDataSource");
039        dataSourceAlias.put("dbcp2", "org.apache.commons.dbcp2.BasicDataSource");
040    }
041
042    private Map<String, String> dataSourceProperties;
043
044    public DataSourceBuilder(Map<String, String> dataSourceProperties) {
045        this.dataSourceProperties = dataSourceProperties;
046    }
047
048    public DataSource build() {
049        String dataSourceClassName = null;
050        String type = dataSourceProperties.get("type");
051        if (StringUtil.isNotBlank(type)) {
052            if (dataSourceAlias.containsKey(type)) {
053                dataSourceClassName = dataSourceAlias.get(type);
054            } else {
055                dataSourceClassName = type;
056            }
057        } else {
058            dataSourceClassName = detectDataSourceClass();
059        }
060
061
062        if (StringUtil.isBlank(dataSourceClassName)) {
063            if (StringUtil.isBlank(type)) {
064                throw FlexExceptions.wrap("The dataSource type can not be null or blank.");
065            } else {
066                throw FlexExceptions.wrap("Cannot find the dataSource type: " + type);
067            }
068        }
069
070        try {
071            Class<?> dataSourceClass = Class.forName(dataSourceClassName);
072            Object dataSourceObject = dataSourceClass.newInstance();
073            setDataSourceProperties(dataSourceObject);
074            return (DataSource) dataSourceObject;
075        } catch (Exception e) {
076            throw new RuntimeException("Cannot new instance dataSource by class: " + dataSourceClassName);
077        }
078    }
079
080    private void setDataSourceProperties(Object dataSourceObject) throws Exception {
081        Reflector reflector = new Reflector(dataSourceObject.getClass());
082        for (String attr : dataSourceProperties.keySet()) {
083            String value = dataSourceProperties.get(attr);
084            String camelAttr = attrToCamel(attr);
085            if ("url".equals(camelAttr) || "jdbcUrl".equals(camelAttr)) {
086                if (reflector.hasSetter("url")) {
087                    reflector.getSetInvoker("url").invoke(dataSourceObject, new Object[]{value});
088                } else if (reflector.hasSetter("jdbcUrl")) {
089                    reflector.getSetInvoker("jdbcUrl").invoke(dataSourceObject, new Object[]{value});
090                }
091            } else {
092                if (reflector.hasSetter(camelAttr)) {
093                    Invoker setInvoker = reflector.getSetInvoker(camelAttr);
094                    setInvoker.invoke(dataSourceObject, new Object[]{ConvertUtil.convert(value, setInvoker.getType())});
095                }
096            }
097        }
098    }
099
100
101    public static String attrToCamel(String string) {
102        int strLen = string.length();
103        StringBuilder sb = new StringBuilder(strLen);
104        for (int i = 0; i < strLen; i++) {
105            char c = string.charAt(i);
106            if (c == '-') {
107                if (++i < strLen) {
108                    sb.append(Character.toUpperCase(string.charAt(i)));
109                }
110            } else {
111                sb.append(c);
112            }
113        }
114        return sb.toString();
115    }
116
117
118    private String detectDataSourceClass() {
119        String[] detectClassNames = new String[]{
120                "com.alibaba.druid.pool.DruidDataSource",
121                "com.zaxxer.hikari.HikariDataSource",
122                "cn.beecp.BeeDataSource",
123                "org.apache.commons.dbcp2.BasicDataSource",
124        };
125
126        for (String detectClassName : detectClassNames) {
127            String result = doDetectDataSourceClass(detectClassName);
128            if (result != null) {
129                return result;
130            }
131        }
132
133        return null;
134    }
135
136
137    private String doDetectDataSourceClass(String className) {
138        try {
139            Class.forName(className);
140            return className;
141        } catch (ClassNotFoundException e) {
142            return null;
143        }
144    }
145}