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}