001/*
002 * Copyright 2002-2019 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 org.springframework.lang.Nullable;
020import org.springframework.transaction.NestedTransactionNotSupportedException;
021import org.springframework.transaction.SavepointManager;
022import org.springframework.util.Assert;
023
024/**
025 * Default implementation of the {@link org.springframework.transaction.TransactionStatus}
026 * interface, used by {@link AbstractPlatformTransactionManager}. Based on the concept
027 * of an underlying "transaction object".
028 *
029 * <p>Holds all status information that {@link AbstractPlatformTransactionManager}
030 * needs internally, including a generic transaction object determined by the
031 * concrete transaction manager implementation.
032 *
033 * <p>Supports delegating savepoint-related methods to a transaction object
034 * that implements the {@link SavepointManager} interface.
035 *
036 * <p><b>NOTE:</b> This is <i>not</i> intended for use with other PlatformTransactionManager
037 * implementations, in particular not for mock transaction managers in testing environments.
038 * Use the alternative {@link SimpleTransactionStatus} class or a mock for the plain
039 * {@link org.springframework.transaction.TransactionStatus} interface instead.
040 *
041 * @author Juergen Hoeller
042 * @since 19.01.2004
043 * @see AbstractPlatformTransactionManager
044 * @see org.springframework.transaction.SavepointManager
045 * @see #getTransaction
046 * @see #createSavepoint
047 * @see #rollbackToSavepoint
048 * @see #releaseSavepoint
049 * @see SimpleTransactionStatus
050 */
051public class DefaultTransactionStatus extends AbstractTransactionStatus {
052
053        @Nullable
054        private final Object transaction;
055
056        private final boolean newTransaction;
057
058        private final boolean newSynchronization;
059
060        private final boolean readOnly;
061
062        private final boolean debug;
063
064        @Nullable
065        private final Object suspendedResources;
066
067
068        /**
069         * Create a new {@code DefaultTransactionStatus} instance.
070         * @param transaction underlying transaction object that can hold state
071         * for the internal transaction implementation
072         * @param newTransaction if the transaction is new, otherwise participating
073         * in an existing transaction
074         * @param newSynchronization if a new transaction synchronization has been
075         * opened for the given transaction
076         * @param readOnly whether the transaction is marked as read-only
077         * @param debug should debug logging be enabled for the handling of this transaction?
078         * Caching it in here can prevent repeated calls to ask the logging system whether
079         * debug logging should be enabled.
080         * @param suspendedResources a holder for resources that have been suspended
081         * for this transaction, if any
082         */
083        public DefaultTransactionStatus(
084                        @Nullable Object transaction, boolean newTransaction, boolean newSynchronization,
085                        boolean readOnly, boolean debug, @Nullable Object suspendedResources) {
086
087                this.transaction = transaction;
088                this.newTransaction = newTransaction;
089                this.newSynchronization = newSynchronization;
090                this.readOnly = readOnly;
091                this.debug = debug;
092                this.suspendedResources = suspendedResources;
093        }
094
095
096        /**
097         * Return the underlying transaction object.
098         * @throws IllegalStateException if no transaction is active
099         */
100        public Object getTransaction() {
101                Assert.state(this.transaction != null, "No transaction active");
102                return this.transaction;
103        }
104
105        /**
106         * Return whether there is an actual transaction active.
107         */
108        public boolean hasTransaction() {
109                return (this.transaction != null);
110        }
111
112        @Override
113        public boolean isNewTransaction() {
114                return (hasTransaction() && this.newTransaction);
115        }
116
117        /**
118         * Return if a new transaction synchronization has been opened
119         * for this transaction.
120         */
121        public boolean isNewSynchronization() {
122                return this.newSynchronization;
123        }
124
125        /**
126         * Return if this transaction is defined as read-only transaction.
127         */
128        public boolean isReadOnly() {
129                return this.readOnly;
130        }
131
132        /**
133         * Return whether the progress of this transaction is debugged. This is used by
134         * {@link AbstractPlatformTransactionManager} as an optimization, to prevent repeated
135         * calls to {@code logger.isDebugEnabled()}. Not really intended for client code.
136         */
137        public boolean isDebug() {
138                return this.debug;
139        }
140
141        /**
142         * Return the holder for resources that have been suspended for this transaction,
143         * if any.
144         */
145        @Nullable
146        public Object getSuspendedResources() {
147                return this.suspendedResources;
148        }
149
150
151        //---------------------------------------------------------------------
152        // Enable functionality through underlying transaction object
153        //---------------------------------------------------------------------
154
155        /**
156         * Determine the rollback-only flag via checking the transaction object, provided
157         * that the latter implements the {@link SmartTransactionObject} interface.
158         * <p>Will return {@code true} if the global transaction itself has been marked
159         * rollback-only by the transaction coordinator, for example in case of a timeout.
160         * @see SmartTransactionObject#isRollbackOnly()
161         */
162        @Override
163        public boolean isGlobalRollbackOnly() {
164                return ((this.transaction instanceof SmartTransactionObject) &&
165                                ((SmartTransactionObject) this.transaction).isRollbackOnly());
166        }
167
168        /**
169         * This implementation exposes the {@link SavepointManager} interface
170         * of the underlying transaction object, if any.
171         * @throws NestedTransactionNotSupportedException if savepoints are not supported
172         * @see #isTransactionSavepointManager()
173         */
174        @Override
175        protected SavepointManager getSavepointManager() {
176                Object transaction = this.transaction;
177                if (!(transaction instanceof SavepointManager)) {
178                        throw new NestedTransactionNotSupportedException(
179                                        "Transaction object [" + this.transaction + "] does not support savepoints");
180                }
181                return (SavepointManager) transaction;
182        }
183
184        /**
185         * Return whether the underlying transaction implements the {@link SavepointManager}
186         * interface and therefore supports savepoints.
187         * @see #getTransaction()
188         * @see #getSavepointManager()
189         */
190        public boolean isTransactionSavepointManager() {
191                return (this.transaction instanceof SavepointManager);
192        }
193
194        /**
195         * Delegate the flushing to the transaction object, provided that the latter
196         * implements the {@link SmartTransactionObject} interface.
197         * @see SmartTransactionObject#flush()
198         */
199        @Override
200        public void flush() {
201                if (this.transaction instanceof SmartTransactionObject) {
202                        ((SmartTransactionObject) this.transaction).flush();
203                }
204        }
205
206}