1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.ehcache.transaction;
17
18 import net.sf.ehcache.CacheManager;
19 import net.sf.ehcache.Element;
20
21 import java.io.ObjectStreamException;
22 import java.io.Serializable;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.locks.ReentrantLock;
25 import java.util.concurrent.locks.ReentrantReadWriteLock;
26
27 /***
28 * A SoftLock implementation with Read-Committed isolation level
29 *
30 * @author Ludovic Orban
31 */
32 public class ReadCommittedSoftLockImpl implements SoftLock {
33 private static final int PRIME = 31;
34
35 private final ReadCommittedSoftLockFactoryImpl factory;
36 private final ReentrantLock lock;
37 private final ReentrantReadWriteLock freezeLock;
38
39 private final String cacheManagerName;
40 private final String cacheName;
41 private final TransactionID transactionID;
42 private final Object key;
43 private Element newElement;
44 private final Element oldElement;
45 private volatile boolean expired;
46
47 /***
48 * Create a new ReadCommittedSoftLockImpl instance
49 * @param factory the creating factory
50 * @param transactionID the transaction ID
51 * @param key the element's key this soft lock is going to protect
52 * @param newElement the new element, can be null
53 * @param oldElement the old element, can be null
54 */
55 ReadCommittedSoftLockImpl(ReadCommittedSoftLockFactoryImpl factory, TransactionID transactionID, Object key,
56 Element newElement, Element oldElement) {
57 this.factory = factory;
58 this.cacheManagerName = factory.getCacheManagerName();
59 this.cacheName = factory.getCacheName();
60 this.transactionID = transactionID;
61 this.key = key;
62 this.newElement = newElement;
63 this.oldElement = oldElement;
64 this.lock = new ReentrantLock();
65 this.freezeLock = new ReentrantReadWriteLock();
66 }
67
68 /***
69 * {@inheritDoc}
70 */
71 public Object getKey() {
72 return key;
73 }
74
75 /***
76 * {@inheritDoc}
77 */
78 public Element getElement(TransactionID currentTransactionId) {
79 freezeLock.readLock().lock();
80 try {
81 if (transactionID.equals(currentTransactionId)) {
82 return newElement;
83 } else {
84 return oldElement;
85 }
86 } finally {
87 freezeLock.readLock().unlock();
88 }
89 }
90
91 /***
92 * {@inheritDoc}
93 */
94 public Element updateElement(Element newElement) {
95 Element e = this.newElement;
96 this.newElement = newElement;
97 return e;
98 }
99
100 /***
101 * {@inheritDoc}
102 */
103 public TransactionID getTransactionID() {
104 return transactionID;
105 }
106
107 /***
108 * {@inheritDoc}
109 */
110 public void lock() {
111 lock.lock();
112 }
113
114 /***
115 * {@inheritDoc}
116 */
117 public boolean tryLock(long ms) throws InterruptedException {
118 return lock.tryLock(ms, TimeUnit.MILLISECONDS);
119 }
120
121 /***
122 * {@inheritDoc}
123 */
124 public void clearTryLock() {
125 lock.unlock();
126 }
127
128 /***
129 * {@inheritDoc}
130 */
131 public void unlock() {
132 lock.unlock();
133 clear();
134 }
135
136 private boolean isLocked() {
137 return lock.isLocked();
138 }
139
140 /***
141 * {@inheritDoc}
142 */
143 public void freeze() {
144 if (!isLocked()) {
145 throw new IllegalStateException("cannot freeze an unlocked soft lock");
146 }
147 freezeLock.writeLock().lock();
148 }
149
150 /***
151 * {@inheritDoc}
152 */
153 public Element getFrozenElement() {
154 if (!isFrozen()) {
155 throw new IllegalStateException("cannot get frozen element of a soft lock which hasn't been frozen or hasn't expired");
156 }
157 if (transactionID.isDecisionCommit()) {
158 return newElement;
159 } else {
160 return oldElement;
161 }
162 }
163
164 /***
165 * {@inheritDoc}
166 */
167 public void unfreeze() {
168 freezeLock.writeLock().unlock();
169 }
170
171 private boolean isFrozen() {
172 return freezeLock.isWriteLocked();
173 }
174
175 /***
176 * {@inheritDoc}
177 */
178 public boolean isExpired() {
179 if (!expired) {
180 expired = !isFrozen() && !isLocked();
181 }
182 return expired;
183 }
184
185 private void clear() {
186 factory.clearSoftLock(this);
187 }
188
189 /***
190 * {@inheritDoc}
191 */
192 @Override
193 public boolean equals(Object object) {
194 if (object instanceof ReadCommittedSoftLockImpl) {
195 ReadCommittedSoftLockImpl other = (ReadCommittedSoftLockImpl) object;
196
197 if (!transactionID.equals(other.transactionID)) {
198 return false;
199 }
200
201 if (!key.equals(other.key)) {
202 return false;
203 }
204
205 return true;
206 }
207 return false;
208 }
209
210 /***
211 * {@inheritDoc}
212 */
213 @Override
214 public int hashCode() {
215 int hashCode = PRIME;
216
217 hashCode *= transactionID.hashCode();
218 hashCode *= key.hashCode();
219
220 return hashCode;
221 }
222
223 private Object writeReplace() throws ObjectStreamException {
224 return new ReadCommittedSoftLockImplSerializedForm(cacheManagerName, cacheName, transactionID, key);
225 }
226
227 /***
228 * {@inheritDoc}
229 */
230 @Override
231 public String toString() {
232 return "Soft Lock [clustered: false, isolation: rc, transactionID: " + transactionID + ", key: " + key +
233 ", newElement: " + newElement + ", oldElement: " + oldElement + "]";
234 }
235
236 /***
237 * ReadCommittedSoftLockImpl serialized form
238 */
239 private static final class ReadCommittedSoftLockImplSerializedForm implements Serializable {
240
241 private final String cacheManagerName;
242 private final String cacheName;
243 private final TransactionID transactionID;
244 private final Object key;
245
246 private ReadCommittedSoftLockImplSerializedForm(String cacheManagerName, String cacheName, TransactionID transactionID, Object key) {
247 this.cacheManagerName = cacheManagerName;
248 this.cacheName = cacheName;
249 this.transactionID = transactionID;
250 this.key = key;
251 }
252
253 private Object readResolve() throws ObjectStreamException {
254 for (int i = 0; i < CacheManager.ALL_CACHE_MANAGERS.size(); i++) {
255 CacheManager cacheManager = CacheManager.ALL_CACHE_MANAGERS.get(i);
256 if (cacheManager.getName().equals(cacheManagerName)) {
257 ReadCommittedSoftLockFactoryImpl softLockFactory = (ReadCommittedSoftLockFactoryImpl)cacheManager.getSoftLockFactory(cacheName);
258 return softLockFactory.getLock(transactionID, key);
259 }
260 }
261 throw new TransactionException("unable to find referent SoftLock in " + cacheManagerName + " " + cacheName +
262 " for key [" + key + "] under transaction " + transactionID);
263 }
264
265 }
266
267 }