SingleConnectionDataSource.java
Upload User: jiancairen
Upload Date: 2007-08-27
Package Size: 26458k
Code Size: 8k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * Copyright 2002-2004 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.springframework.jdbc.datasource;
  17. import java.lang.reflect.InvocationHandler;
  18. import java.lang.reflect.InvocationTargetException;
  19. import java.lang.reflect.Method;
  20. import java.lang.reflect.Proxy;
  21. import java.sql.Connection;
  22. import java.sql.SQLException;
  23. import org.springframework.beans.factory.DisposableBean;
  24. import org.springframework.jdbc.CannotGetJdbcConnectionException;
  25. import org.springframework.util.ObjectUtils;
  26. /**
  27.  * Implementation of SmartDataSource that wraps a single connection which is not
  28.  * closed after use. Obviously, this is not multi-threading capable.
  29.  *
  30.  * <p>Note that at shutdown, someone should close the underlying connection via the
  31.  * close() method. Client code will never call close on the connection handle if it
  32.  * is SmartDataSource-aware (e.g. uses DataSourceUtils.closeConnectionIfNecessary).
  33.  *
  34.  * <p>If client code will call close in the assumption of a pooled connection,
  35.  * like when using persistence tools, set suppressClose to true. This will return a
  36.  * close-suppressing proxy instead of the physical connection. Be aware that you will
  37.  * not be able to cast this to a native OracleConnection or the like anymore.
  38.  *
  39.  * <p>This is primarily a test class. For example, it enables easy testing of code
  40.  * outside an application server, in conjunction with a simple JNDI environment.
  41.  * In contrast to DriverManagerDataSource, it reuses the same connection all the time,
  42.  * avoiding excessive creation of physical connections.
  43.  *
  44.  * @author Rod Johnson
  45.  * @author Juergen Hoeller
  46.  * @see DataSourceUtils#closeConnectionIfNecessary
  47.  */
  48. public class SingleConnectionDataSource extends DriverManagerDataSource implements DisposableBean {
  49. private boolean suppressClose;
  50. /** Wrapped connection */
  51. private Connection target;
  52. /** Proxy connection */
  53. private Connection connection;
  54. /**
  55.  * Constructor for bean-style configuration.
  56.  */
  57. public SingleConnectionDataSource() {
  58. }
  59. /**
  60.  * Create a new SingleConnectionDataSource with the given standard
  61.  * DriverManager parameters.
  62.  * @param suppressClose if the returned connection should be a
  63.  * close-suppressing proxy or the physical connection.
  64.  */
  65. public SingleConnectionDataSource(
  66. String driverClassName, String url, String username, String password, boolean suppressClose)
  67. throws CannotGetJdbcConnectionException {
  68. super(driverClassName, url, username, password);
  69. this.suppressClose = suppressClose;
  70. }
  71. /**
  72.  * Create a new SingleConnectionDataSource with a given connection.
  73.  * @param target underlying target connection
  74.  * @param suppressClose if the connection should be wrapped with a* connection that
  75.  * suppresses close() calls (to allow for normal close() usage in applications that
  76.  * expect a pooled connection but do not know our SmartDataSource interface).
  77.  */
  78. public SingleConnectionDataSource(Connection target, boolean suppressClose) {
  79. if (target == null) {
  80. throw new IllegalArgumentException("Connection is null in SingleConnectionDataSource");
  81. }
  82. this.suppressClose = suppressClose;
  83. init(target);
  84. }
  85. /**
  86.  * Set if the returned connection should be a close-suppressing proxy
  87.  * or the physical connection.
  88.  */
  89. public void setSuppressClose(boolean suppressClose) {
  90. this.suppressClose = suppressClose;
  91. }
  92. /**
  93.  * Return if the returned connection will be a close-suppressing proxy
  94.  * or the physical connection.
  95.  */
  96. public boolean isSuppressClose() {
  97. return suppressClose;
  98. }
  99. /**
  100.  * This is a single connection: Do not close it when returning to the "pool".
  101.  */
  102. public boolean shouldClose(Connection con) {
  103. return false;
  104. }
  105. /**
  106.  * Initialize the underlying connection via DriverManager.
  107.  */
  108. protected void init() throws SQLException {
  109. init(getConnectionFromDriverManager());
  110. }
  111. /**
  112.  * Initialize the underlying connection.
  113.  * Wraps the connection with a close-suppressing proxy if necessary.
  114.  * @param target the JDBC Connection to use
  115.  */
  116. protected void init(Connection target) {
  117. this.target = target;
  118. this.connection = this.suppressClose ? getCloseSuppressingConnectionProxy(target) : target;
  119. }
  120. /**
  121.  * Close the underlying connection.
  122.  * The provider of this DataSource needs to care for proper shutdown.
  123.  * <p>As this bean implements DisposableBean, a bean factory will
  124.  * automatically invoke this on destruction of its cached singletons.
  125.  */
  126. public void destroy() throws SQLException {
  127. if (this.target != null) {
  128. this.target.close();
  129. }
  130. }
  131. public Connection getConnection() throws SQLException {
  132. synchronized (this) {
  133. if (this.connection == null) {
  134. // no underlying connection -> lazy init via DriverManager
  135. init();
  136. }
  137. }
  138. if (this.connection.isClosed()) {
  139. throw new SQLException(
  140. "Connection was closed in SingleConnectionDataSource. Check that user code checks " +
  141. "shouldClose() before closing connections, or set suppressClose to true");
  142. }
  143. if (logger.isDebugEnabled()) {
  144. logger.debug("Returning single connection: " + this.connection);
  145. }
  146. return this.connection;
  147. }
  148. /**
  149.  * Specifying a custom username and password doesn't make sense
  150.  * with a single connection. Returns the single connection if given
  151.  * the same username and password, though.
  152.  */
  153. public Connection getConnection(String username, String password) throws SQLException {
  154. if (ObjectUtils.nullSafeEquals(username, getUsername()) &&
  155. ObjectUtils.nullSafeEquals(password, getPassword())) {
  156. return getConnection();
  157. }
  158. else {
  159. throw new SQLException("SingleConnectionDataSource does not support custom username and password");
  160. }
  161. }
  162. /**
  163.  * Wrap the given Connection with a proxy that delegates every method call to it
  164.  * but suppresses close calls.
  165.  * @param target the original Connection to wrap
  166.  * @return the wrapped Connection
  167.  */
  168. protected Connection getCloseSuppressingConnectionProxy(Connection target) {
  169. return (Connection) Proxy.newProxyInstance(
  170. ConnectionProxy.class.getClassLoader(),
  171. new Class[] {ConnectionProxy.class},
  172. new CloseSuppressingInvocationHandler(target));
  173. }
  174. /**
  175.  * Invocation handler that suppresses close calls on JDBC Connections.
  176.  */
  177. private static class CloseSuppressingInvocationHandler implements InvocationHandler {
  178. private static final String GET_TARGET_CONNECTION_METHOD_NAME = "getTargetConnection";
  179. private static final String CONNECTION_CLOSE_METHOD_NAME = "close";
  180. private final Connection target;
  181. private CloseSuppressingInvocationHandler(Connection target) {
  182. this.target = target;
  183. }
  184. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  185. if (method.getName().equals(GET_TARGET_CONNECTION_METHOD_NAME)) {
  186. return this.target;
  187. }
  188. if (method.getName().equals(CONNECTION_CLOSE_METHOD_NAME)) {
  189. // don't pass the call on
  190. return null;
  191. }
  192. try {
  193. return method.invoke(this.target, args);
  194. }
  195. catch (InvocationTargetException ex) {
  196. throw ex.getTargetException();
  197. }
  198. }
  199. }
  200. }