001/*
002 * Copyright 2002-2018 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.lang.Nullable;
027import org.springframework.util.Assert;
028import org.springframework.util.ClassUtils;
029
030/**
031 * Utility methods for triggering specific {@link TransactionSynchronization}
032 * callback methods on all currently registered synchronizations.
033 *
034 * @author Juergen Hoeller
035 * @since 2.0
036 * @see TransactionSynchronization
037 * @see TransactionSynchronizationManager#getSynchronizations()
038 */
039public abstract class TransactionSynchronizationUtils {
040
041        private static final Log logger = LogFactory.getLog(TransactionSynchronizationUtils.class);
042
043        private static final boolean aopAvailable = ClassUtils.isPresent(
044                        "org.springframework.aop.scope.ScopedObject", TransactionSynchronizationUtils.class.getClassLoader());
045
046
047        /**
048         * Check whether the given resource transaction managers refers to the given
049         * (underlying) resource factory.
050         * @see ResourceTransactionManager#getResourceFactory()
051         * @see org.springframework.core.InfrastructureProxy#getWrappedObject()
052         */
053        public static boolean sameResourceFactory(ResourceTransactionManager tm, Object resourceFactory) {
054                return unwrapResourceIfNecessary(tm.getResourceFactory()).equals(unwrapResourceIfNecessary(resourceFactory));
055        }
056
057        /**
058         * Unwrap the given resource handle if necessary; otherwise return
059         * the given handle as-is.
060         * @see org.springframework.core.InfrastructureProxy#getWrappedObject()
061         */
062        static Object unwrapResourceIfNecessary(Object resource) {
063                Assert.notNull(resource, "Resource must not be null");
064                Object resourceRef = resource;
065                // unwrap infrastructure proxy
066                if (resourceRef instanceof InfrastructureProxy) {
067                        resourceRef = ((InfrastructureProxy) resourceRef).getWrappedObject();
068                }
069                if (aopAvailable) {
070                        // now unwrap scoped proxy
071                        resourceRef = ScopedProxyUnwrapper.unwrapIfNecessary(resourceRef);
072                }
073                return resourceRef;
074        }
075
076
077        /**
078         * Trigger {@code flush} callbacks on all currently registered synchronizations.
079         * @throws RuntimeException if thrown by a {@code flush} callback
080         * @see TransactionSynchronization#flush()
081         */
082        public static void triggerFlush() {
083                for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
084                        synchronization.flush();
085                }
086        }
087
088        /**
089         * Trigger {@code beforeCommit} callbacks on all currently registered synchronizations.
090         * @param readOnly whether the transaction is defined as read-only transaction
091         * @throws RuntimeException if thrown by a {@code beforeCommit} callback
092         * @see TransactionSynchronization#beforeCommit(boolean)
093         */
094        public static void triggerBeforeCommit(boolean readOnly) {
095                for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
096                        synchronization.beforeCommit(readOnly);
097                }
098        }
099
100        /**
101         * Trigger {@code beforeCompletion} callbacks on all currently registered synchronizations.
102         * @see TransactionSynchronization#beforeCompletion()
103         */
104        public static void triggerBeforeCompletion() {
105                for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
106                        try {
107                                synchronization.beforeCompletion();
108                        }
109                        catch (Throwable tsex) {
110                                logger.error("TransactionSynchronization.beforeCompletion threw exception", tsex);
111                        }
112                }
113        }
114
115        /**
116         * Trigger {@code afterCommit} callbacks on all currently registered synchronizations.
117         * @throws RuntimeException if thrown by a {@code afterCommit} callback
118         * @see TransactionSynchronizationManager#getSynchronizations()
119         * @see TransactionSynchronization#afterCommit()
120         */
121        public static void triggerAfterCommit() {
122                invokeAfterCommit(TransactionSynchronizationManager.getSynchronizations());
123        }
124
125        /**
126         * Actually invoke the {@code afterCommit} methods of the
127         * given Spring TransactionSynchronization objects.
128         * @param synchronizations a List of TransactionSynchronization objects
129         * @see TransactionSynchronization#afterCommit()
130         */
131        public static void invokeAfterCommit(@Nullable List<TransactionSynchronization> synchronizations) {
132                if (synchronizations != null) {
133                        for (TransactionSynchronization synchronization : synchronizations) {
134                                synchronization.afterCommit();
135                        }
136                }
137        }
138
139        /**
140         * Trigger {@code afterCompletion} callbacks on all currently registered synchronizations.
141         * @param completionStatus the completion status according to the
142         * constants in the TransactionSynchronization interface
143         * @see TransactionSynchronizationManager#getSynchronizations()
144         * @see TransactionSynchronization#afterCompletion(int)
145         * @see TransactionSynchronization#STATUS_COMMITTED
146         * @see TransactionSynchronization#STATUS_ROLLED_BACK
147         * @see TransactionSynchronization#STATUS_UNKNOWN
148         */
149        public static void triggerAfterCompletion(int completionStatus) {
150                List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
151                invokeAfterCompletion(synchronizations, completionStatus);
152        }
153
154        /**
155         * Actually invoke the {@code afterCompletion} methods of the
156         * given Spring TransactionSynchronization objects.
157         * @param synchronizations a List of TransactionSynchronization objects
158         * @param completionStatus the completion status according to the
159         * constants in the TransactionSynchronization interface
160         * @see TransactionSynchronization#afterCompletion(int)
161         * @see TransactionSynchronization#STATUS_COMMITTED
162         * @see TransactionSynchronization#STATUS_ROLLED_BACK
163         * @see TransactionSynchronization#STATUS_UNKNOWN
164         */
165        public static void invokeAfterCompletion(@Nullable List<TransactionSynchronization> synchronizations,
166                        int completionStatus) {
167
168                if (synchronizations != null) {
169                        for (TransactionSynchronization synchronization : synchronizations) {
170                                try {
171                                        synchronization.afterCompletion(completionStatus);
172                                }
173                                catch (Throwable tsex) {
174                                        logger.error("TransactionSynchronization.afterCompletion threw exception", tsex);
175                                }
176                        }
177                }
178        }
179
180
181        /**
182         * Inner class to avoid hard-coded dependency on AOP module.
183         */
184        private static class ScopedProxyUnwrapper {
185
186                public static Object unwrapIfNecessary(Object resource) {
187                        if (resource instanceof ScopedObject) {
188                                return ((ScopedObject) resource).getTargetObject();
189                        }
190                        else {
191                                return resource;
192                        }
193                }
194        }
195
196}