diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/ExtendedConnectionDataSourceProxy.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/ExtendedConnectionDataSourceProxy.java index 49c77c532..835b5044c 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/ExtendedConnectionDataSourceProxy.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/database/ExtendedConnectionDataSourceProxy.java @@ -26,47 +26,60 @@ import java.sql.SQLException; import javax.sql.DataSource; +import org.springframework.beans.factory.InitializingBean; import org.springframework.jdbc.datasource.ConnectionProxy; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.jdbc.datasource.SmartDataSource; import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.util.Assert; /** - * Implementation of {@link SmartDataSource} that is capable of keeping a single JDBC Connection - * which is NOT closed after each use even if {@link Connection#close()} is called. + * Implementation of {@link SmartDataSource} that is capable of keeping a single + * JDBC Connection which is NOT closed after each use even if + * {@link Connection#close()} is called. * - * The connection can be kept open over multiple transactions when used together with any of Spring's - * {@link org.springframework.transaction.PlatformTransactionManager} implementations. + * The connection can be kept open over multiple transactions when used together + * with any of Spring's + * {@link org.springframework.transaction.PlatformTransactionManager} + * implementations. * - *
Loosely based on the SingleConnectionDataSource implementation in Spring Core. Intended - * to be used with the {@link JdbcCursorItemReader} to provide a connection that remains - * open across transaction boundaries, It remains open for the life of the cursor, and can be - * shared with the main transaction of the rest of the step processing. - * - *
Once close suppression has been turned on for a connection, it will be returned for the first - * {@link #getConnection()} call. Any subsequent calls to {@link #getConnection()} will retrieve a - * new connection from the wrapped {@link DataSource} until the {@link DataSourceUtils} queries - * whether the connection should be closed or not by calling {@link #shouldClose(Connection)} for - * the close-suppressed {@link Connection}. At that point the cycle starts over again, and the next - * {@link #getConnection()} call will have the {@link Connection} that is being close-suppressed - * returned. This allows the use of the close-suppressed {@link Connection} to be the main - * {@link Connection} for an extended data access process. The close suppression is turned off by - * calling {@link #stopCloseSuppression(Connection)}. + *
+ * Loosely based on the SingleConnectionDataSource implementation in Spring + * Core. Intended to be used with the {@link JdbcCursorItemReader} to provide a + * connection that remains open across transaction boundaries, It remains open + * for the life of the cursor, and can be shared with the main transaction of + * the rest of the step processing. * - *
This class is not multi-threading capable. + *
+ * Once close suppression has been turned on for a connection, it will be + * returned for the first {@link #getConnection()} call. Any subsequent calls to + * {@link #getConnection()} will retrieve a new connection from the wrapped + * {@link DataSource} until the {@link DataSourceUtils} queries whether the + * connection should be closed or not by calling + * {@link #shouldClose(Connection)} for the close-suppressed {@link Connection}. + * At that point the cycle starts over again, and the next + * {@link #getConnection()} call will have the {@link Connection} that is being + * close-suppressed returned. This allows the use of the close-suppressed + * {@link Connection} to be the main {@link Connection} for an extended data + * access process. The close suppression is turned off by calling + * {@link #stopCloseSuppression(Connection)}. + * + *
+ * This class is not multi-threading capable. + * + *
+ * The connection returned will be a close-suppressing proxy instead of the
+ * physical {@link Connection}. Be aware that you will not be able to cast this
+ * to a native OracleConnection or the like anymore; you need to
+ * use a {@link org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor}.
*
- *
The connection returned will be a close-suppressing proxy instead of the physical
- * {@link Connection}. Be aware that you will not be able to cast this to a native
- * OracleConnection or the like anymore; you need to use a
- * {@link org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor}.
- *
* @author Thomas Risberg
* @see #getConnection()
* @see java.sql.Connection#close()
* @see DataSourceUtils#releaseConnection
* @see org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor
*/
-public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
+public class ExtendedConnectionDataSourceProxy implements SmartDataSource, InitializingBean {
/** Provided DataSource */
private DataSource dataSource;
@@ -87,7 +100,8 @@ public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
}
/**
- * Constructor that takes as a parameter with the {&link DataSource} to be wrapped.
+ * Constructor that takes as a parameter with the {&link DataSource} to be
+ * wrapped.
*/
public ExtendedConnectionDataSourceProxy(DataSource dataSource) {
this.dataSource = dataSource;
@@ -114,9 +128,11 @@ public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
}
/**
- * Return the status of close suppression being activated for a given {@link Connection}
+ * Return the status of close suppression being activated for a given
+ * {@link Connection}
*
- * @param connection the {@link Connection} that the close suppression status is requested for
+ * @param connection the {@link Connection} that the close suppression
+ * status is requested for
* @return true or false
*/
public boolean isCloseSuppressionActive(Connection connection) {
@@ -125,23 +141,25 @@ public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
}
return connection.equals(closeSuppressedConnection) ? true : false;
}
-
+
/**
*
- * @param connection the {@link Connection} that close suppression is requested for
+ * @param connection the {@link Connection} that close suppression is
+ * requested for
*/
public void startCloseSuppression(Connection connection) {
synchronized (this.connectionMonitor) {
closeSuppressedConnection = connection;
- if(TransactionSynchronizationManager.isActualTransactionActive()) {
+ if (TransactionSynchronizationManager.isActualTransactionActive()) {
borrowedConnection = true;
}
}
}
-
+
/**
*
- * @param connection the {@link Connection} that close suppression should be turned off for
+ * @param connection the {@link Connection} that close suppression should be
+ * turned off for
*/
public void stopCloseSuppression(Connection connection) {
synchronized (this.connectionMonitor) {
@@ -149,7 +167,7 @@ public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
borrowedConnection = false;
}
}
-
+
public Connection getConnection() throws SQLException {
synchronized (this.connectionMonitor) {
return initConnection(null, null);
@@ -204,29 +222,27 @@ public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
}
/**
- * Wrap the given Connection with a proxy that delegates every method call to it
- * but suppresses close calls.
+ * Wrap the given Connection with a proxy that delegates every method call
+ * to it but suppresses close calls.
* @param target the original Connection to wrap
* @return the wrapped Connection
*/
protected Connection getCloseSuppressingConnectionProxy(Connection target) {
- return (Connection) Proxy.newProxyInstance(
- ConnectionProxy.class.getClassLoader(),
- new Class[] {ConnectionProxy.class},
- new CloseSuppressingInvocationHandler(target, this));
+ return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(),
+ new Class[] { ConnectionProxy.class }, new CloseSuppressingInvocationHandler(target, this));
}
-
/**
- * Invocation handler that suppresses close calls on JDBC Connections until the associated instance of the
- * ExtendedConnectionDataSourceProxy determines the connection should actually be closed.
+ * Invocation handler that suppresses close calls on JDBC Connections until
+ * the associated instance of the ExtendedConnectionDataSourceProxy
+ * determines the connection should actually be closed.
*/
private static class CloseSuppressingInvocationHandler implements InvocationHandler {
private final Connection target;
-
+
private final ExtendedConnectionDataSourceProxy dataSource;
-
+
public CloseSuppressingInvocationHandler(Connection target, ExtendedConnectionDataSourceProxy dataSource) {
this.dataSource = dataSource;
this.target = target;
@@ -244,8 +260,9 @@ public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
return new Integer(System.identityHashCode(proxy));
}
else if (method.getName().equals("close")) {
- // Handle close method: don't pass the call on if we are suppressing close calls.
- if (dataSource.completeCloseCall((Connection)proxy)) {
+ // Handle close method: don't pass the call on if we are
+ // suppressing close calls.
+ if (dataSource.completeCloseCall((Connection) proxy)) {
return null;
}
else {
@@ -254,7 +271,8 @@ public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
}
}
else if (method.getName().equals("getTargetConnection")) {
- // Handle getTargetConnection method: return underlying Connection.
+ // Handle getTargetConnection method: return underlying
+ // Connection.
return this.target;
}
@@ -267,5 +285,39 @@ public class ExtendedConnectionDataSourceProxy implements SmartDataSource {
}
}
}
-
+
+ /**
+ * Performs only a 'shallow' non-recursive check of self's and delegate's
+ * class to retain Java 5 compatibility.
+ */
+ public boolean isWrapperFor(Class> iface) throws SQLException {
+ if (iface.isAssignableFrom(SmartDataSource.class) || iface.isAssignableFrom(dataSource.getClass())) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns either self or delegate (in this order) if one of them can be
+ * cast to supplied parameter class. Does *not* support recursive unwrapping
+ * of the delegate to retain Java 5 compatibility.
+ */
+ public