KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > transaction > interceptor > AbstractTransactionAspectTests


1 /*
2  * Copyright 2002-2005 the original author or authors.
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 org.springframework.transaction.interceptor;
18
19 import java.lang.reflect.Method JavaDoc;
20
21 import junit.framework.TestCase;
22 import org.easymock.MockControl;
23
24 import org.springframework.beans.ITestBean;
25 import org.springframework.beans.TestBean;
26 import org.springframework.transaction.CannotCreateTransactionException;
27 import org.springframework.transaction.NoTransactionException;
28 import org.springframework.transaction.PlatformTransactionManager;
29 import org.springframework.transaction.TransactionDefinition;
30 import org.springframework.transaction.TransactionStatus;
31 import org.springframework.transaction.TransactionSystemException;
32 import org.springframework.transaction.UnexpectedRollbackException;
33 import org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo;
34 import org.springframework.transaction.support.DefaultTransactionStatus;
35
36 /**
37  * Mock object based tests for transaction aspects.
38  * True unit test in that it tests how the transaction aspect uses
39  * the PlatformTransactionManager helper, rather than indirectly
40  * testing the helper implementation.
41  *
42  * <ü>This is a superclass to allow testing both the AOP Alliance MethodInterceptor
43  * and the AspectJ aspect.
44  *
45  * @author Rod Johnson
46  * @since 16.03.2003
47  */

48 public abstract class AbstractTransactionAspectTests extends TestCase {
49     
50     protected Method JavaDoc exceptionalMethod;
51     
52     protected Method JavaDoc getNameMethod;
53     
54     protected Method JavaDoc setNameMethod;
55     
56     public AbstractTransactionAspectTests() {
57         try {
58             // Cache the methods we'll be testing
59
exceptionalMethod = ITestBean.class.getMethod("exceptional", new Class JavaDoc[] { Throwable JavaDoc.class });
60             getNameMethod = ITestBean.class.getMethod("getName", (Class JavaDoc[]) null);
61             setNameMethod = ITestBean.class.getMethod("setName", new Class JavaDoc[] { String JavaDoc.class} );
62         }
63         catch (NoSuchMethodException JavaDoc ex) {
64             throw new RuntimeException JavaDoc("Shouldn't happen", ex);
65         }
66     }
67     
68     /**
69      * Subclasses must implement this to create an advised object based on the
70      * given target. In the case of AspectJ, the advised object will already
71      * have been created, as there's no distinction between target and proxy.
72      * In the case of Spring's own AOP framework, a proxy must be created
73      * using a suitably configured transaction interceptor
74      * @param target target if there's a distinct target. If not (AspectJ),
75      * return target.
76      * @return transactional advised object
77      */

78     protected abstract Object JavaDoc advised(
79             Object JavaDoc target, PlatformTransactionManager ptm, TransactionAttributeSource tas) throws Exception JavaDoc;
80
81     public void testNoTransaction() throws Exception JavaDoc {
82         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
83         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
84
85         // expect no calls
86
ptxControl.replay();
87
88         TestBean tb = new TestBean();
89         TransactionAttributeSource tas = new MapTransactionAttributeSource();
90
91         // All the methods in this class use the advised() template method
92
// to obtain a transaction object, configured with the given PlatformTransactionManager
93
// and transaction attribute source
94
ITestBean itb = (ITestBean) advised(tb, ptm, tas);
95
96         checkTransactionStatus(false);
97         itb.getName();
98         checkTransactionStatus(false);
99
100         ptxControl.verify();
101     }
102
103     /**
104      * Check that a transaction is created and committed.
105      */

106     public void testTransactionShouldSucceed() throws Exception JavaDoc {
107         TransactionAttribute txatt = new DefaultTransactionAttribute();
108
109         Method JavaDoc m = getNameMethod;
110         MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
111         tas.register(m, txatt);
112
113         TransactionStatus status = transactionStatusForNewTransaction();
114         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
115         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
116         // expect a transaction
117
ptm.getTransaction(txatt);
118         ptxControl.setReturnValue(status, 1);
119         ptm.commit(status);
120         ptxControl.setVoidCallable(1);
121         ptxControl.replay();
122
123         TestBean tb = new TestBean();
124         
125         ITestBean itb = (ITestBean) advised(tb, ptm, tas);
126
127         checkTransactionStatus(false);
128         // verification!?
129
itb.getName();
130         checkTransactionStatus(false);
131
132         ptxControl.verify();
133     }
134     
135     /**
136      * Check that a transaction is created and committed.
137      */

138     public void testTransactionShouldSucceedWithNotNew() throws Exception JavaDoc {
139         TransactionAttribute txatt = new DefaultTransactionAttribute();
140
141         Method JavaDoc m = getNameMethod;
142         MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
143         tas.register(m, txatt);
144
145         TransactionStatus status = new DefaultTransactionStatus(new Object JavaDoc(), false, false, false, false, null);
146         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
147         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
148         // expect a transaction
149
ptm.getTransaction(txatt);
150         ptxControl.setReturnValue(status, 1);
151         ptm.commit(status);
152         ptxControl.setVoidCallable(1);
153         ptxControl.replay();
154
155         TestBean tb = new TestBean();
156
157         ITestBean itb = (ITestBean) advised(tb, ptm, tas);
158
159         checkTransactionStatus(false);
160         // verification!?
161
itb.getName();
162         checkTransactionStatus(false);
163
164         ptxControl.verify();
165     }
166
167     public void testEnclosingTransactionWithNonTransactionMethodOnAdvisedInside() throws Throwable JavaDoc {
168         TransactionAttribute txatt = new DefaultTransactionAttribute();
169
170         Method JavaDoc m = exceptionalMethod;
171         MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
172         tas.register(m, txatt);
173
174         TransactionStatus status = transactionStatusForNewTransaction();
175         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
176         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
177         // Expect a transaction
178
ptm.getTransaction(txatt);
179         ptxControl.setReturnValue(status, 1);
180         ptm.commit(status);
181         ptxControl.setVoidCallable(1);
182         ptxControl.replay();
183         
184         final String JavaDoc spouseName = "innerName";
185
186         TestBean outer = new TestBean() {
187             public void exceptional(Throwable JavaDoc t) throws Throwable JavaDoc {
188                 TransactionInfo ti = TransactionAspectSupport.currentTransactionInfo();
189                 assertTrue(ti.hasTransaction());
190                 assertEquals(spouseName, getSpouse().getName());
191             }
192         };
193         TestBean inner = new TestBean() {
194             public String JavaDoc getName() {
195                 // Assert that we're in the inner proxy
196
TransactionInfo ti = TransactionAspectSupport.currentTransactionInfo();
197                 assertFalse(ti.hasTransaction());
198                 return spouseName;
199             }
200         };
201         
202         ITestBean outerProxy = (ITestBean) advised(outer, ptm, tas);
203         ITestBean innerProxy = (ITestBean) advised(inner, ptm, tas);
204         outer.setSpouse(innerProxy);
205
206         checkTransactionStatus(false);
207
208         // Will invoke inner.getName, which is non-transactional
209
outerProxy.exceptional(null);
210         
211         checkTransactionStatus(false);
212
213         ptxControl.verify();
214     }
215     
216     public void testEnclosingTransactionWithNestedTransactionOnAdvisedInside() throws Throwable JavaDoc {
217         final TransactionAttribute outerTxatt = new DefaultTransactionAttribute();
218         final TransactionAttribute innerTxatt = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_NESTED);
219
220         Method JavaDoc outerMethod = exceptionalMethod;
221         Method JavaDoc innerMethod = getNameMethod;
222         MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
223         tas.register(outerMethod, outerTxatt);
224         tas.register(innerMethod, innerTxatt);
225
226         TransactionStatus outerStatus = transactionStatusForNewTransaction();
227         TransactionStatus innerStatus = transactionStatusForNewTransaction();
228         
229         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
230         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
231         // Expect a transaction
232
ptm.getTransaction(outerTxatt);
233         ptxControl.setReturnValue(outerStatus, 1);
234         
235         ptm.getTransaction(innerTxatt);
236         ptxControl.setReturnValue(innerStatus, 1);
237         
238         ptm.commit(innerStatus);
239         ptxControl.setVoidCallable(1);
240         
241         ptm.commit(outerStatus);
242         ptxControl.setVoidCallable(1);
243         ptxControl.replay();
244         
245         final String JavaDoc spouseName = "innerName";
246
247         TestBean outer = new TestBean() {
248             public void exceptional(Throwable JavaDoc t) throws Throwable JavaDoc {
249                 TransactionInfo ti = TransactionAspectSupport.currentTransactionInfo();
250                 assertTrue(ti.hasTransaction());
251                 assertEquals(outerTxatt, ti.getTransactionAttribute());
252                 assertEquals(spouseName, getSpouse().getName());
253             }
254         };
255         TestBean inner = new TestBean() {
256             public String JavaDoc getName() {
257                 // Assert that we're in the inner proxy
258
TransactionInfo ti = TransactionAspectSupport.currentTransactionInfo();
259                 // Has nested transaction
260
assertTrue(ti.hasTransaction());
261                 assertEquals(innerTxatt, ti.getTransactionAttribute());
262                 return spouseName;
263             }
264         };
265         
266         ITestBean outerProxy = (ITestBean) advised(outer, ptm, tas);
267         ITestBean innerProxy = (ITestBean) advised(inner, ptm, tas);
268         outer.setSpouse(innerProxy);
269
270         checkTransactionStatus(false);
271
272         // Will invoke inner.getName, which is non-transactional
273
outerProxy.exceptional(null);
274         
275         checkTransactionStatus(false);
276
277         ptxControl.verify();
278     }
279     
280     public void testRollbackOnCheckedException() throws Throwable JavaDoc {
281         doTestRollbackOnException(new Exception JavaDoc(), true, false);
282     }
283
284     public void testNoRollbackOnCheckedException() throws Throwable JavaDoc {
285         doTestRollbackOnException(new Exception JavaDoc(), false, false);
286     }
287
288     public void testRollbackOnUncheckedException() throws Throwable JavaDoc {
289         doTestRollbackOnException(new RuntimeException JavaDoc(), true, false);
290     }
291
292     public void testNoRollbackOnUncheckedException() throws Throwable JavaDoc {
293         doTestRollbackOnException(new RuntimeException JavaDoc(), false, false);
294     }
295
296     public void testRollbackOnCheckedExceptionWithRollbackException() throws Throwable JavaDoc {
297         doTestRollbackOnException(new Exception JavaDoc(), true, true);
298     }
299
300     public void testNoRollbackOnCheckedExceptionWithRollbackException() throws Throwable JavaDoc {
301         doTestRollbackOnException(new Exception JavaDoc(), false, true);
302     }
303
304     public void testRollbackOnUncheckedExceptionWithRollbackException() throws Throwable JavaDoc {
305         doTestRollbackOnException(new RuntimeException JavaDoc(), true, true);
306     }
307
308     public void testNoRollbackOnUncheckedExceptionWithRollbackException() throws Throwable JavaDoc {
309         doTestRollbackOnException(new RuntimeException JavaDoc(), false, true);
310     }
311
312     /**
313      * Check that the given exception thrown by the target can produce the
314      * desired behavior with the appropriate transaction attribute.
315      * @param ex exception to be thrown by the target
316      * @param shouldRollback whether this should cause a transaction rollback
317      */

318     protected void doTestRollbackOnException(
319             final Exception JavaDoc ex, final boolean shouldRollback, boolean rollbackException) throws Exception JavaDoc {
320
321         TransactionAttribute txatt = new DefaultTransactionAttribute() {
322             public boolean rollbackOn(Throwable JavaDoc t) {
323                 assertTrue(t == ex);
324                 return shouldRollback;
325             }
326         };
327
328         Method JavaDoc m = exceptionalMethod;
329         MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
330         tas.register(m, txatt);
331
332         TransactionStatus status = new DefaultTransactionStatus(null, false, false, false, false, null);
333         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
334         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
335         // Gets additional call(s) from TransactionControl
336

337         ptm.getTransaction(txatt);
338         ptxControl.setReturnValue(status, 1);
339
340         if (shouldRollback) {
341             ptm.rollback(status);
342         }
343         else {
344             ptm.commit(status);
345         }
346         TransactionSystemException tex = new TransactionSystemException("system exception");
347         if (rollbackException) {
348             ptxControl.setThrowable(tex, 1);
349         }
350         else {
351             ptxControl.setVoidCallable(1);
352         }
353         ptxControl.replay();
354
355         TestBean tb = new TestBean();
356         ITestBean itb = (ITestBean) advised(tb, ptm, tas);
357
358         try {
359             itb.exceptional(ex);
360             fail("Should have thrown exception");
361         }
362         catch (Throwable JavaDoc t) {
363             if (rollbackException) {
364                 assertEquals("Caught wrong exception", tex, t );
365             }
366             else {
367                 assertEquals("Caught wrong exception", ex, t);
368             }
369         }
370
371         ptxControl.verify();
372     }
373     
374     /**
375      * Test that TransactionControl.setRollbackOnly works
376      * @throws java.lang.Exception
377      */

378     public void testProgrammaticRollback() throws Exception JavaDoc {
379         TransactionAttribute txatt = new DefaultTransactionAttribute();
380
381         Method JavaDoc m = getNameMethod;
382         MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
383         tas.register(m, txatt);
384
385         TransactionStatus status = transactionStatusForNewTransaction();
386         assertTrue(status.isNewTransaction());
387         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
388         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
389
390         ptm.getTransaction(txatt);
391         ptxControl.setReturnValue(status, 1);
392         ptm.commit(status);
393         ptxControl.setVoidCallable(1);
394         ptxControl.replay();
395
396         final String JavaDoc name = "jenny";
397         TestBean tb = new TestBean() {
398             public String JavaDoc getName() {
399                 TransactionStatus txStatus = TransactionInterceptor.currentTransactionStatus();
400                 txStatus.setRollbackOnly();
401                 return name;
402             }
403         };
404         
405         ITestBean itb = (ITestBean) advised(tb, ptm, tas);
406
407         // verification!?
408
assertTrue(name.equals(itb.getName()));
409
410         ptxControl.verify();
411     }
412     
413     /**
414      * @return a TransactionStatus object configured for a new transaction
415      */

416     private TransactionStatus transactionStatusForNewTransaction() {
417         return new DefaultTransactionStatus(new Object JavaDoc(), true, false, false, false, null);
418     }
419
420     /**
421      * Simulate a transaction infrastructure failure.
422      * Shouldn't invoke target method.
423      */

424     public void testCannotCreateTransaction() throws Exception JavaDoc {
425         TransactionAttribute txatt = new DefaultTransactionAttribute();
426
427         Method JavaDoc m = getNameMethod;
428         MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
429         tas.register(m, txatt);
430
431         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
432         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
433         // Expect a transaction
434
ptm.getTransaction(txatt);
435         CannotCreateTransactionException ex = new CannotCreateTransactionException("foobar", null);
436         ptxControl.setThrowable(ex);
437         ptxControl.replay();
438
439         TestBean tb = new TestBean() {
440             public String JavaDoc getName() {
441                 throw new UnsupportedOperationException JavaDoc(
442                         "Shouldn't have invoked target method when couldn't create transaction for transactional method");
443             }
444         };
445         ITestBean itb = (ITestBean) advised(tb, ptm, tas);
446
447         try {
448             itb.getName();
449             fail("Shouldn't have invoked method");
450         }
451         catch (CannotCreateTransactionException thrown) {
452             assertTrue(thrown == ex);
453         }
454         ptxControl.verify();
455     }
456
457     /**
458      * Simulate failure of the underlying transaction infrastructure to commit.
459      * Check that the target method was invoked, but that the transaction
460      * infrastructure exception was thrown to the client
461      */

462     public void testCannotCommitTransaction() throws Exception JavaDoc {
463         TransactionAttribute txatt = new DefaultTransactionAttribute();
464
465         Method JavaDoc m = setNameMethod;
466         MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
467         tas.register(m, txatt);
468         Method JavaDoc m2 = getNameMethod;
469         // No attributes for m2
470

471         MockControl ptxControl = MockControl.createControl(PlatformTransactionManager.class);
472         PlatformTransactionManager ptm = (PlatformTransactionManager) ptxControl.getMock();
473
474         TransactionStatus status = transactionStatusForNewTransaction();
475         ptm.getTransaction(txatt);
476         ptxControl.setReturnValue(status);
477         UnexpectedRollbackException ex = new UnexpectedRollbackException("foobar", null);
478         ptm.commit(status);
479         ptxControl.setThrowable(ex);
480         ptxControl.replay();
481
482         TestBean tb = new TestBean();
483         ITestBean itb = (ITestBean) advised(tb, ptm, tas);
484
485         String JavaDoc name = "new name";
486         try {
487             itb.setName(name);
488             fail("Shouldn't have succeeded");
489         }
490         catch (UnexpectedRollbackException thrown) {
491             assertTrue(thrown == ex);
492         }
493
494         // Should have invoked target and changed name
495
assertTrue(itb.getName() == name);
496         ptxControl.verify();
497     }
498
499     protected void checkTransactionStatus(boolean expected) {
500         try {
501             TransactionInterceptor.currentTransactionStatus();
502             if (!expected) {
503                 fail("Should have thrown NoTransactionException");
504             }
505         }
506         catch (NoTransactionException ex) {
507             if (expected) {
508                 fail("Should have current TransactionStatus");
509             }
510         }
511     }
512
513 }
514
Popular Tags