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
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
186
187 }
188
189 public boolean delistResource(XAResource xaresource, int i) throws IllegalStateException, SystemException {
190
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
200 return 0;
201 }
202
203 public void registerSynchronization(Synchronization synchronization) throws IllegalStateException, RollbackException,
204 SystemException {
205
206
207 }
208
209 public void rollback() throws IllegalStateException, SystemException {
210
211
212 }
213
214 public void setRollbackOnly() throws IllegalStateException, SystemException {
215
216
217 }
218
219 }
220
221
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 }