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