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
019import java.util.Date;
020
021import org.springframework.lang.Nullable;
022import org.springframework.transaction.TransactionTimedOutException;
023
024/**
025 * Convenient base class for resource holders.
026 *
027 * <p>Features rollback-only support for participating transactions.
028 * Can expire after a certain number of seconds or milliseconds
029 * in order to determine a transactional timeout.
030 *
031 * @author Juergen Hoeller
032 * @since 02.02.2004
033 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
034 * @see org.springframework.jdbc.datasource.DataSourceUtils#applyTransactionTimeout
035 */
036public abstract class ResourceHolderSupport implements ResourceHolder {
037
038        private boolean synchronizedWithTransaction = false;
039
040        private boolean rollbackOnly = false;
041
042        @Nullable
043        private Date deadline;
044
045        private int referenceCount = 0;
046
047        private boolean isVoid = false;
048
049
050        /**
051         * Mark the resource as synchronized with a transaction.
052         */
053        public void setSynchronizedWithTransaction(boolean synchronizedWithTransaction) {
054                this.synchronizedWithTransaction = synchronizedWithTransaction;
055        }
056
057        /**
058         * Return whether the resource is synchronized with a transaction.
059         */
060        public boolean isSynchronizedWithTransaction() {
061                return this.synchronizedWithTransaction;
062        }
063
064        /**
065         * Mark the resource transaction as rollback-only.
066         */
067        public void setRollbackOnly() {
068                this.rollbackOnly = true;
069        }
070
071        /**
072         * Reset the rollback-only status for this resource transaction.
073         * <p>Only really intended to be called after custom rollback steps which
074         * keep the original resource in action, e.g. in case of a savepoint.
075         * @since 5.0
076         * @see org.springframework.transaction.SavepointManager#rollbackToSavepoint
077         */
078        public void resetRollbackOnly() {
079                this.rollbackOnly = false;
080        }
081
082        /**
083         * Return whether the resource transaction is marked as rollback-only.
084         */
085        public boolean isRollbackOnly() {
086                return this.rollbackOnly;
087        }
088
089        /**
090         * Set the timeout for this object in seconds.
091         * @param seconds number of seconds until expiration
092         */
093        public void setTimeoutInSeconds(int seconds) {
094                setTimeoutInMillis(seconds * 1000L);
095        }
096
097        /**
098         * Set the timeout for this object in milliseconds.
099         * @param millis number of milliseconds until expiration
100         */
101        public void setTimeoutInMillis(long millis) {
102                this.deadline = new Date(System.currentTimeMillis() + millis);
103        }
104
105        /**
106         * Return whether this object has an associated timeout.
107         */
108        public boolean hasTimeout() {
109                return (this.deadline != null);
110        }
111
112        /**
113         * Return the expiration deadline of this object.
114         * @return the deadline as Date object
115         */
116        @Nullable
117        public Date getDeadline() {
118                return this.deadline;
119        }
120
121        /**
122         * Return the time to live for this object in seconds.
123         * Rounds up eagerly, e.g. 9.00001 still to 10.
124         * @return number of seconds until expiration
125         * @throws TransactionTimedOutException if the deadline has already been reached
126         */
127        public int getTimeToLiveInSeconds() {
128                double diff = ((double) getTimeToLiveInMillis()) / 1000;
129                int secs = (int) Math.ceil(diff);
130                checkTransactionTimeout(secs <= 0);
131                return secs;
132        }
133
134        /**
135         * Return the time to live for this object in milliseconds.
136         * @return number of milliseconds until expiration
137         * @throws TransactionTimedOutException if the deadline has already been reached
138         */
139        public long getTimeToLiveInMillis() throws TransactionTimedOutException{
140                if (this.deadline == null) {
141                        throw new IllegalStateException("No timeout specified for this resource holder");
142                }
143                long timeToLive = this.deadline.getTime() - System.currentTimeMillis();
144                checkTransactionTimeout(timeToLive <= 0);
145                return timeToLive;
146        }
147
148        /**
149         * Set the transaction rollback-only if the deadline has been reached,
150         * and throw a TransactionTimedOutException.
151         */
152        private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
153                if (deadlineReached) {
154                        setRollbackOnly();
155                        throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
156                }
157        }
158
159        /**
160         * Increase the reference count by one because the holder has been requested
161         * (i.e. someone requested the resource held by it).
162         */
163        public void requested() {
164                this.referenceCount++;
165        }
166
167        /**
168         * Decrease the reference count by one because the holder has been released
169         * (i.e. someone released the resource held by it).
170         */
171        public void released() {
172                this.referenceCount--;
173        }
174
175        /**
176         * Return whether there are still open references to this holder.
177         */
178        public boolean isOpen() {
179                return (this.referenceCount > 0);
180        }
181
182        /**
183         * Clear the transactional state of this resource holder.
184         */
185        public void clear() {
186                this.synchronizedWithTransaction = false;
187                this.rollbackOnly = false;
188                this.deadline = null;
189        }
190
191        /**
192         * Reset this resource holder - transactional state as well as reference count.
193         */
194        @Override
195        public void reset() {
196                clear();
197                this.referenceCount = 0;
198        }
199
200        @Override
201        public void unbound() {
202                this.isVoid = true;
203        }
204
205        @Override
206        public boolean isVoid() {
207                return this.isVoid;
208        }
209
210}