View Javadoc
1   /*
2    *    Copyright 2009-2024 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 org.apache.ibatis.datasource.unpooled;
17  
18  import java.io.PrintWriter;
19  import java.sql.Connection;
20  import java.sql.Driver;
21  import java.sql.DriverManager;
22  import java.sql.DriverPropertyInfo;
23  import java.sql.SQLException;
24  import java.util.Enumeration;
25  import java.util.Map;
26  import java.util.Properties;
27  import java.util.concurrent.ConcurrentHashMap;
28  import java.util.concurrent.Executors;
29  import java.util.logging.Logger;
30  
31  import javax.sql.DataSource;
32  
33  import org.apache.ibatis.io.Resources;
34  import org.apache.ibatis.util.MapUtil;
35  
36  /**
37   * @author Clinton Begin
38   * @author Eduardo Macarron
39   */
40  public class UnpooledDataSource implements DataSource {
41  
42    private ClassLoader driverClassLoader;
43    private Properties driverProperties;
44    private static final Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();
45  
46    private String driver;
47    private String url;
48    private String username;
49    private String password;
50  
51    private Boolean autoCommit;
52    private Integer defaultTransactionIsolationLevel;
53    private Integer defaultNetworkTimeout;
54  
55    static {
56      Enumeration<Driver> drivers = DriverManager.getDrivers();
57      while (drivers.hasMoreElements()) {
58        Driver driver = drivers.nextElement();
59        registeredDrivers.put(driver.getClass().getName(), driver);
60      }
61    }
62  
63    public UnpooledDataSource() {
64    }
65  
66    public UnpooledDataSource(String driver, String url, String username, String password) {
67      this.driver = driver;
68      this.url = url;
69      this.username = username;
70      this.password = password;
71    }
72  
73    public UnpooledDataSource(String driver, String url, Properties driverProperties) {
74      this.driver = driver;
75      this.url = url;
76      this.driverProperties = driverProperties;
77    }
78  
79    public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username,
80        String password) {
81      this.driverClassLoader = driverClassLoader;
82      this.driver = driver;
83      this.url = url;
84      this.username = username;
85      this.password = password;
86    }
87  
88    public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
89      this.driverClassLoader = driverClassLoader;
90      this.driver = driver;
91      this.url = url;
92      this.driverProperties = driverProperties;
93    }
94  
95    @Override
96    public Connection getConnection() throws SQLException {
97      return doGetConnection(username, password);
98    }
99  
100   @Override
101   public Connection getConnection(String username, String password) throws SQLException {
102     return doGetConnection(username, password);
103   }
104 
105   @Override
106   public void setLoginTimeout(int loginTimeout) {
107     DriverManager.setLoginTimeout(loginTimeout);
108   }
109 
110   @Override
111   public int getLoginTimeout() {
112     return DriverManager.getLoginTimeout();
113   }
114 
115   @Override
116   public void setLogWriter(PrintWriter logWriter) {
117     DriverManager.setLogWriter(logWriter);
118   }
119 
120   @Override
121   public PrintWriter getLogWriter() {
122     return DriverManager.getLogWriter();
123   }
124 
125   public ClassLoader getDriverClassLoader() {
126     return driverClassLoader;
127   }
128 
129   public void setDriverClassLoader(ClassLoader driverClassLoader) {
130     this.driverClassLoader = driverClassLoader;
131   }
132 
133   public Properties getDriverProperties() {
134     return driverProperties;
135   }
136 
137   public void setDriverProperties(Properties driverProperties) {
138     this.driverProperties = driverProperties;
139   }
140 
141   public String getDriver() {
142     return driver;
143   }
144 
145   public void setDriver(String driver) {
146     this.driver = driver;
147   }
148 
149   public String getUrl() {
150     return url;
151   }
152 
153   public void setUrl(String url) {
154     this.url = url;
155   }
156 
157   public String getUsername() {
158     return username;
159   }
160 
161   public void setUsername(String username) {
162     this.username = username;
163   }
164 
165   public String getPassword() {
166     return password;
167   }
168 
169   public void setPassword(String password) {
170     this.password = password;
171   }
172 
173   public Boolean isAutoCommit() {
174     return autoCommit;
175   }
176 
177   public void setAutoCommit(Boolean autoCommit) {
178     this.autoCommit = autoCommit;
179   }
180 
181   public Integer getDefaultTransactionIsolationLevel() {
182     return defaultTransactionIsolationLevel;
183   }
184 
185   public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
186     this.defaultTransactionIsolationLevel = defaultTransactionIsolationLevel;
187   }
188 
189   /**
190    * Gets the default network timeout.
191    *
192    * @return the default network timeout
193    *
194    * @since 3.5.2
195    */
196   public Integer getDefaultNetworkTimeout() {
197     return defaultNetworkTimeout;
198   }
199 
200   /**
201    * Sets the default network timeout value to wait for the database operation to complete. See
202    * {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
203    *
204    * @param defaultNetworkTimeout
205    *          The time in milliseconds to wait for the database operation to complete.
206    *
207    * @since 3.5.2
208    */
209   public void setDefaultNetworkTimeout(Integer defaultNetworkTimeout) {
210     this.defaultNetworkTimeout = defaultNetworkTimeout;
211   }
212 
213   private Connection doGetConnection(String username, String password) throws SQLException {
214     Properties props = new Properties();
215     if (driverProperties != null) {
216       props.putAll(driverProperties);
217     }
218     if (username != null) {
219       props.setProperty("user", username);
220     }
221     if (password != null) {
222       props.setProperty("password", password);
223     }
224     return doGetConnection(props);
225   }
226 
227   private Connection doGetConnection(Properties properties) throws SQLException {
228     initializeDriver();
229     Connection connection = DriverManager.getConnection(url, properties);
230     configureConnection(connection);
231     return connection;
232   }
233 
234   private void initializeDriver() throws SQLException {
235     try {
236       MapUtil.computeIfAbsent(registeredDrivers, driver, x -> {
237         Class<?> driverType;
238         try {
239           if (driverClassLoader != null) {
240             driverType = Class.forName(x, true, driverClassLoader);
241           } else {
242             driverType = Resources.classForName(x);
243           }
244           Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
245           DriverManager.registerDriver(new DriverProxy(driverInstance));
246           return driverInstance;
247         } catch (Exception e) {
248           throw new RuntimeException("Error setting driver on UnpooledDataSource.", e);
249         }
250       });
251     } catch (RuntimeException re) {
252       throw new SQLException("Error setting driver on UnpooledDataSource.", re.getCause());
253     }
254   }
255 
256   private void configureConnection(Connection conn) throws SQLException {
257     if (defaultNetworkTimeout != null) {
258       conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
259     }
260     if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
261       conn.setAutoCommit(autoCommit);
262     }
263     if (defaultTransactionIsolationLevel != null) {
264       conn.setTransactionIsolation(defaultTransactionIsolationLevel);
265     }
266   }
267 
268   private static class DriverProxy implements Driver {
269     private final Driver driver;
270 
271     DriverProxy(Driver d) {
272       this.driver = d;
273     }
274 
275     @Override
276     public boolean acceptsURL(String u) throws SQLException {
277       return this.driver.acceptsURL(u);
278     }
279 
280     @Override
281     public Connection connect(String u, Properties p) throws SQLException {
282       return this.driver.connect(u, p);
283     }
284 
285     @Override
286     public int getMajorVersion() {
287       return this.driver.getMajorVersion();
288     }
289 
290     @Override
291     public int getMinorVersion() {
292       return this.driver.getMinorVersion();
293     }
294 
295     @Override
296     public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
297       return this.driver.getPropertyInfo(u, p);
298     }
299 
300     @Override
301     public boolean jdbcCompliant() {
302       return this.driver.jdbcCompliant();
303     }
304 
305     @Override
306     public Logger getParentLogger() {
307       return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
308     }
309   }
310 
311   @Override
312   public <T> T unwrap(Class<T> iface) throws SQLException {
313     throw new SQLException(getClass().getName() + " is not a wrapper.");
314   }
315 
316   @Override
317   public boolean isWrapperFor(Class<?> iface) throws SQLException {
318     return false;
319   }
320 
321   @Override
322   public Logger getParentLogger() {
323     // requires JDK version 1.6
324     return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
325   }
326 
327 }