1 package net.sf.ehcache.transaction;
2
3 import bitronix.tm.BitronixTransactionManager;
4 import bitronix.tm.TransactionManagerServices;
5 import bitronix.tm.internal.TransactionStatusChangeListener;
6 import net.sf.ehcache.Cache;
7 import net.sf.ehcache.CacheManager;
8 import net.sf.ehcache.Ehcache;
9 import net.sf.ehcache.Element;
10 import net.sf.ehcache.TransactionController;
11 import net.sf.ehcache.config.CacheConfiguration;
12 import net.sf.ehcache.config.Configuration;
13 import net.sf.ehcache.config.DiskStoreConfiguration;
14 import net.sf.ehcache.transaction.xa.EhcacheXAResourceImpl;
15 import net.sf.ehcache.transaction.xa.XATransactionStore;
16 import net.sf.ehcache.transaction.xa.XidTransactionID;
17 import net.sf.ehcache.util.RetryAssert;
18
19 import org.hamcrest.core.Is;
20 import org.junit.After;
21 import org.junit.Before;
22 import org.junit.Test;
23
24 import java.io.ByteArrayInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.lang.reflect.Field;
29 import java.util.concurrent.Callable;
30 import java.util.concurrent.TimeUnit;
31
32 import javax.transaction.Status;
33 import javax.transaction.SystemException;
34 import javax.transaction.xa.Xid;
35
36 import static junit.framework.Assert.assertNull;
37 import static junit.framework.Assert.assertTrue;
38 import static org.junit.Assert.assertEquals;
39
40 /***
41 * @author lorban
42 */
43 public class SoftLockPinningTest {
44
45 private CacheManager cacheManager;
46 private Ehcache cache1;
47 private Ehcache cache2;
48 private Ehcache xaCache1;
49 private Ehcache xaCache2;
50 private TransactionController transactionController;
51 private BitronixTransactionManager transactionManager;
52
53 @Before
54 public void setUp() throws Exception {
55 TransactionManagerServices.getConfiguration().setJournal("null").setServerId("SoftLockPinningTest");
56 transactionManager = TransactionManagerServices.getTransactionManager();
57
58 cacheManager = new CacheManager(new Configuration().diskStore(new DiskStoreConfiguration().path(System.getProperty("java.io.tmpdir")))
59 .cache(new CacheConfiguration().name("localTxCache1")
60 .maxEntriesLocalHeap(1)
61 .overflowToDisk(true)
62 .transactionalMode(CacheConfiguration.TransactionalMode.LOCAL)
63 )
64 .cache(new CacheConfiguration().name("localTxCache2")
65 .maxEntriesLocalHeap(1)
66 .overflowToDisk(true)
67 .transactionalMode(CacheConfiguration.TransactionalMode.LOCAL)
68 )
69 .cache(new CacheConfiguration().name("xaCache1")
70 .maxEntriesLocalHeap(1)
71 .overflowToDisk(true)
72 .transactionalMode(CacheConfiguration.TransactionalMode.XA_STRICT)
73 )
74 .cache(new CacheConfiguration().name("xaCache2")
75 .maxEntriesLocalHeap(1)
76 .overflowToDisk(true)
77 .transactionalMode(CacheConfiguration.TransactionalMode.XA_STRICT)
78 )
79 );
80
81 transactionController = cacheManager.getTransactionController();
82 transactionController.begin();
83 cache1 = cacheManager.getEhcache("localTxCache1");
84 cache1.removeAll();
85 cache2 = cacheManager.getEhcache("localTxCache2");
86 cache2.removeAll();
87 transactionController.commit();
88
89 transactionManager.begin();
90 xaCache1 = cacheManager.getEhcache("xaCache1");
91 xaCache1.removeAll();
92 xaCache2 = cacheManager.getEhcache("xaCache2");
93 xaCache2.removeAll();
94 transactionManager.commit();
95 }
96
97 @After
98 public void tearDown() throws Exception {
99 if (transactionController.getCurrentTransactionContext() != null) {
100 transactionController.rollback();
101 }
102 if (transactionManager.getStatus() != Status.STATUS_NO_TRANSACTION) {
103 transactionManager.rollback();
104 }
105 transactionManager.shutdown();
106 cacheManager.shutdown();
107 }
108
109 @Test
110 public void testDiskBackedCacheLocalTx() throws Exception {
111 transactionController.begin();
112
113 for (int i = 0; i < 100; i++) {
114 Element element1 = new Element(i, i);
115 element1.setTimeToIdle(1);
116 element1.setTimeToLive(1);
117 cache1.put(element1);
118
119 Element element2 = new Element(i, i);
120 element2.setTimeToIdle(1);
121 element2.setTimeToLive(1);
122 cache2.put(element2);
123 }
124
125 assertEquals(100, cache1.getMemoryStoreSize());
126 assertEquals(100, cache2.getMemoryStoreSize());
127 RetryAssert.assertBy(5, TimeUnit.SECONDS, new Callable<Integer>() {
128 public Integer call() throws Exception {
129 return cache1.getDiskStoreSize();
130 }
131 }, Is.is(100));
132 RetryAssert.assertBy(5, TimeUnit.SECONDS, new Callable<Integer>() {
133 public Integer call() throws Exception {
134 return cache2.getDiskStoreSize();
135 }
136 }, Is.is(100));
137
138
139 Thread.sleep(1999);
140
141 transactionController.commit();
142
143 transactionController.begin();
144 assertEquals(100, cache1.getSize());
145 assertEquals(100, cache2.getSize());
146 transactionController.commit();
147
148
149 Thread.sleep(1999);
150
151 transactionController.begin();
152 for (int i = 0; i < 100; i++) {
153 assertNull("cache1 key " + i, cache1.get(i));
154 assertNull("cache2 key " + i, cache2.get(i));
155 }
156 transactionController.commit();
157 }
158
159 @Test
160 public void testDiskBackedCacheXaStrictTx() throws Exception {
161 transactionManager.begin();
162
163 for (int i = 0; i < 100; i++) {
164 Element element1 = new Element(i, i);
165 element1.setTimeToIdle(1);
166 element1.setTimeToLive(1);
167 xaCache1.put(element1);
168
169 Element element2 = new Element(i, i);
170 element2.setTimeToIdle(1);
171 element2.setTimeToLive(1);
172 xaCache2.put(element2);
173 }
174
175
176
177
178 transactionManager.getCurrentTransaction().addTransactionStatusChangeListener(new TransactionStatusChangeListener() {
179 public void statusChanged(final int oldStatus, final int newStatus) {
180 if (oldStatus == Status.STATUS_PREPARED) {
181
182 assertEquals(100, xaCache1.getMemoryStoreSize());
183 assertEquals(100, xaCache2.getMemoryStoreSize());
184 RetryAssert.assertBy(5, TimeUnit.SECONDS, new Callable<Integer>() {
185 public Integer call() throws Exception {
186 return xaCache1.getDiskStoreSize();
187 }
188 }, Is.is(100));
189 RetryAssert.assertBy(5, TimeUnit.SECONDS, new Callable<Integer>() {
190 public Integer call() throws Exception {
191 return xaCache2.getDiskStoreSize();
192 }
193 }, Is.is(100));
194
195 }
196 }
197 });
198
199
200 Thread.sleep(1999);
201
202 transactionManager.commit();
203
204 transactionManager.begin();
205 assertEquals(100, xaCache1.getSize());
206 assertEquals(100, xaCache2.getSize());
207 transactionManager.commit();
208
209
210 Thread.sleep(1999);
211
212 transactionManager.begin();
213 for (int i = 0; i < 100; i++) {
214 assertNull("xaCache1 key " + i, xaCache1.get(i));
215 assertNull("xaCache2 key " + i, xaCache2.get(i));
216 }
217 transactionManager.commit();
218 }
219
220 @Test
221 public void testSoftLockSerialization() throws Exception {
222 transactionController.begin();
223
224 TransactionID transactionId = transactionController.getCurrentTransactionContext().getTransactionId();
225 SoftLock originalSoftLock = cacheManager.getSoftLockFactory(cache1.getName()).createSoftLock(transactionId, -1, null, null);
226
227
228 ByteArrayOutputStream baos = new ByteArrayOutputStream();
229 ObjectOutputStream oos = new ObjectOutputStream(baos);
230 oos.writeObject(originalSoftLock);
231 oos.close();
232
233
234 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
235 SoftLock deserializedSoftLock = (SoftLock)ois.readObject();
236 ois.close();
237
238
239 assertTrue(originalSoftLock == deserializedSoftLock);
240
241 transactionController.commit();
242 }
243
244 @Test
245 public void testXaSoftLockSerialization() throws Exception {
246 transactionManager.begin();
247 XidTransactionID xidTransactionID = findXidTransactionId(xaCache1);
248
249 SoftLock originalSoftLock = cacheManager.getSoftLockFactory(xaCache1.getName()).createSoftLock(xidTransactionID, -1, null, null);
250
251
252 ByteArrayOutputStream baos = new ByteArrayOutputStream();
253 ObjectOutputStream oos = new ObjectOutputStream(baos);
254 oos.writeObject(originalSoftLock);
255 oos.close();
256
257
258 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
259 SoftLock deserializedSoftLock = (SoftLock)ois.readObject();
260 ois.close();
261
262
263 assertTrue(originalSoftLock == deserializedSoftLock);
264
265 transactionManager.commit();
266 }
267
268 private XidTransactionID findXidTransactionId(Ehcache xaCache) throws NoSuchFieldException, IllegalAccessException, SystemException {
269
270 xaCache.put(new Element(1, 1));
271
272 Field compoundStoreField = Cache.class.getDeclaredField("compoundStore");
273 compoundStoreField.setAccessible(true);
274 XATransactionStore xaTransactionStore = (XATransactionStore)compoundStoreField.get(xaCache);
275 EhcacheXAResourceImpl ehcacheXAResource = xaTransactionStore.getOrCreateXAResource();
276
277 Field currentXidField = EhcacheXAResourceImpl.class.getDeclaredField("currentXid");
278 currentXidField.setAccessible(true);
279 Xid currentXid = (Xid)currentXidField.get(ehcacheXAResource);
280
281 Field transactionIdFactoryField = XATransactionStore.class.getDeclaredField("transactionIdFactory");
282 transactionIdFactoryField.setAccessible(true);
283 TransactionIDFactory transactionIdFactory = (TransactionIDFactory)transactionIdFactoryField.get(xaTransactionStore);
284
285 return transactionIdFactory.createXidTransactionID(currentXid);
286 }
287
288 }