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