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