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  
17  package net.sf.ehcache.transaction.xa;
18  
19  import static org.mockito.Mockito.mock;
20  import static org.mockito.Mockito.when;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  import java.util.UUID;
29  import java.util.concurrent.ConcurrentHashMap;
30  import java.util.concurrent.ConcurrentMap;
31  
32  import javax.transaction.HeuristicMixedException;
33  import javax.transaction.HeuristicRollbackException;
34  import javax.transaction.InvalidTransactionException;
35  import javax.transaction.NotSupportedException;
36  import javax.transaction.RollbackException;
37  import javax.transaction.Synchronization;
38  import javax.transaction.SystemException;
39  import javax.transaction.Transaction;
40  import javax.transaction.TransactionManager;
41  import javax.transaction.xa.XAException;
42  import javax.transaction.xa.XAResource;
43  import javax.transaction.xa.Xid;
44  
45  import junit.framework.TestCase;
46  import net.sf.ehcache.CacheException;
47  import net.sf.ehcache.Ehcache;
48  import net.sf.ehcache.Element;
49  import net.sf.ehcache.Status;
50  import net.sf.ehcache.concurrent.StripedReadWriteLockSync;
51  import net.sf.ehcache.store.AbstractStore;
52  import net.sf.ehcache.store.ElementValueComparator;
53  import net.sf.ehcache.store.LruPolicy;
54  import net.sf.ehcache.store.Policy;
55  import net.sf.ehcache.transaction.StorePutCommand;
56  import net.sf.ehcache.transaction.TransactionContext;
57  import net.sf.ehcache.writer.CacheWriterManager;
58  
59  /***
60   *
61   * @author nelrahma
62   *
63   */
64  public class EhcacheXAResourceTest extends TestCase {
65  
66      public void test() throws XAException, SystemException, RollbackException {
67          TestStore underlyingStore = new TestStore();
68          TestStore oldVersionStore = new TestStore();
69          TestTransactionManager txnManager = new TestTransactionManager();
70          EhcacheXAStoreImpl store = new EhcacheXAStoreImpl(underlyingStore, oldVersionStore, false);
71          Ehcache theCache = mock(Ehcache.class);
72          when(theCache.getName()).thenReturn("testCache");
73          EhcacheXAResourceImpl resource = new EhcacheXAResourceImpl(theCache, txnManager, store);
74          TestXid xid1 = new TestXid(1L);
75  
76          TestTransaction testTxn = new TestTransaction();
77          txnManager.txn = testTxn;
78  
79          resource.start(xid1, XAResource.TMNOFLAGS);
80  
81          resource.start(xid1, XAResource.TMJOIN);
82  
83          resource.end(xid1, XAResource.TMSUCCESS);
84  
85          resource.end(xid1, XAResource.TMFAIL);
86  
87          testTxn = new TestTransaction();
88          txnManager.txn = testTxn;
89  
90          resource.start(xid1, XAResource.TMNOFLAGS);
91  
92          resource.end(xid1, XAResource.TMSUCCESS);
93  
94          resource.start(xid1, XAResource.TMNOFLAGS);
95  
96          TransactionContext context = resource.createTransactionContext();
97  
98          assertEquals(1, store.transactionContextXids.size());
99  
100         Element element = new Element("key1", "value1");
101 
102         context.addCommand(new StorePutCommand(element), element);
103         assertEquals(1, testTxn.resources.size());
104         assertEquals(0, underlyingStore.getSize());
105         assertEquals(0, oldVersionStore.getSize());
106         assertEquals(1, context.getCommands().size());
107 
108         resource.prepare(xid1);
109 
110         assertEquals(1, underlyingStore.getSize());
111         assertEquals(0, oldVersionStore.getSize());
112         assertEquals(1, store.prepareXids.size());
113 
114         Xid [] recoverXids = resource.recover(XAResource.TMSUCCESS);
115 
116         assertEquals(1, recoverXids.length);
117         try {
118             resource.commit(xid1, false);
119         } catch (XAException e) {
120             assertTrue(e.getMessage().contains("has been heuristically rolled back"));
121             resource.rollback(xid1);
122         }
123 
124         assertEquals(1, underlyingStore.getSize());
125         assertEquals(0, oldVersionStore.getSize());
126         assertEquals(0, store.transactionContextXids.size());
127         assertEquals(0, store.prepareXids.size());
128 
129     }
130 
131     public static final class TestXid implements Xid {
132         private final Long id;
133         private final byte[] globalTransactionId;
134         private final byte[] branchQualifier;
135         private final int formatId;
136 
137         public TestXid(Long id) {
138             this.id = id;
139             this.globalTransactionId = new byte[] { id.byteValue() };
140             this.branchQualifier = new byte[] { 0, 1, 1 };
141             this.formatId = 2;
142         }
143 
144         public byte[] getBranchQualifier() {
145             return this.branchQualifier;
146         }
147 
148         public int getFormatId() {
149             return formatId;
150         }
151 
152         public byte[] getGlobalTransactionId() {
153             return globalTransactionId;
154         }
155 
156         @Override
157         public int hashCode() {
158             int result = formatId;
159             result = 31 * result + (globalTransactionId != null ? Arrays.hashCode(globalTransactionId) : 0);
160             result = 31 * result + (branchQualifier != null ? Arrays.hashCode(branchQualifier) : 0);
161             return result;
162         }
163 
164         @Override
165         public String toString() {
166             return "XidClustered{" + "formatId=" + formatId + ", globalTxId=" + Arrays.toString(globalTransactionId) + ", branchQualifier="
167                     + Arrays.toString(branchQualifier) + '}';
168         }
169     }
170 
171     // Test Transaction
172     private static final class TestTransaction implements Transaction {
173 
174         private int hashCode = UUID.randomUUID().hashCode();
175 
176         Set<XAResource> resources = new HashSet();
177 
178         @Override
179         public int hashCode() {
180             return hashCode;
181         }
182 
183         public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException,
184                 SystemException {
185             // TODO Auto-generated method stub
186 
187         }
188 
189         public boolean delistResource(XAResource xaresource, int i) throws IllegalStateException, SystemException {
190             // TODO Auto-generated method stub
191             return false;
192         }
193 
194         public boolean enlistResource(XAResource xaresource) throws IllegalStateException, RollbackException, SystemException {
195             return resources.add(xaresource);
196         }
197 
198         public int getStatus() throws SystemException {
199             // TODO Auto-generated method stub
200             return 0;
201         }
202 
203         public void registerSynchronization(Synchronization synchronization) throws IllegalStateException, RollbackException,
204                 SystemException {
205             // TODO Auto-generated method stub
206 
207         }
208 
209         public void rollback() throws IllegalStateException, SystemException {
210             // TODO Auto-generated method stub
211 
212         }
213 
214         public void setRollbackOnly() throws IllegalStateException, SystemException {
215             // TODO Auto-generated method stub
216 
217         }
218 
219     }
220 
221     // Test Transaction Manager
222     private static final class TestTransactionManager implements TransactionManager {
223 
224         public TestTransaction txn;
225 
226         public void begin() throws NotSupportedException, SystemException {
227             throw new UnsupportedOperationException("method not supported for this unit test");
228         }
229 
230         public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, RollbackException,
231                 SecurityException, SystemException {
232             throw new UnsupportedOperationException("method not supported for this unit test");
233         }
234 
235         public int getStatus() throws SystemException {
236             throw new UnsupportedOperationException("method not supported for this unit test");
237         }
238 
239         public Transaction getTransaction() throws SystemException {
240             return txn;
241         }
242 
243         public void resume(Transaction arg0) throws IllegalStateException, InvalidTransactionException, SystemException {
244             throw new UnsupportedOperationException("method not supported for this unit test");
245         }
246 
247         public void rollback() throws IllegalStateException, SecurityException, SystemException {
248             throw new UnsupportedOperationException("method not supported for this unit test");
249         }
250 
251         public void setRollbackOnly() throws IllegalStateException, SystemException {
252             throw new UnsupportedOperationException("method not supported for this unit test");
253         }
254 
255         public void setTransactionTimeout(int arg0) throws SystemException {
256             throw new UnsupportedOperationException("method not supported for this unit test");
257         }
258 
259         public Transaction suspend() throws SystemException {
260             throw new UnsupportedOperationException("method not supported for this unit test");
261         }
262     }
263 
264     private static class TestStore extends AbstractStore {
265 
266         final static LruPolicy LRU_POLICY = new LruPolicy();
267         final ConcurrentMap<Object, Element> storeMap = new ConcurrentHashMap<Object, Element>();
268         final StripedReadWriteLockSync syncs = new StripedReadWriteLockSync(2);
269 
270         public boolean bufferFull() {
271             return false;
272         }
273 
274         public boolean containsKey(Object key) {
275             return storeMap.containsKey(key);
276         }
277 
278         public void dispose() {
279             //
280         }
281 
282         public void expireElements() {
283             //
284         }
285 
286         @SuppressWarnings("all")
287         public void flush() throws IOException {
288             if (false) {
289                 throw new IOException();
290             }
291         }
292 
293         public Element get(Object key) {
294             return storeMap.get(key);
295         }
296 
297         public Object getInternalContext() {
298             return syncs;
299         }
300 
301         public List getKeys() {
302             return new ArrayList(storeMap.keySet());
303         }
304 
305         public Element getQuiet(Object key) {
306             return storeMap.get(key);
307         }
308 
309         public int getSize() {
310             return storeMap.size();
311         }
312 
313         public long getSizeInBytes() {
314             return storeMap.size();
315         }
316 
317         public Status getStatus() {
318             return null;
319         }
320 
321         public int getTerracottaClusteredSize() {
322             return storeMap.size();
323         }
324 
325         public boolean put(Element element) throws CacheException {
326             if (element == null) {
327                 return false;
328             } else {
329                 return storeMap.put(element.getObjectKey(), element) == null;
330             }
331         }
332 
333         public Element remove(Object key) {
334             return storeMap.remove(key);
335         }
336 
337         public void removeAll() throws CacheException {
338             storeMap.clear();
339         }
340 
341         public boolean putWithWriter(Element element, CacheWriterManager writerManager) throws CacheException {
342             return true;
343         }
344 
345         public Element removeWithWriter(Object key, CacheWriterManager writerManager) throws CacheException {
346             return null;
347         }
348 
349         @Override
350         public void setNodeCoherent(boolean coherent) throws UnsupportedOperationException {
351             //
352         }
353 
354         @Override
355         public void waitUntilClusterCoherent() throws UnsupportedOperationException {
356             //
357         }
358 
359         public boolean containsKeyInMemory(Object key) {
360             return containsKey(key);
361         }
362 
363         public boolean containsKeyOnDisk(Object key) {
364             return false;
365         }
366 
367         public Policy getInMemoryEvictionPolicy() {
368             return LRU_POLICY;
369         }
370 
371         public int getInMemorySize() {
372             return getSize();
373         }
374 
375         public long getInMemorySizeInBytes() {
376             return getSizeInBytes();
377         }
378 
379         public int getOnDiskSize() {
380             return 0;
381         }
382 
383         public long getOnDiskSizeInBytes() {
384             return 0;
385         }
386 
387         public void setInMemoryEvictionPolicy(Policy policy) {
388             //
389         }
390 
391         public Element putIfAbsent(Element element) throws NullPointerException {
392             throw new UnsupportedOperationException();
393         }
394 
395         public Element removeElement(Element element, ElementValueComparator comparator) throws NullPointerException {
396             throw new UnsupportedOperationException();
397         }
398 
399         public boolean replace(Element old, Element element, ElementValueComparator comparator) throws NullPointerException, IllegalArgumentException {
400             throw new UnsupportedOperationException();
401         }
402 
403         public Element replace(Element element) throws NullPointerException {
404             throw new UnsupportedOperationException();
405         }
406 
407         public int getOffHeapSize() {
408             return 0;
409         }
410 
411         public long getOffHeapSizeInBytes() {
412             return 0;
413         }
414 
415         public boolean containsKeyOffHeap(Object key) {
416             return false;
417         }
418 
419         public Object getMBean() {
420             return null;
421         }
422     }
423 }