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