001: /*
002: * Copyright 2002-2007 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.Assert;
026: import org.springframework.util.ClassUtils;
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: Assert.hasText(driverClassName,
144: "Property 'driverClassName' must not be empty");
145: this .driverClassName = driverClassName.trim();
146: try {
147: Class.forName(this .driverClassName, true, ClassUtils
148: .getDefaultClassLoader());
149: } catch (ClassNotFoundException ex) {
150: throw new CannotGetJdbcConnectionException(
151: "Could not load JDBC driver class ["
152: + this .driverClassName + "]", ex);
153: }
154: if (logger.isInfoEnabled()) {
155: logger.info("Loaded JDBC driver: " + this .driverClassName);
156: }
157: }
158:
159: /**
160: * Return the JDBC driver class name, if any.
161: */
162: public String getDriverClassName() {
163: return this .driverClassName;
164: }
165:
166: /**
167: * Set the JDBC URL to use for accessing the DriverManager.
168: * @see java.sql.DriverManager#getConnection(String, String, String)
169: */
170: public void setUrl(String url) {
171: Assert.hasText(url, "Property 'url' must not be empty");
172: this .url = url.trim();
173: }
174:
175: /**
176: * Return the JDBC URL to use for accessing the DriverManager.
177: */
178: public String getUrl() {
179: return this .url;
180: }
181:
182: /**
183: * Set the JDBC username to use for accessing the DriverManager.
184: * @see java.sql.DriverManager#getConnection(String, String, String)
185: */
186: public void setUsername(String username) {
187: this .username = username;
188: }
189:
190: /**
191: * Return the JDBC username to use for accessing the DriverManager.
192: */
193: public String getUsername() {
194: return this .username;
195: }
196:
197: /**
198: * Set the JDBC password to use for accessing the DriverManager.
199: * @see java.sql.DriverManager#getConnection(String, String, String)
200: */
201: public void setPassword(String password) {
202: this .password = password;
203: }
204:
205: /**
206: * Return the JDBC password to use for accessing the DriverManager.
207: */
208: public String getPassword() {
209: return this .password;
210: }
211:
212: /**
213: * Specify arbitrary connection properties as key/value pairs,
214: * to be passed to the DriverManager.
215: * <p>Can also contain "user" and "password" properties. However,
216: * any "username" and "password" bean properties specified on this
217: * DataSource will override the corresponding connection properties.
218: * @see java.sql.DriverManager#getConnection(String, java.util.Properties)
219: */
220: public void setConnectionProperties(Properties connectionProperties) {
221: this .connectionProperties = connectionProperties;
222: }
223:
224: /**
225: * Return the connection properties to be passed to the DriverManager, if any.
226: */
227: public Properties getConnectionProperties() {
228: return this .connectionProperties;
229: }
230:
231: /**
232: * This implementation delegates to <code>getConnectionFromDriverManager</code>,
233: * using the default username and password of this DataSource.
234: * @see #getConnectionFromDriverManager()
235: */
236: public Connection getConnection() throws SQLException {
237: return getConnectionFromDriverManager();
238: }
239:
240: /**
241: * This implementation delegates to <code>getConnectionFromDriverManager</code>,
242: * using the given username and password.
243: * @see #getConnectionFromDriverManager(String, String)
244: */
245: public Connection getConnection(String username, String password)
246: throws SQLException {
247: return getConnectionFromDriverManager(username, password);
248: }
249:
250: /**
251: * Get a Connection from the DriverManager,
252: * using the default username and password of this DataSource.
253: * @see #getConnectionFromDriverManager(String, String)
254: */
255: protected Connection getConnectionFromDriverManager()
256: throws SQLException {
257: return getConnectionFromDriverManager(getUsername(),
258: getPassword());
259: }
260:
261: /**
262: * Build properties for the DriverManager, including the given username
263: * and password (if any).
264: * @see #getConnectionFromDriverManager(String, java.util.Properties)
265: */
266: protected Connection getConnectionFromDriverManager(
267: String username, String password) throws SQLException {
268: Properties props = new Properties(getConnectionProperties());
269: if (username != null) {
270: props.setProperty("user", username);
271: }
272: if (password != null) {
273: props.setProperty("password", password);
274: }
275: return getConnectionFromDriverManager(getUrl(), props);
276: }
277:
278: /**
279: * Getting a connection using the nasty static from DriverManager is extracted
280: * into a protected method to allow for easy unit testing.
281: * @see java.sql.DriverManager#getConnection(String, java.util.Properties)
282: */
283: protected Connection getConnectionFromDriverManager(String url,
284: Properties props) throws SQLException {
285: if (logger.isDebugEnabled()) {
286: logger.debug("Creating new JDBC Connection to [" + url
287: + "]");
288: }
289: return DriverManager.getConnection(url, props);
290: }
291:
292: }
|