001: /*
002: * Copyright 2002-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.jdbc.datasource;
018:
019: import java.sql.Connection;
020: import java.sql.DriverManager;
021: import java.sql.SQLException;
022: import java.util.Properties;
023:
024: import org.springframework.jdbc.CannotGetJdbcConnectionException;
025: import org.springframework.util.ClassUtils;
026: import org.springframework.util.StringUtils;
027:
028: /**
029: * Simple implementation of the standard JDBC DataSource interface, configuring
030: * a plain old JDBC Driver via bean properties, and returning a new Connection
031: * for every <code>getConnection</code> call.
032: *
033: * <p><b>NOTE: This class is not an actual connection pool; it does not actually
034: * pool Connections.</b> It just serves as simple replacement for a full-blown
035: * connection pool, implementing the same standard interface, but creating new
036: * Connections on every call.
037: *
038: * <p>Useful for test or standalone environments outside of a J2EE container, either
039: * as a DataSource bean in a corresponding ApplicationContext or in conjunction with
040: * a simple JNDI environment. Pool-assuming <code>Connection.close()</code> calls will
041: * simply close the Connection, so any DataSource-aware persistence code should work.
042: *
043: * <p>In a J2EE container, it is recommended to use a JNDI DataSource provided by
044: * the container. Such a DataSource can be exposed as a DataSource bean in a Spring
045: * ApplicationContext via JndiObjectFactoryBean, for seamless switching to and from
046: * a local DataSource bean like this class. For tests, you can then either set up a
047: * mock JNDI environment through Spring's SimpleNamingContextBuilder, or switch the
048: * bean definition to a local DataSource (which is simpler and thus recommended).
049: *
050: * <p>If you need a "real" connection pool outside of a J2EE container, consider
051: * <a href="http://jakarta.apache.org/commons/dbcp">Apache's Jakarta Commons DBCP</a>
052: * or <a href="http://sourceforge.net/projects/c3p0">C3P0</a>.
053: * Commons DBCP's BasicDataSource and C3P0's ComboPooledDataSource are full
054: * connection pool beans, supporting the same basic properties as this class
055: * plus specific settings (such as minimal/maximal pool size etc).
056: *
057: * <p>Commons DBCP's BasicDataSource can even be used as a direct replacement for an
058: * instance of this class just by changing the class name of the bean definition to
059: * "org.apache.commons.dbcp.BasicDataSource", because the names of all common
060: * properties match exactly. Note that both BasicDataSource and ComboPooledDataSource
061: * should be defined with destroy-method="close", for immediate shutdown when the
062: * Spring ApplicationContext shuts down.
063: *
064: * @author Juergen Hoeller
065: * @since 14.03.2003
066: * @see org.springframework.jndi.JndiObjectFactoryBean
067: * @see org.springframework.mock.jndi.SimpleNamingContextBuilder
068: * @see org.apache.commons.dbcp.BasicDataSource
069: * @see com.mchange.v2.c3p0.ComboPooledDataSource
070: */
071: public class DriverManagerDataSource extends AbstractDataSource {
072:
073: private String driverClassName;
074:
075: private String url;
076:
077: private String username;
078:
079: private String password;
080:
081: private Properties connectionProperties;
082:
083: /**
084: * Constructor for bean-style configuration.
085: */
086: public DriverManagerDataSource() {
087: }
088:
089: /**
090: * Create a new DriverManagerDataSource with the given standard
091: * DriverManager parameters.
092: * @param driverClassName the JDBC driver class name
093: * @param url the JDBC URL to use for accessing the DriverManager
094: * @param username the JDBC username to use for accessing the DriverManager
095: * @param password the JDBC password to use for accessing the DriverManager
096: * @see java.sql.DriverManager#getConnection(String, String, String)
097: */
098: public DriverManagerDataSource(String driverClassName, String url,
099: String username, String password)
100: throws CannotGetJdbcConnectionException {
101: setDriverClassName(driverClassName);
102: setUrl(url);
103: setUsername(username);
104: setPassword(password);
105: }
106:
107: /**
108: * Create a new DriverManagerDataSource with the given standard
109: * DriverManager parameters.
110: * @param url the JDBC URL to use for accessing the DriverManager
111: * @param username the JDBC username to use for accessing the DriverManager
112: * @param password the JDBC password to use for accessing the DriverManager
113: * @see java.sql.DriverManager#getConnection(String, String, String)
114: */
115: public DriverManagerDataSource(String url, String username,
116: String password) throws CannotGetJdbcConnectionException {
117: setUrl(url);
118: setUsername(username);
119: setPassword(password);
120: }
121:
122: /**
123: * Create a new DriverManagerDataSource with the given JDBC URL,
124: * not specifying a username or password for JDBC access.
125: * @param url the JDBC URL to use for accessing the DriverManager
126: * @see java.sql.DriverManager#getConnection(String)
127: */
128: public DriverManagerDataSource(String url)
129: throws CannotGetJdbcConnectionException {
130: setUrl(url);
131: }
132:
133: /**
134: * Set the JDBC driver class name. This driver will get initialized
135: * on startup, registering itself with the JDK's DriverManager.
136: * <p>Alternatively, consider initializing the JDBC driver yourself
137: * before instantiating this DataSource.
138: * @see Class#forName(String)
139: * @see java.sql.DriverManager#registerDriver(java.sql.Driver)
140: */
141: public void setDriverClassName(String driverClassName)
142: throws CannotGetJdbcConnectionException {
143: if (!StringUtils.hasText(driverClassName)) {
144: throw new IllegalArgumentException(
145: "driverClassName must not be empty");
146: }
147: this .driverClassName = driverClassName.trim();
148: try {
149: Class.forName(this .driverClassName, true, ClassUtils
150: .getDefaultClassLoader());
151: } catch (ClassNotFoundException ex) {
152: throw new CannotGetJdbcConnectionException(
153: "Could not load JDBC driver class ["
154: + this .driverClassName + "]", ex);
155: }
156: if (logger.isInfoEnabled()) {
157: logger.info("Loaded JDBC driver: " + this .driverClassName);
158: }
159: }
160:
161: /**
162: * Return the JDBC driver class name, if any.
163: */
164: public String getDriverClassName() {
165: return driverClassName;
166: }
167:
168: /**
169: * Set the JDBC URL to use for accessing the DriverManager.
170: * @see java.sql.DriverManager#getConnection(String, String, String)
171: */
172: public void setUrl(String url) {
173: if (!StringUtils.hasText(url)) {
174: throw new IllegalArgumentException("url must not be empty");
175: }
176: this .url = url.trim();
177: }
178:
179: /**
180: * Return the JDBC URL to use for accessing the DriverManager.
181: */
182: public String getUrl() {
183: return url;
184: }
185:
186: /**
187: * Set the JDBC username to use for accessing the DriverManager.
188: * @see java.sql.DriverManager#getConnection(String, String, String)
189: */
190: public void setUsername(String username) {
191: this .username = username;
192: }
193:
194: /**
195: * Return the JDBC username to use for accessing the DriverManager.
196: */
197: public String getUsername() {
198: return username;
199: }
200:
201: /**
202: * Set the JDBC password to use for accessing the DriverManager.
203: * @see java.sql.DriverManager#getConnection(String, String, String)
204: */
205: public void setPassword(String password) {
206: this .password = password;
207: }
208:
209: /**
210: * Return the JDBC password to use for accessing the DriverManager.
211: */
212: public String getPassword() {
213: return password;
214: }
215:
216: /**
217: * Specify arbitrary connection properties as key/value pairs,
218: * to be passed to the DriverManager.
219: * <p>Can also contain "user" and "password" properties. However,
220: * any "username" and "password" bean properties specified on this
221: * DataSource will override the corresponding connection properties.
222: * @see java.sql.DriverManager#getConnection(String, java.util.Properties)
223: */
224: public void setConnectionProperties(Properties connectionProperties) {
225: this .connectionProperties = connectionProperties;
226: }
227:
228: /**
229: * Return the connection properties to be passed to the DriverManager, if any.
230: */
231: public Properties getConnectionProperties() {
232: return connectionProperties;
233: }
234:
235: /**
236: * This implementation delegates to <code>getConnectionFromDriverManager</code>,
237: * using the default username and password of this DataSource.
238: * @see #getConnectionFromDriverManager()
239: */
240: public Connection getConnection() throws SQLException {
241: return getConnectionFromDriverManager();
242: }
243:
244: /**
245: * This implementation delegates to <code>getConnectionFromDriverManager</code>,
246: * using the given username and password.
247: * @see #getConnectionFromDriverManager(String, String)
248: */
249: public Connection getConnection(String username, String password)
250: throws SQLException {
251: return getConnectionFromDriverManager(username, password);
252: }
253:
254: /**
255: * Get a Connection from the DriverManager,
256: * using the default username and password of this DataSource.
257: * @see #getConnectionFromDriverManager(String, String)
258: */
259: protected Connection getConnectionFromDriverManager()
260: throws SQLException {
261: return getConnectionFromDriverManager(getUsername(),
262: getPassword());
263: }
264:
265: /**
266: * Build properties for the DriverManager, including the given username
267: * and password (if any).
268: * @see #getConnectionFromDriverManager(String, java.util.Properties)
269: */
270: protected Connection getConnectionFromDriverManager(
271: String username, String password) throws SQLException {
272:
273: Properties props = new Properties(getConnectionProperties());
274: if (username != null) {
275: props.setProperty("user", username);
276: }
277: if (password != null) {
278: props.setProperty("password", password);
279: }
280: return getConnectionFromDriverManager(getUrl(), props);
281: }
282:
283: /**
284: * Getting a connection using the nasty static from DriverManager is extracted
285: * into a protected method to allow for easy unit testing.
286: * @see java.sql.DriverManager#getConnection(String, java.util.Properties)
287: */
288: protected Connection getConnectionFromDriverManager(String url,
289: Properties props) throws SQLException {
290:
291: if (logger.isDebugEnabled()) {
292: logger.debug("Creating new JDBC Connection to [" + url
293: + "]");
294: }
295: return DriverManager.getConnection(url, props);
296: }
297:
298: }
|