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 019/** 020 * {@link TransactionSynchronization} implementation that manages a 021 * {@link ResourceHolder} bound through {@link TransactionSynchronizationManager}. 022 * 023 * @author Juergen Hoeller 024 * @since 2.5.5 025 */ 026public abstract class ResourceHolderSynchronization<H extends ResourceHolder, K> 027 implements TransactionSynchronization { 028 029 private final H resourceHolder; 030 031 private final K resourceKey; 032 033 private volatile boolean holderActive = true; 034 035 036 /** 037 * Create a new ResourceHolderSynchronization for the given holder. 038 * @param resourceHolder the ResourceHolder to manage 039 * @param resourceKey the key to bind the ResourceHolder for 040 * @see TransactionSynchronizationManager#bindResource 041 */ 042 public ResourceHolderSynchronization(H resourceHolder, K resourceKey) { 043 this.resourceHolder = resourceHolder; 044 this.resourceKey = resourceKey; 045 } 046 047 048 @Override 049 public void suspend() { 050 if (this.holderActive) { 051 TransactionSynchronizationManager.unbindResource(this.resourceKey); 052 } 053 } 054 055 @Override 056 public void resume() { 057 if (this.holderActive) { 058 TransactionSynchronizationManager.bindResource(this.resourceKey, this.resourceHolder); 059 } 060 } 061 062 @Override 063 public void flush() { 064 flushResource(this.resourceHolder); 065 } 066 067 @Override 068 public void beforeCommit(boolean readOnly) { 069 } 070 071 @Override 072 public void beforeCompletion() { 073 if (shouldUnbindAtCompletion()) { 074 TransactionSynchronizationManager.unbindResource(this.resourceKey); 075 this.holderActive = false; 076 if (shouldReleaseBeforeCompletion()) { 077 releaseResource(this.resourceHolder, this.resourceKey); 078 } 079 } 080 } 081 082 @Override 083 public void afterCommit() { 084 if (!shouldReleaseBeforeCompletion()) { 085 processResourceAfterCommit(this.resourceHolder); 086 } 087 } 088 089 @Override 090 public void afterCompletion(int status) { 091 if (shouldUnbindAtCompletion()) { 092 boolean releaseNecessary = false; 093 if (this.holderActive) { 094 // The thread-bound resource holder might not be available anymore, 095 // since afterCompletion might get called from a different thread. 096 this.holderActive = false; 097 TransactionSynchronizationManager.unbindResourceIfPossible(this.resourceKey); 098 this.resourceHolder.unbound(); 099 releaseNecessary = true; 100 } 101 else { 102 releaseNecessary = shouldReleaseAfterCompletion(this.resourceHolder); 103 } 104 if (releaseNecessary) { 105 releaseResource(this.resourceHolder, this.resourceKey); 106 } 107 } 108 else { 109 // Probably a pre-bound resource... 110 cleanupResource(this.resourceHolder, this.resourceKey, (status == STATUS_COMMITTED)); 111 } 112 this.resourceHolder.reset(); 113 } 114 115 116 /** 117 * Return whether this holder should be unbound at completion 118 * (or should rather be left bound to the thread after the transaction). 119 * <p>The default implementation returns {@code true}. 120 */ 121 protected boolean shouldUnbindAtCompletion() { 122 return true; 123 } 124 125 /** 126 * Return whether this holder's resource should be released before 127 * transaction completion ({@code true}) or rather after 128 * transaction completion ({@code false}). 129 * <p>Note that resources will only be released when they are 130 * unbound from the thread ({@link #shouldUnbindAtCompletion()}). 131 * <p>The default implementation returns {@code true}. 132 * @see #releaseResource 133 */ 134 protected boolean shouldReleaseBeforeCompletion() { 135 return true; 136 } 137 138 /** 139 * Return whether this holder's resource should be released after 140 * transaction completion ({@code true}). 141 * <p>The default implementation returns {@code !shouldReleaseBeforeCompletion()}, 142 * releasing after completion if no attempt was made before completion. 143 * @see #releaseResource 144 */ 145 protected boolean shouldReleaseAfterCompletion(H resourceHolder) { 146 return !shouldReleaseBeforeCompletion(); 147 } 148 149 /** 150 * Flush callback for the given resource holder. 151 * @param resourceHolder the resource holder to flush 152 */ 153 protected void flushResource(H resourceHolder) { 154 } 155 156 /** 157 * After-commit callback for the given resource holder. 158 * Only called when the resource hasn't been released yet 159 * ({@link #shouldReleaseBeforeCompletion()}). 160 * @param resourceHolder the resource holder to process 161 */ 162 protected void processResourceAfterCommit(H resourceHolder) { 163 } 164 165 /** 166 * Release the given resource (after it has been unbound from the thread). 167 * @param resourceHolder the resource holder to process 168 * @param resourceKey the key that the ResourceHolder was bound for 169 */ 170 protected void releaseResource(H resourceHolder, K resourceKey) { 171 } 172 173 /** 174 * Perform a cleanup on the given resource (which is left bound to the thread). 175 * @param resourceHolder the resource holder to process 176 * @param resourceKey the key that the ResourceHolder was bound for 177 * @param committed whether the transaction has committed ({@code true}) 178 * or rolled back ({@code false}) 179 */ 180 protected void cleanupResource(H resourceHolder, K resourceKey, boolean committed) { 181 } 182 183}