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}