1 16 17 package org.springframework.jdbc.datasource; 18 19 import java.lang.reflect.InvocationHandler ; 20 import java.lang.reflect.InvocationTargetException ; 21 import java.lang.reflect.Method ; 22 import java.lang.reflect.Proxy ; 23 import java.sql.Connection ; 24 import java.sql.SQLException ; 25 26 import org.springframework.beans.factory.DisposableBean; 27 import org.springframework.jdbc.CannotGetJdbcConnectionException; 28 import org.springframework.util.Assert; 29 import org.springframework.util.ObjectUtils; 30 31 58 public class SingleConnectionDataSource extends DriverManagerDataSource 59 implements SmartDataSource, DisposableBean { 60 61 62 private boolean suppressClose; 63 64 65 private Boolean autoCommit; 66 67 68 private Connection target; 69 70 71 private Connection connection; 72 73 74 private final Object connectionMonitor = new Object (); 75 76 77 80 public SingleConnectionDataSource() { 81 } 82 83 94 public SingleConnectionDataSource( 95 String driverClassName, String url, String username, String password, boolean suppressClose) 96 throws CannotGetJdbcConnectionException { 97 98 super(driverClassName, url, username, password); 99 this.suppressClose = suppressClose; 100 } 101 102 112 public SingleConnectionDataSource(String url, String username, String password, boolean suppressClose) 113 throws CannotGetJdbcConnectionException { 114 115 super(url, username, password); 116 this.suppressClose = suppressClose; 117 } 118 119 127 public SingleConnectionDataSource(String url, boolean suppressClose) 128 throws CannotGetJdbcConnectionException { 129 130 super(url); 131 this.suppressClose = suppressClose; 132 } 133 134 142 public SingleConnectionDataSource(Connection target, boolean suppressClose) { 143 Assert.notNull(target, "Connection must not be null"); 144 this.target = target; 145 this.suppressClose = suppressClose; 146 this.connection = (suppressClose ? getCloseSuppressingConnectionProxy(target) : target); 147 } 148 149 150 154 public void setSuppressClose(boolean suppressClose) { 155 this.suppressClose = suppressClose; 156 } 157 158 162 protected boolean isSuppressClose() { 163 return suppressClose; 164 } 165 166 169 public void setAutoCommit(boolean autoCommit) { 170 this.autoCommit = (autoCommit ? Boolean.TRUE : Boolean.FALSE); 171 } 172 173 177 protected Boolean getAutoCommitValue() { 178 return autoCommit; 179 } 180 181 182 public Connection getConnection() throws SQLException { 183 synchronized (this.connectionMonitor) { 184 if (this.connection == null) { 185 initConnection(); 187 } 188 if (this.connection.isClosed()) { 189 throw new SQLException ( 190 "Connection was closed in SingleConnectionDataSource. Check that user code checks " + 191 "shouldClose() before closing Connections, or set 'suppressClose' to 'true'"); 192 } 193 return this.connection; 194 } 195 } 196 197 202 public Connection getConnection(String username, String password) throws SQLException { 203 if (ObjectUtils.nullSafeEquals(username, getUsername()) && 204 ObjectUtils.nullSafeEquals(password, getPassword())) { 205 return getConnection(); 206 } 207 else { 208 throw new SQLException ("SingleConnectionDataSource does not support custom username and password"); 209 } 210 } 211 212 215 public boolean shouldClose(Connection con) { 216 synchronized (this.connectionMonitor) { 217 return (con != this.connection && con != this.target); 218 } 219 } 220 221 227 public void destroy() { 228 synchronized (this.connectionMonitor) { 229 closeConnection(); 230 } 231 } 232 233 234 237 public void initConnection() throws SQLException { 238 if (getUrl() == null) { 239 throw new IllegalStateException ("'url' property is required for lazily initializing a Connection"); 240 } 241 synchronized (this.connectionMonitor) { 242 closeConnection(); 243 this.target = getConnectionFromDriverManager(); 244 prepareConnection(this.target); 245 if (logger.isInfoEnabled()) { 246 logger.info("Established shared JDBC Connection: " + this.target); 247 } 248 this.connection = (isSuppressClose() ? getCloseSuppressingConnectionProxy(this.target) : this.target); 249 } 250 } 251 252 255 public void resetConnection() { 256 synchronized (this.connectionMonitor) { 257 closeConnection(); 258 this.target = null; 259 this.connection = null; 260 } 261 } 262 263 270 protected void prepareConnection(Connection con) throws SQLException { 271 Boolean autoCommit = getAutoCommitValue(); 272 if (autoCommit != null && con.getAutoCommit() != autoCommit.booleanValue()) { 273 con.setAutoCommit(autoCommit.booleanValue()); 274 } 275 } 276 277 280 private void closeConnection() { 281 if (this.target != null) { 282 try { 283 this.target.close(); 284 } 285 catch (Throwable ex) { 286 logger.warn("Could not close shared JDBC Connection", ex); 287 } 288 } 289 } 290 291 297 protected Connection getCloseSuppressingConnectionProxy(Connection target) { 298 return (Connection ) Proxy.newProxyInstance( 299 ConnectionProxy.class.getClassLoader(), 300 new Class [] {ConnectionProxy.class}, 301 new CloseSuppressingInvocationHandler(target)); 302 } 303 304 305 308 private static class CloseSuppressingInvocationHandler implements InvocationHandler { 309 310 private final Connection target; 311 312 public CloseSuppressingInvocationHandler(Connection target) { 313 this.target = target; 314 } 315 316 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable { 317 319 if (method.getName().equals("getTargetConnection")) { 320 return this.target; 322 } 323 else if (method.getName().equals("equals")) { 324 return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); 326 } 327 else if (method.getName().equals("hashCode")) { 328 return new Integer (hashCode()); 330 } 331 else if (method.getName().equals("close")) { 332 return null; 334 } 335 336 try { 338 return method.invoke(this.target, args); 339 } 340 catch (InvocationTargetException ex) { 341 throw ex.getTargetException(); 342 } 343 } 344 } 345 346 } 347 | Popular Tags |