View Javadoc
1   /**
2    *    Copyright 2009-2019 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    * @since 3.5.2
189    */
190   public Integer getDefaultNetworkTimeout() {
191     return defaultNetworkTimeout;
192   }
193 
194   /**
195    * Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
196    *
197    * @param defaultNetworkTimeout
198    *          The time in milliseconds to wait for the database operation to complete.
199    * @since 3.5.2
200    */
201   public void setDefaultNetworkTimeout(Integer defaultNetworkTimeout) {
202     this.defaultNetworkTimeout = defaultNetworkTimeout;
203   }
204 
205   private Connection doGetConnection(String username, String password) throws SQLException {
206     Properties props = new Properties();
207     if (driverProperties != null) {
208       props.putAll(driverProperties);
209     }
210     if (username != null) {
211       props.setProperty("user", username);
212     }
213     if (password != null) {
214       props.setProperty("password", password);
215     }
216     return doGetConnection(props);
217   }
218 
219   private Connection doGetConnection(Properties properties) throws SQLException {
220     initializeDriver();
221     Connection connection = DriverManager.getConnection(url, properties);
222     configureConnection(connection);
223     return connection;
224   }
225 
226   private synchronized void initializeDriver() throws SQLException {
227     if (!registeredDrivers.containsKey(driver)) {
228       Class<?> driverType;
229       try {
230         if (driverClassLoader != null) {
231           driverType = Class.forName(driver, true, driverClassLoader);
232         } else {
233           driverType = Resources.classForName(driver);
234         }
235         // DriverManager requires the driver to be loaded via the system ClassLoader.
236         // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
237         Driver driverInstance = (Driver)driverType.getDeclaredConstructor().newInstance();
238         DriverManager.registerDriver(new DriverProxy(driverInstance));
239         registeredDrivers.put(driver, driverInstance);
240       } catch (Exception e) {
241         throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
242       }
243     }
244   }
245 
246   private void configureConnection(Connection conn) throws SQLException {
247     if (defaultNetworkTimeout != null) {
248       conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
249     }
250     if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
251       conn.setAutoCommit(autoCommit);
252     }
253     if (defaultTransactionIsolationLevel != null) {
254       conn.setTransactionIsolation(defaultTransactionIsolationLevel);
255     }
256   }
257 
258   private static class DriverProxy implements Driver {
259     private Driver driver;
260 
261     DriverProxy(Driver d) {
262       this.driver = d;
263     }
264 
265     @Override
266     public boolean acceptsURL(String u) throws SQLException {
267       return this.driver.acceptsURL(u);
268     }
269 
270     @Override
271     public Connection connect(String u, Properties p) throws SQLException {
272       return this.driver.connect(u, p);
273     }
274 
275     @Override
276     public int getMajorVersion() {
277       return this.driver.getMajorVersion();
278     }
279 
280     @Override
281     public int getMinorVersion() {
282       return this.driver.getMinorVersion();
283     }
284 
285     @Override
286     public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
287       return this.driver.getPropertyInfo(u, p);
288     }
289 
290     @Override
291     public boolean jdbcCompliant() {
292       return this.driver.jdbcCompliant();
293     }
294 
295     @Override
296     public Logger getParentLogger() {
297       return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
298     }
299   }
300 
301   @Override
302   public <T> T unwrap(Class<T> iface) throws SQLException {
303     throw new SQLException(getClass().getName() + " is not a wrapper.");
304   }
305 
306   @Override
307   public boolean isWrapperFor(Class<?> iface) throws SQLException {
308     return false;
309   }
310 
311   @Override
312   public Logger getParentLogger() {
313     // requires JDK version 1.6
314     return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
315   }
316 
317 }