/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.transaction.support;
/**
* {@link TransactionSynchronization} implementation that manages a
* {@link ResourceHolder} bound through {@link TransactionSynchronizationManager}.
*
* @author Juergen Hoeller
* @since 2.5.5
*/
public abstract class ResourceHolderSynchronization<H extends ResourceHolder, K>
implements TransactionSynchronization {
private final H resourceHolder;
private final K resourceKey;
private volatile boolean holderActive = true;
/**
* Create a new ResourceHolderSynchronization for the given holder.
* @param resourceHolder the ResourceHolder to manage
* @param resourceKey the key to bind the ResourceHolder for
* @see TransactionSynchronizationManager#bindResource
*/
public ResourceHolderSynchronization(H resourceHolder, K resourceKey) {
this.resourceHolder = resourceHolder;
this.resourceKey = resourceKey;
}
public void suspend() {
if (this.holderActive) {
TransactionSynchronizationManager.unbindResource(this.resourceKey);
}
}
public void resume() {
if (this.holderActive) {
TransactionSynchronizationManager.bindResource(this.resourceKey, this.resourceHolder);
}
}
public void flush() {
flushResource(this.resourceHolder);
}
public void beforeCommit(boolean readOnly) {
}
public void beforeCompletion() {
if (shouldUnbindAtCompletion()) {
TransactionSynchronizationManager.unbindResource(this.resourceKey);
this.holderActive = false;
if (shouldReleaseBeforeCompletion()) {
releaseResource(this.resourceHolder, this.resourceKey);
}
}
}
public void afterCommit() {
if (!shouldReleaseBeforeCompletion()) {
processResourceAfterCommit(this.resourceHolder);
}
}
public void afterCompletion(int status) {
if (shouldUnbindAtCompletion()) {
boolean releaseNecessary = false;
if (this.holderActive) {
// The thread-bound resource holder might not be available anymore,
// since afterCompletion might get called from a different thread.
this.holderActive = false;
TransactionSynchronizationManager.unbindResourceIfPossible(this.resourceKey);
this.resourceHolder.unbound();
releaseNecessary = true;
}
else {
releaseNecessary = !shouldReleaseBeforeCompletion();
}
if (releaseNecessary) {
releaseResource(this.resourceHolder, this.resourceKey);
}
}
else {
// Probably a pre-bound resource...
cleanupResource(this.resourceHolder, this.resourceKey, (status == STATUS_COMMITTED));
}
this.resourceHolder.reset();
}
/**
* Return whether this holder should be unbound at completion
* (or should rather be left bound to the thread after the transaction).
* <p>The default implementation returns <code>true</code>.
*/
protected boolean shouldUnbindAtCompletion() {
return true;
}
/**
* Return whether this holder's resource should be released before
* transaction completion (<code>true</code>) or rather after
* transaction completion (<code>false</code>).
* <p>Note that resources will only be released when they are
* unbound from the thread ({@link #shouldUnbindAtCompletion()}).
* <p>The default implementation returns <code>true</code>.
* @see #releaseResource
*/
protected boolean shouldReleaseBeforeCompletion() {
return true;
}
/**
* Flush callback for the given resource holder.
* @param resourceHolder the resource holder to flush
*/
protected void flushResource(H resourceHolder) {
}
/**
* After-commit callback for the given resource holder.
* Only called when the resource hasn't been released yet
* ({@link #shouldReleaseBeforeCompletion()}).
* @param resourceHolder the resource holder to process
*/
protected void processResourceAfterCommit(H resourceHolder) {
}
/**
* Release the given resource (after it has been unbound from the thread).
* @param resourceHolder the resource holder to process
* @param resourceKey the key that the ResourceHolder was bound for
*/
protected void releaseResource(H resourceHolder, K resourceKey) {
}
/**
* Perform a cleanup on the given resource (which is left bound to the thread).
* @param resourceHolder the resource holder to process
* @param resourceKey the key that the ResourceHolder was bound for
* @param committed whether the transaction has committed (<code>true</code>)
* or rolled back (<code>false</code>)
*/
protected void cleanupResource(H resourceHolder, K resourceKey, boolean committed) {
}
}
|