KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > jdbc > datasource > TransactionAwareDataSourceProxy


1 /*
2  * Copyright 2002-2007 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
17 package org.springframework.jdbc.datasource;
18
19 import java.lang.reflect.InvocationHandler JavaDoc;
20 import java.lang.reflect.InvocationTargetException JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Proxy JavaDoc;
23 import java.sql.Connection JavaDoc;
24 import java.sql.SQLException JavaDoc;
25 import java.sql.Statement JavaDoc;
26
27 import javax.sql.DataSource JavaDoc;
28
29 import org.springframework.util.Assert;
30
31 /**
32  * Proxy for a target JDBC {@link javax.sql.DataSource}, adding awareness of
33  * Spring-managed transactions. Similar to a transactional JNDI DataSource
34  * as provided by a J2EE server.
35  *
36  * <p>Data access code that should remain unaware of Spring's data access support
37  * can work with this proxy to seamlessly participate in Spring-managed transactions.
38  * Note that the transaction manager, for example {@link DataSourceTransactionManager},
39  * still needs to work with the underlying DataSource, <i>not</i> with this proxy.
40  *
41  * <p><b>Make sure that TransactionAwareDataSourceProxy is the outermost DataSource
42  * of a chain of DataSource proxies/adapters.</b> TransactionAwareDataSourceProxy
43  * can delegate either directly to the target connection pool or to some
44  * intermediary proxy/adapter like {@link LazyConnectionDataSourceProxy} or
45  * {@link UserCredentialsDataSourceAdapter}.
46  *
47  * <p>Delegates to {@link DataSourceUtils} for automatically participating in
48  * thread-bound transactions, for example managed by {@link DataSourceTransactionManager}.
49  * <code>getConnection</code> calls and <code>close</code> calls on returned Connections
50  * will behave properly within a transaction, i.e. always operate on the transactional
51  * Connection. If not within a transaction, normal DataSource behavior applies.
52  *
53  * <p>This proxy allows data access code to work with the plain JDBC API and still
54  * participate in Spring-managed transactions, similar to JDBC code in a J2EE/JTA
55  * environment. However, if possible, use Spring's DataSourceUtils, JdbcTemplate or
56  * JDBC operation objects to get transaction participation even without a proxy for
57  * the target DataSource, avoiding the need to define such a proxy in the first place.
58  *
59  * <p>As a further effect, using a transaction-aware DataSource will apply remaining
60  * transaction timeouts to all created JDBC (Prepared/Callable)Statement. This means
61  * that all operations performed through standard JDBC will automatically participate
62  * in Spring-managed transaction timeouts.
63  *
64  * <p><b>NOTE:</b> This DataSource proxy needs to return wrapped Connections
65  * (which implement the {@link ConnectionProxy} interface) in order to handle
66  * close calls properly. Therefore, the returned Connections cannot be cast
67  * to a native JDBC Connection type like OracleConnection or to a connection
68  * pool implementation type. Use a corresponding
69  * {@link org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor}
70  * to retrieve the native JDBC Connection.
71  *
72  * @author Juergen Hoeller
73  * @since 1.1
74  * @see javax.sql.DataSource#getConnection()
75  * @see java.sql.Connection#close()
76  * @see DataSourceUtils#doGetConnection
77  * @see DataSourceUtils#applyTransactionTimeout
78  * @see DataSourceUtils#doReleaseConnection
79  */

80 public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
81
82     /**
83      * Create a new TransactionAwareDataSourceProxy.
84      * @see #setTargetDataSource
85      */

86     public TransactionAwareDataSourceProxy() {
87     }
88
89     /**
90      * Create a new TransactionAwareDataSourceProxy.
91      * @param targetDataSource the target DataSource
92      */

93     public TransactionAwareDataSourceProxy(DataSource JavaDoc targetDataSource) {
94         super(targetDataSource);
95     }
96
97
98     /**
99      * Delegate to DataSourceUtils for automatically participating in Spring-managed
100      * transactions. Throws the original SQLException, if any.
101      * <p>The returned Connection handle implements the ConnectionProxy interface,
102      * allowing to retrieve the underlying target Connection.
103      * @return a transactional Connection if any, a new one else
104      * @see DataSourceUtils#doGetConnection
105      * @see ConnectionProxy#getTargetConnection
106      */

107     public Connection JavaDoc getConnection() throws SQLException JavaDoc {
108         Assert.state(getTargetDataSource() != null, "'targetDataSource' is required");
109         Connection JavaDoc con = DataSourceUtils.doGetConnection(getTargetDataSource());
110         return getTransactionAwareConnectionProxy(con, getTargetDataSource());
111     }
112
113     /**
114      * Wrap the given Connection with a proxy that delegates every method call to it
115      * but delegates <code>close</code> calls to DataSourceUtils.
116      * @param target the original Connection to wrap
117      * @param dataSource DataSource that the Connection came from
118      * @return the wrapped Connection
119      * @see java.sql.Connection#close()
120      * @see DataSourceUtils#doReleaseConnection
121      */

122     protected Connection JavaDoc getTransactionAwareConnectionProxy(Connection JavaDoc target, DataSource JavaDoc dataSource) {
123         return (Connection JavaDoc) Proxy.newProxyInstance(
124                 ConnectionProxy.class.getClassLoader(),
125                 new Class JavaDoc[] {ConnectionProxy.class},
126                 new TransactionAwareInvocationHandler(target, dataSource));
127     }
128
129
130     /**
131      * Invocation handler that delegates close calls on JDBC Connections
132      * to DataSourceUtils for being aware of thread-bound transactions.
133      */

134     private static class TransactionAwareInvocationHandler implements InvocationHandler JavaDoc {
135
136         private final Connection JavaDoc target;
137
138         private final DataSource JavaDoc dataSource;
139
140         public TransactionAwareInvocationHandler(Connection JavaDoc target, DataSource JavaDoc dataSource) {
141             this.target = target;
142             this.dataSource = dataSource;
143         }
144
145         public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
146             // Invocation on ConnectionProxy interface coming in...
147

148             if (method.getName().equals("getTargetConnection")) {
149                 // Handle getTargetConnection method: return underlying Connection.
150
return this.target;
151             }
152             else if (method.getName().equals("equals")) {
153                 // Only considered as equal when proxies are identical.
154
return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
155             }
156             else if (method.getName().equals("hashCode")) {
157                 // Use hashCode of Connection proxy.
158
return new Integer JavaDoc(hashCode());
159             }
160             else if (method.getName().equals("close")) {
161                 // Handle close method: only close if not within a transaction.
162
DataSourceUtils.doReleaseConnection(this.target, this.dataSource);
163                 return null;
164             }
165
166             // Invoke method on target Connection.
167
try {
168                 Object JavaDoc retVal = method.invoke(this.target, args);
169
170                 // If return value is a Statement, apply transaction timeout.
171
// Applies to createStatement, prepareStatement, prepareCall.
172
if (retVal instanceof Statement JavaDoc) {
173                     DataSourceUtils.applyTransactionTimeout((Statement JavaDoc) retVal, this.dataSource);
174                 }
175
176                 return retVal;
177             }
178             catch (InvocationTargetException JavaDoc ex) {
179                 throw ex.getTargetException();
180             }
181         }
182     }
183
184 }
185
Popular Tags