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