KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > test > AbstractTransactionalSpringContextTests


1 /*
2  * Copyright 2002-2007 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.test;
18
19 import org.springframework.transaction.PlatformTransactionManager;
20 import org.springframework.transaction.TransactionDefinition;
21 import org.springframework.transaction.TransactionException;
22 import org.springframework.transaction.TransactionStatus;
23 import org.springframework.transaction.support.DefaultTransactionDefinition;
24
25 /**
26  * Convenient base class for tests that should occur in a transaction, but normally
27  * will roll the transaction back on the completion of each test.
28  *
29  * <p>This is useful in a range of circumstances, allowing the following benefits:
30  * <ul>
31  * <li>Ability to delete or insert any data in the database, without affecting other tests
32  * <li>Providing a transactional context for any code requiring a transaction
33  * <li>Ability to write anything to the database without any need to clean up.
34  * </ul>
35  *
36  * <p>This class is typically very fast, compared to traditional setup/teardown scripts.
37  *
38  * <p>If data should be left in the database, call the {@link #setComplete()}
39  * method in each test. The {@link #setDefaultRollback "defaultRollback"} property,
40  * which defaults to "true", determines whether transactions will complete by default.
41  *
42  * <p>It is even possible to end the transaction early; for example, to verify lazy
43  * loading behavior of an O/R mapping tool. (This is a valuable away to avoid
44  * unexpected errors when testing a web UI, for example.) Simply call the
45  * {@link #endTransaction()} method. Execution will then occur without a
46  * transactional context.
47  *
48  * <p>The {@link #startNewTransaction()} method may be called after a call to
49  * {@link #endTransaction()} if you wish to create a new transaction, quite
50  * independent of the old transaction. The new transaction's default fate will be to
51  * roll back, unless {@link #setComplete()} is called again during the scope of the
52  * new transaction. Any number of transactions may be created and ended in this way.
53  * The final transaction will automatically be rolled back when the test case is
54  * torn down.
55  *
56  * <p>Transactional behavior requires a single bean in the context implementing the
57  * {@link org.springframework.transaction.PlatformTransactionManager} interface.
58  * This will be set by the superclass's Dependency Injection mechanism.
59  * If using the superclass's Field Injection mechanism, the implementation should
60  * be named "transactionManager". This mechanism allows the use of the
61  * {@link AbstractDependencyInjectionSpringContextTests} superclass even
62  * when there is more than one transaction manager in the context.
63  *
64  * <p><b>This base class can also be used without transaction management, if no
65  * PlatformTransactionManager bean is found in the context provided.</b>
66  * Be careful about using this mode, as it allows the potential to permanently modify
67  * data. This mode is available only if dependency checking is turned off in the
68  * {@link AbstractDependencyInjectionSpringContextTests} superclass. The non-transactional
69  * capability is provided to enable use of the same subclass in different environments.
70  *
71  * @author Rod Johnson
72  * @author Juergen Hoeller
73  * @since 1.1.1
74  */

75 public abstract class AbstractTransactionalSpringContextTests extends AbstractDependencyInjectionSpringContextTests {
76
77     /** The transaction manager to use */
78     protected PlatformTransactionManager transactionManager;
79
80     /** Should we roll back by default? */
81     private boolean defaultRollback = true;
82
83     /** Should we commit the current transaction? */
84     private boolean complete = false;
85
86     /** Number of transactions started */
87     private int transactionsStarted = 0;
88
89     /**
90      * Transaction definition used by this test class: by default, a plain
91      * DefaultTransactionDefinition. Subclasses can change this to cause different behavior.
92      */

93     protected TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
94
95     /**
96      * TransactionStatus for this test. Typical subclasses won't need to use it.
97      */

98     protected TransactionStatus transactionStatus;
99
100
101     /**
102      * Default constructor for AbstractTransactionalSpringContextTests.
103      */

104     public AbstractTransactionalSpringContextTests() {
105     }
106
107     /**
108      * Constructor for AbstractTransactionalSpringContextTests with a JUnit name.
109      */

110     public AbstractTransactionalSpringContextTests(String JavaDoc name) {
111         super(name);
112     }
113
114
115     /**
116      * Specify the transaction manager to use. No transaction management will be available
117      * if this is not set. Populated through dependency injection by the superclass.
118      * <p>This mode works only if dependency checking is turned off in the
119      * {@link AbstractDependencyInjectionSpringContextTests} superclass.
120      */

121     public void setTransactionManager(PlatformTransactionManager transactionManager) {
122         this.transactionManager = transactionManager;
123     }
124
125     /**
126      * Subclasses can set this value in their constructor to change
127      * default, which is always to roll the transaction back.
128      */

129     public void setDefaultRollback(boolean defaultRollback) {
130         this.defaultRollback = defaultRollback;
131     }
132
133
134     /**
135      * Call this method in an overridden {@link #runBare()} method to
136      * prevent transactional execution.
137      */

138     protected void preventTransaction() {
139         this.transactionDefinition = null;
140     }
141
142     /**
143      * Call this method in an overridden {@link #runBare()} method to
144      * override the transaction attributes that will be used, so that
145      * {@link #setUp()} and {@link #tearDown()} behavior is modified.
146      * @param customDefinition the custom transaction definition
147      */

148     protected void setTransactionDefinition(TransactionDefinition customDefinition) {
149         this.transactionDefinition = customDefinition;
150     }
151
152
153     /**
154      * This implementation creates a transaction before test execution.
155      * Override {@link #onSetUpBeforeTransaction()} and/or
156      * {@link #onSetUpInTransaction()} to add custom set-up behavior.
157      */

158     protected final void onSetUp() throws Exception JavaDoc {
159         this.complete = !this.defaultRollback;
160
161         if (this.transactionManager == null) {
162             logger.info("No transaction manager set: test will NOT run within a transaction");
163         }
164         else if (this.transactionDefinition == null) {
165             logger.info("No transaction definition set: test will NOT run within a transaction");
166         }
167         else {
168             onSetUpBeforeTransaction();
169             startNewTransaction();
170             try {
171                 onSetUpInTransaction();
172             }
173             catch (Exception JavaDoc ex) {
174                 endTransaction();
175                 throw ex;
176             }
177         }
178     }
179
180     /**
181      * Subclasses can override this method to perform any setup operations,
182      * such as populating a database table, <i>before</i> the transaction
183      * created by this class. Only invoked if there <i>is</i> a transaction:
184      * that is, if {@link #preventTransaction()} has not been invoked in
185      * an overridden {@link #runTest()} method.
186      * @throws Exception simply let any exception propagate
187      */

188     protected void onSetUpBeforeTransaction() throws Exception JavaDoc {
189     }
190
191     /**
192      * Subclasses can override this method to perform any setup operations,
193      * such as populating a database table, <i>within</i> the transaction
194      * created by this class.
195      * <p><b>NB:</b> Not called if there is no transaction management, due to no
196      * transaction manager being provided in the context.
197      * <p>If any {@link Throwable} is thrown, the transaction that has been started
198      * prior to the execution of this method will be {@link #endTransaction() ended}
199      * (or rather an attempt will be made to {@link #endTransaction() end it gracefully});
200      * The offending {@link Throwable} will then be rethrown.
201      * @throws Exception simply let any exception propagate
202      */

203     protected void onSetUpInTransaction() throws Exception JavaDoc {
204     }
205
206
207     /**
208      * This implementation ends the transaction after test execution.
209      * Override {@link #onTearDownInTransaction()} and/or
210      * {@link #onTearDownAfterTransaction()} to add custom tear-down behavior.
211      * <p>Note that {@link #onTearDownInTransaction()} will only be called
212      * if a transaction is still active at the time of the test shutdown.
213      * In particular, it will <i>not</i> be called if the transaction has
214      * been completed with an explicit {@link #endTransaction()} call before.
215      * @throws Exception simply let any exception propagate
216      */

217     protected final void onTearDown() throws Exception JavaDoc {
218         // Call onTearDownInTransaction and end transaction if the transaction is still active.
219
if (this.transactionStatus != null && !this.transactionStatus.isCompleted()) {
220             try {
221                 onTearDownInTransaction();
222             }
223             finally {
224                 endTransaction();
225             }
226         }
227         // Call onTearDownAfterTransaction if there was at least one transaction,
228
// even if it has been completed early through an endTransaction() call.
229
if (this.transactionsStarted > 0) {
230             onTearDownAfterTransaction();
231         }
232     }
233
234     /**
235      * Subclasses can override this method to run invariant tests here.
236      * The transaction is <i>still active</i> at this point, so any changes
237      * made in the transaction will still be visible. However, there is no need
238      * to clean up the database, as a rollback will follow automatically.
239      * <p><b>NB:</b> Not called if there is no actual transaction, for example
240      * due to no transaction manager being provided in the application context.
241      * @throws Exception simply let any exception propagate
242      */

243     protected void onTearDownInTransaction() throws Exception JavaDoc {
244     }
245
246     /**
247      * Subclasses can override this method to perform cleanup after a transaction
248      * here. At this point, the transaction is <i>not active anymore</i>.
249      * @throws Exception simply let any exception propagate
250      */

251     protected void onTearDownAfterTransaction() throws Exception JavaDoc {
252     }
253
254
255     /**
256      * Cause the transaction to commit for this test method,
257      * even if default is set to rollback.
258      * @throws IllegalStateException if the operation cannot be set to
259      * complete as no transaction manager was provided
260      */

261     protected void setComplete() {
262         if (this.transactionManager == null) {
263             throw new IllegalStateException JavaDoc("No transaction manager set");
264         }
265         this.complete = true;
266     }
267
268     /**
269      * Immediately force a commit or rollback of the transaction,
270      * according to the complete flag.
271      * <p>Can be used to explicitly let the transaction end early,
272      * for example to check whether lazy associations of persistent objects
273      * work outside of a transaction (that is, have been initialized properly).
274      * @see #setComplete()
275      */

276     protected void endTransaction() {
277         if (this.transactionStatus != null) {
278             try {
279                 if (!this.complete) {
280                     this.transactionManager.rollback(this.transactionStatus);
281                     logger.info("Rolled back transaction after test execution");
282                 }
283                 else {
284                     this.transactionManager.commit(this.transactionStatus);
285                     logger.info("Committed transaction after test execution");
286                 }
287             }
288             finally {
289                 this.transactionStatus = null;
290             }
291         }
292     }
293
294     /**
295      * Start a new transaction. Only call this method if {@link #endTransaction()}
296      * has been called. {@link #setComplete()} can be used again in the new transaction.
297      * The fate of the new transaction, by default, will be the usual rollback.
298      * @throws TransactionException if starting the transaction failed
299      */

300     protected void startNewTransaction() throws TransactionException {
301         if (this.transactionStatus != null) {
302             throw new IllegalStateException JavaDoc("Cannot start new transaction without ending existing transaction: " +
303                     "Invoke endTransaction() before startNewTransaction()");
304         }
305         if (this.transactionManager == null) {
306             throw new IllegalStateException JavaDoc("No transaction manager set");
307         }
308
309         this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
310         ++this.transactionsStarted;
311         this.complete = !this.defaultRollback;
312
313         if (logger.isInfoEnabled()) {
314             logger.info("Began transaction (" + this.transactionsStarted + "): transaction manager [" +
315                     this.transactionManager + "]; default rollback = " + this.defaultRollback);
316         }
317     }
318
319 }
320
Popular Tags