KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > transaction > support > TransactionSynchronizationManager


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.transaction.support;
18
19 import java.util.ArrayList JavaDoc;
20 import java.util.Collections JavaDoc;
21 import java.util.Comparator JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.LinkedList JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import org.springframework.core.OrderComparator;
31 import org.springframework.util.Assert;
32
33 /**
34  * Central helper that manages resources and transaction synchronizations per thread.
35  * To be used by resource management code but not by typical application code.
36  *
37  * <p>Supports one resource per key without overwriting, that is, a resource needs
38  * to be removed before a new one can be set for the same key.
39  * Supports a list of transaction synchronizations if synchronization is active.
40  *
41  * <p>Resource management code should check for thread-bound resources, e.g. JDBC
42  * Connections or Hibernate Sessions, via <code>getResource</code>. Such code is
43  * normally not supposed to bind resources to threads, as this is the responsibility
44  * of transaction managers. A further option is to lazily bind on first use if
45  * transaction synchronization is active, for performing transactions that span
46  * an arbitrary number of resources.
47  *
48  * <p>Transaction synchronization must be activated and deactivated by a transaction
49  * manager via {@link #initSynchronization()} and {@link #clearSynchronization()}.
50  * This is automatically supported by {@link AbstractPlatformTransactionManager},
51  * and thus by all standard Spring transaction managers, such as
52  * {@link org.springframework.transaction.jta.JtaTransactionManager} and
53  * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}.
54  *
55  * <p>Resource management code should only register synchronizations when this
56  * manager is active, which can be checked via {@link #isSynchronizationActive};
57  * it should perform immediate resource cleanup else. If transaction synchronization
58  * isn't active, there is either no current transaction, or the transaction manager
59  * doesn't support transaction synchronization.
60  *
61  * <p>Synchronization is for example used to always return the same resources
62  * within a JTA transaction, e.g. a JDBC Connection or a Hibernate Session for
63  * any given DataSource or SessionFactory, respectively.
64  *
65  * @author Juergen Hoeller
66  * @since 02.06.2003
67  * @see #isSynchronizationActive
68  * @see #registerSynchronization
69  * @see TransactionSynchronization
70  * @see AbstractPlatformTransactionManager#setTransactionSynchronization
71  * @see org.springframework.transaction.jta.JtaTransactionManager
72  * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
73  * @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
74  */

75 public abstract class TransactionSynchronizationManager {
76
77     private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
78
79
80     private static final ThreadLocal JavaDoc resources = new ThreadLocal JavaDoc();
81
82     private static final ThreadLocal JavaDoc synchronizations = new ThreadLocal JavaDoc();
83
84     private static final Comparator JavaDoc synchronizationComparator = new OrderComparator();
85
86     private static final ThreadLocal JavaDoc currentTransactionName = new ThreadLocal JavaDoc();
87
88     private static final ThreadLocal JavaDoc currentTransactionReadOnly = new ThreadLocal JavaDoc();
89
90     private static final ThreadLocal JavaDoc currentTransactionIsolationLevel= new ThreadLocal JavaDoc();
91
92     private static final ThreadLocal JavaDoc actualTransactionActive = new ThreadLocal JavaDoc();
93
94
95     //-------------------------------------------------------------------------
96
// Management of transaction-associated resource handles
97
//-------------------------------------------------------------------------
98

99     /**
100      * Return all resources that are bound to the current thread.
101      * <p>Mainly for debugging purposes. Resource managers should always invoke
102      * <code>hasResource</code> for a specific resource key that they are interested in.
103      * @return a Map with resource keys (usually the resource factory) and resource
104      * values (usually the active resource object), or an empty Map if there are
105      * currently no resources bound
106      * @see #hasResource
107      */

108     public static Map JavaDoc getResourceMap() {
109         Map JavaDoc map = (Map JavaDoc) resources.get();
110         return (map != null ? Collections.unmodifiableMap(map) : Collections.EMPTY_MAP);
111     }
112
113     /**
114      * Check if there is a resource for the given key bound to the current thread.
115      * @param key the key to check (usually the resource factory)
116      * @return if there is a value bound to the current thread
117      * @see ResourceTransactionManager#getResourceFactory()
118      */

119     public static boolean hasResource(Object JavaDoc key) {
120         Assert.notNull(key, "Key must not be null");
121         Map JavaDoc map = (Map JavaDoc) resources.get();
122         return (map != null && map.containsKey(key));
123     }
124
125     /**
126      * Retrieve a resource for the given key that is bound to the current thread.
127      * @param key the key to check (usually the resource factory)
128      * @return a value bound to the current thread (usually the active
129      * resource object), or <code>null</code> if none
130      * @see ResourceTransactionManager#getResourceFactory()
131      */

132     public static Object JavaDoc getResource(Object JavaDoc key) {
133         Assert.notNull(key, "Key must not be null");
134         Map JavaDoc map = (Map JavaDoc) resources.get();
135         if (map == null) {
136             return null;
137         }
138         Object JavaDoc value = map.get(key);
139         if (value != null && logger.isDebugEnabled()) {
140             logger.debug("Retrieved value [" + value + "] for key [" + key + "] bound to thread [" +
141                     Thread.currentThread().getName() + "]");
142         }
143         return value;
144     }
145
146     /**
147      * Bind the given resource for the given key to the current thread.
148      * @param key the key to bind the value to (usually the resource factory)
149      * @param value the value to bind (usually the active resource object)
150      * @throws IllegalStateException if there is already a value bound to the thread
151      * @see ResourceTransactionManager#getResourceFactory()
152      */

153     public static void bindResource(Object JavaDoc key, Object JavaDoc value) throws IllegalStateException JavaDoc {
154         Assert.notNull(key, "Key must not be null");
155         Assert.notNull(value, "Value must not be null");
156         Map JavaDoc map = (Map JavaDoc) resources.get();
157         // set ThreadLocal Map if none found
158
if (map == null) {
159             map = new HashMap JavaDoc();
160             resources.set(map);
161         }
162         if (map.containsKey(key)) {
163             throw new IllegalStateException JavaDoc("Already value [" + map.get(key) + "] for key [" + key +
164                     "] bound to thread [" + Thread.currentThread().getName() + "]");
165         }
166         map.put(key, value);
167         if (logger.isDebugEnabled()) {
168             logger.debug("Bound value [" + value + "] for key [" + key + "] to thread [" +
169                     Thread.currentThread().getName() + "]");
170         }
171     }
172
173     /**
174      * Unbind a resource for the given key from the current thread.
175      * @param key the key to unbind (usually the resource factory)
176      * @return the previously bound value (usually the active resource object)
177      * @throws IllegalStateException if there is no value bound to the thread
178      * @see ResourceTransactionManager#getResourceFactory()
179      */

180     public static Object JavaDoc unbindResource(Object JavaDoc key) throws IllegalStateException JavaDoc {
181         Assert.notNull(key, "Key must not be null");
182         Map JavaDoc map = (Map JavaDoc) resources.get();
183         if (map == null || !map.containsKey(key)) {
184             throw new IllegalStateException JavaDoc(
185                     "No value for key [" + key + "] bound to thread [" + Thread.currentThread().getName() + "]");
186         }
187         Object JavaDoc value = map.remove(key);
188         // remove entire ThreadLocal if empty
189
if (map.isEmpty()) {
190             resources.set(null);
191         }
192         if (logger.isDebugEnabled()) {
193             logger.debug("Removed value [" + value + "] for key [" + key + "] from thread [" +
194                     Thread.currentThread().getName() + "]");
195         }
196         return value;
197     }
198
199
200     //-------------------------------------------------------------------------
201
// Management of transaction synchronizations
202
//-------------------------------------------------------------------------
203

204     /**
205      * Return if transaction synchronization is active for the current thread.
206      * Can be called before register to avoid unnecessary instance creation.
207      * @see #registerSynchronization
208      */

209     public static boolean isSynchronizationActive() {
210         return (synchronizations.get() != null);
211     }
212
213     /**
214      * Activate transaction synchronization for the current thread.
215      * Called by a transaction manager on transaction begin.
216      * @throws IllegalStateException if synchronization is already active
217      */

218     public static void initSynchronization() throws IllegalStateException JavaDoc {
219         if (isSynchronizationActive()) {
220             throw new IllegalStateException JavaDoc("Cannot activate transaction synchronization - already active");
221         }
222         logger.debug("Initializing transaction synchronization");
223         synchronizations.set(new LinkedList JavaDoc());
224     }
225
226     /**
227      * Register a new transaction synchronization for the current thread.
228      * Typically called by resource management code.
229      * <p>Note that synchronizations can implement the
230      * {@link org.springframework.core.Ordered} interface.
231      * They will be executed in an order according to their order value (if any).
232      * @param synchronization the synchronization object to register
233      * @throws IllegalStateException if transaction synchronization is not active
234      * @see org.springframework.core.Ordered
235      */

236     public static void registerSynchronization(TransactionSynchronization synchronization)
237         throws IllegalStateException JavaDoc {
238
239         Assert.notNull(synchronization, "TransactionSynchronization must not be null");
240         if (!isSynchronizationActive()) {
241             throw new IllegalStateException JavaDoc("Transaction synchronization is not active");
242         }
243         List JavaDoc synchs = (List JavaDoc) synchronizations.get();
244         synchs.add(synchronization);
245     }
246
247     /**
248      * Return an unmodifiable snapshot list of all registered synchronizations
249      * for the current thread.
250      * @return unmodifiable List of TransactionSynchronization instances
251      * @throws IllegalStateException if synchronization is not active
252      * @see TransactionSynchronization
253      */

254     public static List JavaDoc getSynchronizations() throws IllegalStateException JavaDoc {
255         if (!isSynchronizationActive()) {
256             throw new IllegalStateException JavaDoc("Transaction synchronization is not active");
257         }
258         List JavaDoc synchs = (List JavaDoc) synchronizations.get();
259         // Sort lazily here, not in registerSynchronization.
260
Collections.sort(synchs, synchronizationComparator);
261         // Return unmodifiable snapshot, to avoid ConcurrentModificationExceptions
262
// while iterating and invoking synchronization callbacks that in turn
263
// might register further synchronizations.
264
return Collections.unmodifiableList(new ArrayList JavaDoc(synchs));
265     }
266
267     /**
268      * Deactivate transaction synchronization for the current thread.
269      * Called by the transaction manager on transaction cleanup.
270      * @throws IllegalStateException if synchronization is not active
271      */

272     public static void clearSynchronization() throws IllegalStateException JavaDoc {
273         if (!isSynchronizationActive()) {
274             throw new IllegalStateException JavaDoc("Cannot deactivate transaction synchronization - not active");
275         }
276         logger.debug("Clearing transaction synchronization");
277         synchronizations.set(null);
278     }
279
280
281     //-------------------------------------------------------------------------
282
// Exposure of transaction characteristics
283
//-------------------------------------------------------------------------
284

285     /**
286      * Expose the name of the current transaction, if any.
287      * Called by the transaction manager on transaction begin and on cleanup.
288      * @param name the name of the transaction, or <code>null</code> to reset it
289      * @see org.springframework.transaction.TransactionDefinition#getName()
290      */

291     public static void setCurrentTransactionName(String JavaDoc name) {
292         currentTransactionName.set(name);
293     }
294
295     /**
296      * Return the name of the current transaction, or <code>null</code> if none set.
297      * To be called by resource management code for optimizations per use case,
298      * for example to optimize fetch strategies for specific named transactions.
299      * @see org.springframework.transaction.TransactionDefinition#getName()
300      */

301     public static String JavaDoc getCurrentTransactionName() {
302         return (String JavaDoc) currentTransactionName.get();
303     }
304
305     /**
306      * Expose a read-only flag for the current transaction.
307      * Called by the transaction manager on transaction begin and on cleanup.
308      * @param readOnly <code>true</code> to mark the current transaction
309      * as read-only; <code>false</code> to reset such a read-only marker
310      * @see org.springframework.transaction.TransactionDefinition#isReadOnly()
311      */

312     public static void setCurrentTransactionReadOnly(boolean readOnly) {
313         currentTransactionReadOnly.set(readOnly ? Boolean.TRUE : null);
314     }
315
316     /**
317      * Return whether the current transaction is marked as read-only.
318      * To be called by resource management code when preparing a newly
319      * created resource (for example, a Hibernate Session).
320      * <p>Note that transaction synchronizations receive the read-only flag
321      * as argument for the <code>beforeCommit</code> callback, to be able
322      * to suppress change detection on commit. The present method is meant
323      * to be used for earlier read-only checks, for example to set the
324      * flush mode of a Hibernate Session to "FlushMode.NEVER" upfront.
325      * @see org.springframework.transaction.TransactionDefinition#isReadOnly()
326      * @see TransactionSynchronization#beforeCommit(boolean)
327      * @see org.hibernate.Session#flush
328      * @see org.hibernate.Session#setFlushMode
329      * @see org.hibernate.FlushMode#NEVER
330      */

331     public static boolean isCurrentTransactionReadOnly() {
332         return (currentTransactionReadOnly.get() != null);
333     }
334
335     /**
336      * Expose an isolation level for the current transaction.
337      * Called by the transaction manager on transaction begin and on cleanup.
338      * @param isolationLevel the isolation level to expose, according to the
339      * JDBC Connection constants (equivalent to the corresponding Spring
340      * TransactionDefinition constants), or <code>null</code> to reset it
341      * @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED
342      * @see java.sql.Connection#TRANSACTION_READ_COMMITTED
343      * @see java.sql.Connection#TRANSACTION_REPEATABLE_READ
344      * @see java.sql.Connection#TRANSACTION_SERIALIZABLE
345      * @see org.springframework.transaction.TransactionDefinition#ISOLATION_READ_UNCOMMITTED
346      * @see org.springframework.transaction.TransactionDefinition#ISOLATION_READ_COMMITTED
347      * @see org.springframework.transaction.TransactionDefinition#ISOLATION_REPEATABLE_READ
348      * @see org.springframework.transaction.TransactionDefinition#ISOLATION_SERIALIZABLE
349      * @see org.springframework.transaction.TransactionDefinition#getIsolationLevel()
350      */

351     public static void setCurrentTransactionIsolationLevel(Integer JavaDoc isolationLevel) {
352         currentTransactionIsolationLevel.set(isolationLevel);
353     }
354
355     /**
356      * Return the isolation level for the current transaction, if any.
357      * To be called by resource management code when preparing a newly
358      * created resource (for example, a JDBC Connection).
359      * @return the currently exposed isolation level, according to the
360      * JDBC Connection constants (equivalent to the corresponding Spring
361      * TransactionDefinition constants), or <code>null</code> if none
362      * @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED
363      * @see java.sql.Connection#TRANSACTION_READ_COMMITTED
364      * @see java.sql.Connection#TRANSACTION_REPEATABLE_READ
365      * @see java.sql.Connection#TRANSACTION_SERIALIZABLE
366      * @see org.springframework.transaction.TransactionDefinition#ISOLATION_READ_UNCOMMITTED
367      * @see org.springframework.transaction.TransactionDefinition#ISOLATION_READ_COMMITTED
368      * @see org.springframework.transaction.TransactionDefinition#ISOLATION_REPEATABLE_READ
369      * @see org.springframework.transaction.TransactionDefinition#ISOLATION_SERIALIZABLE
370      * @see org.springframework.transaction.TransactionDefinition#getIsolationLevel()
371      */

372     public static Integer JavaDoc getCurrentTransactionIsolationLevel() {
373         return (Integer JavaDoc) currentTransactionIsolationLevel.get();
374     }
375
376     /**
377      * Expose whether there currently is an actual transaction active.
378      * Called by the transaction manager on transaction begin and on cleanup.
379      * @param active <code>true</code> to mark the current thread as being associated
380      * with an actual transaction; <code>false</code> to reset that marker
381      */

382     public static void setActualTransactionActive(boolean active) {
383         actualTransactionActive.set(active ? Boolean.TRUE : null);
384     }
385
386     /**
387      * Return whether there currently is an actual transaction active.
388      * This indicates whether the current thread is associated with an actual
389      * transaction rather than just with active transaction synchronization.
390      * <p>To be called by resource management code that wants to discriminate
391      * between active transaction synchronization (with or without backing
392      * resource transaction; also on PROPAGATION_SUPPORTS) and an actual
393      * transaction being active (with backing resource transaction;
394      * on PROPAGATION_REQUIRES, PROPAGATION_REQUIRES_NEW, etc).
395      * @see #isSynchronizationActive()
396      */

397     public static boolean isActualTransactionActive() {
398         return (actualTransactionActive.get() != null);
399     }
400
401
402     /**
403      * Clear the entire transaction synchronization state for the current thread:
404      * registered synchronizations as well as the various transaction characteristics.
405      * @see #clearSynchronization()
406      * @see #setCurrentTransactionName
407      * @see #setCurrentTransactionReadOnly
408      * @see #setCurrentTransactionIsolationLevel
409      * @see #setActualTransactionActive
410      */

411     public static void clear() {
412         clearSynchronization();
413         setCurrentTransactionName(null);
414         setCurrentTransactionReadOnly(false);
415         setCurrentTransactionIsolationLevel(null);
416         setActualTransactionActive(false);
417     }
418
419 }
420
Popular Tags