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}