001/*
002 * Copyright 2002-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.transaction.support;
018
019import java.util.List;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023
024import org.springframework.aop.scope.ScopedObject;
025import org.springframework.core.InfrastructureProxy;
026import org.springframework.util.Assert;
027import org.springframework.util.ClassUtils;
028
029/**
030 * Utility methods for triggering specific {@link TransactionSynchronization}
031 * callback methods on all currently registered synchronizations.
032 *
033 * @author Juergen Hoeller
034 * @since 2.0
035 * @see TransactionSynchronization
036 * @see TransactionSynchronizationManager#getSynchronizations()
037 */
038public abstract class TransactionSynchronizationUtils {
039
040        private static final Log logger = LogFactory.getLog(TransactionSynchronizationUtils.class);
041
042        private static final boolean aopAvailable = ClassUtils.isPresent(
043                        "org.springframework.aop.scope.ScopedObject", TransactionSynchronizationUtils.class.getClassLoader());
044
045
046        /**
047         * Check whether the given resource transaction managers refers to the given
048         * (underlying) resource factory.
049         * @see ResourceTransactionManager#getResourceFactory()
050         * @see org.springframework.core.InfrastructureProxy#getWrappedObject()
051         */
052        public static boolean sameResourceFactory(ResourceTransactionManager tm, Object resourceFactory) {
053                return unwrapResourceIfNecessary(tm.getResourceFactory()).equals(unwrapResourceIfNecessary(resourceFactory));
054        }
055
056        /**
057         * Unwrap the given resource handle if necessary; otherwise return
058         * the given handle as-is.
059         * @see org.springframework.core.InfrastructureProxy#getWrappedObject()
060         */
061        static Object unwrapResourceIfNecessary(Object resource) {
062                Assert.notNull(resource, "Resource must not be null");
063                Object resourceRef = resource;
064                // unwrap infrastructure proxy
065                if (resourceRef instanceof InfrastructureProxy) {
066                        resourceRef = ((InfrastructureProxy) resourceRef).getWrappedObject();
067                }
068                if (aopAvailable) {
069                        // now unwrap scoped proxy
070                        resourceRef = ScopedProxyUnwrapper.unwrapIfNecessary(resourceRef);
071                }
072                return resourceRef;
073        }
074
075
076        /**
077         * Trigger {@code flush} callbacks on all currently registered synchronizations.
078         * @throws RuntimeException if thrown by a {@code flush} callback
079         * @see TransactionSynchronization#flush()
080         */
081        public static void triggerFlush() {
082                for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
083                        synchronization.flush();
084                }
085        }
086
087        /**
088         * Trigger {@code beforeCommit} callbacks on all currently registered synchronizations.
089         * @param readOnly whether the transaction is defined as read-only transaction
090         * @throws RuntimeException if thrown by a {@code beforeCommit} callback
091         * @see TransactionSynchronization#beforeCommit(boolean)
092         */
093        public static void triggerBeforeCommit(boolean readOnly) {
094                for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
095                        synchronization.beforeCommit(readOnly);
096                }
097        }
098
099        /**
100         * Trigger {@code beforeCompletion} callbacks on all currently registered synchronizations.
101         * @see TransactionSynchronization#beforeCompletion()
102         */
103        public static void triggerBeforeCompletion() {
104                for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
105                        try {
106                                synchronization.beforeCompletion();
107                        }
108                        catch (Throwable tsex) {
109                                logger.error("TransactionSynchronization.beforeCompletion threw exception", tsex);
110                        }
111                }
112        }
113
114        /**
115         * Trigger {@code afterCommit} callbacks on all currently registered synchronizations.
116         * @throws RuntimeException if thrown by a {@code afterCommit} callback
117         * @see TransactionSynchronizationManager#getSynchronizations()
118         * @see TransactionSynchronization#afterCommit()
119         */
120        public static void triggerAfterCommit() {
121                invokeAfterCommit(TransactionSynchronizationManager.getSynchronizations());
122        }
123
124        /**
125         * Actually invoke the {@code afterCommit} methods of the
126         * given Spring TransactionSynchronization objects.
127         * @param synchronizations List of TransactionSynchronization objects
128         * @see TransactionSynchronization#afterCommit()
129         */
130        public static void invokeAfterCommit(List<TransactionSynchronization> synchronizations) {
131                if (synchronizations != null) {
132                        for (TransactionSynchronization synchronization : synchronizations) {
133                                synchronization.afterCommit();
134                        }
135                }
136        }
137
138        /**
139         * Trigger {@code afterCompletion} callbacks on all currently registered synchronizations.
140         * @see TransactionSynchronizationManager#getSynchronizations()
141         * @param completionStatus the completion status according to the
142         * constants in the TransactionSynchronization interface
143         * @see TransactionSynchronization#afterCompletion(int)
144         * @see TransactionSynchronization#STATUS_COMMITTED
145         * @see TransactionSynchronization#STATUS_ROLLED_BACK
146         * @see TransactionSynchronization#STATUS_UNKNOWN
147         */
148        public static void triggerAfterCompletion(int completionStatus) {
149                List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
150                invokeAfterCompletion(synchronizations, completionStatus);
151        }
152
153        /**
154         * Actually invoke the {@code afterCompletion} methods of the
155         * given Spring TransactionSynchronization objects.
156         * @param synchronizations List of TransactionSynchronization objects
157         * @param completionStatus the completion status according to the
158         * constants in the TransactionSynchronization interface
159         * @see TransactionSynchronization#afterCompletion(int)
160         * @see TransactionSynchronization#STATUS_COMMITTED
161         * @see TransactionSynchronization#STATUS_ROLLED_BACK
162         * @see TransactionSynchronization#STATUS_UNKNOWN
163         */
164        public static void invokeAfterCompletion(List<TransactionSynchronization> synchronizations, int completionStatus) {
165                if (synchronizations != null) {
166                        for (TransactionSynchronization synchronization : synchronizations) {
167                                try {
168                                        synchronization.afterCompletion(completionStatus);
169                                }
170                                catch (Throwable tsex) {
171                                        logger.error("TransactionSynchronization.afterCompletion threw exception", tsex);
172                                }
173                        }
174                }
175        }
176
177
178        /**
179         * Inner class to avoid hard-coded dependency on AOP module.
180         */
181        private static class ScopedProxyUnwrapper {
182
183                public static Object unwrapIfNecessary(Object resource) {
184                        if (resource instanceof ScopedObject) {
185                                return ((ScopedObject) resource).getTargetObject();
186                        }
187                        else {
188                                return resource;
189                        }
190                }
191        }
192
193}