View Javadoc

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 }