View Javadoc
1   /*
2    * Copyright 2004-2025 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.ibatis.common.jdbc;
17  
18  import com.ibatis.common.beans.Probe;
19  import com.ibatis.common.beans.ProbeFactory;
20  
21  import java.time.Duration;
22  import java.util.Iterator;
23  import java.util.Map;
24  
25  import javax.sql.DataSource;
26  
27  import org.apache.commons.dbcp2.BasicDataSource;
28  
29  /**
30   * Wrapper class to simplify use of DBCP.
31   */
32  public class DbcpConfiguration {
33  
34    /** The Constant PROBE. */
35    private static final Probe PROBE = ProbeFactory.getProbe();
36  
37    /** The Constant ADD_DRIVER_PROPS_PREFIX. */
38    private static final String ADD_DRIVER_PROPS_PREFIX = "Driver.";
39  
40    /** The Constant ADD_DRIVER_PROPS_PREFIX_LENGTH. */
41    private static final int ADD_DRIVER_PROPS_PREFIX_LENGTH = ADD_DRIVER_PROPS_PREFIX.length();
42  
43    /** The data source. */
44    private DataSource dataSource;
45  
46    /**
47     * Constructor to supply a map of properties.
48     *
49     * @param properties
50     *          - the map of configuration properties
51     */
52    public DbcpConfiguration(Map properties) {
53      try {
54  
55        dataSource = legacyDbcpConfiguration(properties);
56        if (dataSource == null) {
57          dataSource = newDbcpConfiguration(properties);
58        }
59  
60      } catch (Exception e) {
61        throw new RuntimeException("Error initializing DbcpDataSourceFactory.  Cause: " + e, e);
62      }
63    }
64  
65    /**
66     * Getter for DataSource.
67     *
68     * @return The DataSource
69     */
70    public DataSource getDataSource() {
71      return dataSource;
72    }
73  
74    /**
75     * New dbcp configuration.
76     *
77     * @param map
78     *          the map
79     *
80     * @return the basic data source
81     */
82    private BasicDataSource newDbcpConfiguration(Map map) {
83      BasicDataSource basicDataSource = new BasicDataSource();
84      Iterator props = map.keySet().iterator();
85      while (props.hasNext()) {
86        String propertyName = (String) props.next();
87        if (propertyName.startsWith(ADD_DRIVER_PROPS_PREFIX)) {
88          String value = (String) map.get(propertyName);
89          basicDataSource.addConnectionProperty(propertyName.substring(ADD_DRIVER_PROPS_PREFIX_LENGTH), value);
90        } else if (PROBE.hasWritableProperty(basicDataSource, propertyName)) {
91          String value = (String) map.get(propertyName);
92          Object convertedValue = convertValue(basicDataSource, propertyName, value);
93          PROBE.setObject(basicDataSource, propertyName, convertedValue);
94        }
95      }
96      return basicDataSource;
97    }
98  
99    /**
100    * Convert value.
101    *
102    * @param object
103    *          the object
104    * @param propertyName
105    *          the property name
106    * @param value
107    *          the value
108    *
109    * @return the object
110    */
111   private Object convertValue(Object object, String propertyName, String value) {
112     Object convertedValue = value;
113     Class targetType = PROBE.getPropertyTypeForSetter(object, propertyName);
114     if (targetType == Integer.class || targetType == int.class) {
115       convertedValue = Integer.valueOf(value);
116     } else if (targetType == Long.class || targetType == long.class) {
117       convertedValue = Long.valueOf(value);
118     } else if (targetType == Boolean.class || targetType == boolean.class) {
119       convertedValue = Boolean.valueOf(value);
120     }
121     return convertedValue;
122   }
123 
124   /**
125    * Legacy dbcp configuration.
126    *
127    * @param map
128    *          the map
129    *
130    * @return the basic data source
131    */
132   private BasicDataSource legacyDbcpConfiguration(Map map) {
133     BasicDataSource basicDataSource = null;
134     if (map.containsKey("JDBC.Driver")) {
135       basicDataSource = new BasicDataSource();
136       String driver = (String) map.get("JDBC.Driver");
137       String url = (String) map.get("JDBC.ConnectionURL");
138       String username = (String) map.get("JDBC.Username");
139       String password = (String) map.get("JDBC.Password");
140       String validationQuery = (String) map.get("Pool.ValidationQuery");
141       String maxActive = (String) map.get("Pool.MaximumActiveConnections");
142       String maxIdle = (String) map.get("Pool.MaximumIdleConnections");
143       String maxWait = (String) map.get("Pool.MaximumWait");
144 
145       basicDataSource.setUrl(url);
146       basicDataSource.setDriverClassName(driver);
147       basicDataSource.setUsername(username);
148       basicDataSource.setPassword(password);
149 
150       if (notEmpty(validationQuery)) {
151         basicDataSource.setValidationQuery(validationQuery);
152       }
153 
154       if (notEmpty(maxActive)) {
155         basicDataSource.setMaxTotal(Integer.parseInt(maxActive));
156       }
157 
158       if (notEmpty(maxIdle)) {
159         basicDataSource.setMaxIdle(Integer.parseInt(maxIdle));
160       }
161 
162       if (notEmpty(maxWait)) {
163         basicDataSource.setMaxWait(Duration.ofMillis(Integer.parseInt(maxWait)));
164       }
165 
166       Iterator props = map.keySet().iterator();
167       while (props.hasNext()) {
168         String propertyName = (String) props.next();
169         if (propertyName.startsWith(ADD_DRIVER_PROPS_PREFIX)) {
170           String value = (String) map.get(propertyName);
171           basicDataSource.addConnectionProperty(propertyName.substring(ADD_DRIVER_PROPS_PREFIX_LENGTH), value);
172         }
173       }
174     }
175     return basicDataSource;
176   }
177 
178   /**
179    * Not empty.
180    *
181    * @param s
182    *          the s
183    *
184    * @return true, if successful
185    */
186   private boolean notEmpty(String s) {
187     return s != null && !s.isEmpty();
188   }
189 
190 }