Project

General

Profile

Download (12.1 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2009 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.test.integration;
11

    
12
import org.apache.log4j.Logger;
13
import org.junit.After;
14
import org.junit.Before;
15
import org.springframework.transaction.PlatformTransactionManager;
16
import org.springframework.transaction.TransactionDefinition;
17
import org.springframework.transaction.TransactionException;
18
import org.springframework.transaction.TransactionStatus;
19
import org.springframework.transaction.support.DefaultTransactionDefinition;
20
import org.unitils.database.annotations.Transactional;
21
import org.unitils.database.util.TransactionMode;
22
import org.unitils.spring.annotation.SpringBeanByType;
23

    
24
@Transactional(TransactionMode.DISABLED)
25
public abstract class CdmTransactionalIntegrationTest extends CdmIntegrationTest {
26
    protected static final Logger logger = Logger.getLogger(CdmTransactionalIntegrationTest.class);
27

    
28
    /**
29
     * The transaction manager to use
30
     */
31
    @SpringBeanByType
32
    PlatformTransactionManager transactionManager;
33

    
34
    /**
35
     * Should we roll back by default?
36
     */
37
    private boolean defaultRollback = true;
38

    
39
    /**
40
     * Should we commit the current transaction?
41
     */
42
    private boolean	complete = false;
43

    
44
    /**
45
     * Number of transactions started
46
     */
47
    private int	transactionsStarted = 0;
48

    
49
    /**
50
     * Transaction definition used by this test class: by default, a plain
51
     * DefaultTransactionDefinition. Subclasses can change this to cause
52
     * different behavior.
53
     */
54
    protected TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
55

    
56
    /**
57
     * TransactionStatus for this test. Typical subclasses won't need to use it.
58
     */
59
    protected TransactionStatus	transactionStatus;
60

    
61
    /**
62
     * Get the <em>default rollback</em> flag for this test.
63
     *
64
     * @see #setDefaultRollback(boolean)
65
     * @return The <em>default rollback</em> flag.
66
     */
67
    protected boolean isDefaultRollback() {
68
        return this.defaultRollback;
69
    }
70

    
71
    /**
72
     * Subclasses can set this value in their constructor to change the default,
73
     * which is always to roll the transaction back.
74
     */
75
    public void setDefaultRollback(final boolean defaultRollback) {
76
        this.defaultRollback = defaultRollback;
77
    }
78

    
79
    /**
80
     * <p>
81
     * Determines whether or not to rollback transactions for the current test.
82
     * </p>
83
     * <p>
84
     * The default implementation delegates to {@link #isDefaultRollback()}.
85
     * Subclasses can override as necessary.
86
     * </p>
87
     *
88
     * @return The <em>rollback</em> flag for the current test.
89
     */
90
    protected boolean isRollback() {
91
        return isDefaultRollback();
92
    }
93

    
94
    /**
95
     * Call this method in an overridden {@link #runBare()} method to prevent
96
     * transactional execution.
97
     */
98
    protected void preventTransaction() {
99
        this.transactionDefinition = null;
100
    }
101

    
102
    /**
103
     * Call this method in an overridden {@link #runBare()} method to override
104
     * the transaction attributes that will be used, so that {@link #setUp()}
105
     * and {@link #tearDown()} behavior is modified.
106
     *
107
     * @param customDefinition the custom transaction definition
108
     */
109
    protected void setTransactionDefinition(final TransactionDefinition customDefinition) {
110
        this.transactionDefinition = customDefinition;
111
    }
112

    
113
    /**
114
     * This implementation creates a transaction before test execution.
115
     * <p>
116
     * Override {@link #onSetUpBeforeTransaction()} and/or
117
     * {@link #onSetUpInTransaction()} to add custom set-up behavior for
118
     * transactional execution. Alternatively, override this method for general
119
     * set-up behavior, calling <code>super.onSetUp()</code> as part of your
120
     * method implementation.
121
     *
122
     * @throws Exception simply let any exception propagate
123
     * @see #onTearDown()
124
     */
125
    @Before
126
    public void onSetUp() throws Exception {
127

    
128
        this.complete = !this.isRollback();
129

    
130
        if (this.transactionManager == null) {
131
            logger.info("No transaction manager set: test will NOT run within a transaction");
132
        }
133
        else if (this.transactionDefinition == null) {
134
            logger.info("No transaction definition set: test will NOT run within a transaction");
135
        }
136
        else {
137
            onSetUpBeforeTransaction();
138
            startNewTransaction();
139
            try {
140
                onSetUpInTransaction();
141
            }
142
            catch (final Exception ex) {
143
                endTransaction();
144
                throw ex;
145
            }
146
        }
147
    }
148

    
149
    /**
150
     * Subclasses can override this method to perform any setup operations, such
151
     * as populating a database table, <i>before</i> the transaction created by
152
     * this class. Only invoked if there <i>is</i> a transaction: that is, if
153
     * {@link #preventTransaction()} has not been invoked in an overridden
154
     * {@link #runTest()} method.
155
     *
156
     * @throws Exception simply let any exception propagate
157
     */
158
    protected void onSetUpBeforeTransaction() throws Exception {
159

    
160
    }
161

    
162
    /**
163
     * Subclasses can override this method to perform any setup operations, such
164
     * as populating a database table, <i>within</i> the transaction created by
165
     * this class.
166
     * <p>
167
     * <b>NB:</b> Not called if there is no transaction management, due to no
168
     * transaction manager being provided in the context.
169
     * <p>
170
     * If any {@link Throwable} is thrown, the transaction that has been started
171
     * prior to the execution of this method will be
172
     * {@link #endTransaction() ended} (or rather an attempt will be made to
173
     * {@link #endTransaction() end it gracefully}); The offending
174
     * {@link Throwable} will then be rethrown.
175
     *
176
     * @throws Exception simply let any exception propagate
177
     */
178
    protected void onSetUpInTransaction() throws Exception {
179

    
180
    }
181

    
182
    /**
183
     * This implementation ends the transaction after test execution.
184
     * <p>
185
     * Override {@link #onTearDownInTransaction()} and/or
186
     * {@link #onTearDownAfterTransaction()} to add custom tear-down behavior
187
     * for transactional execution. Alternatively, override this method for
188
     * general tear-down behavior, calling <code>super.onTearDown()</code> as
189
     * part of your method implementation.
190
     * <p>
191
     * Note that {@link #onTearDownInTransaction()} will only be called if a
192
     * transaction is still active at the time of the test shutdown. In
193
     * particular, it will <i>not</i> be called if the transaction has been
194
     * completed with an explicit {@link #endTransaction()} call before.
195
     *
196
     * @throws Exception simply let any exception propagate
197
     * @see #onSetUp()
198
     */
199
    @After
200
    public void onTearDown() throws Exception {
201

    
202
        // Call onTearDownInTransaction and end transaction if the transaction
203
        // is still active.
204
        if (this.transactionStatus != null && !this.transactionStatus.isCompleted()) {
205
            try {
206
                onTearDownInTransaction();
207
            }
208
            finally {
209
                endTransaction();
210
            }
211
        }
212
        // Call onTearDownAfterTransaction if there was at least one
213
        // transaction, even if it has been completed early through an
214
        // endTransaction() call.
215
        if (this.transactionsStarted > 0) {
216
            onTearDownAfterTransaction();
217
        }
218
    }
219

    
220
    /**
221
     * Subclasses can override this method to run invariant tests here. The
222
     * transaction is <i>still active</i> at this point, so any changes made in
223
     * the transaction will still be visible. However, there is no need to clean
224
     * up the database, as a rollback will follow automatically.
225
     * <p>
226
     * <b>NB:</b> Not called if there is no actual transaction, for example due
227
     * to no transaction manager being provided in the application context.
228
     *
229
     * @throws Exception simply let any exception propagate
230
     */
231
    protected void onTearDownInTransaction() throws Exception {
232

    
233
    }
234

    
235
    /**
236
     * Subclasses can override this method to perform cleanup after a
237
     * transaction here. At this point, the transaction is <i>not active anymore</i>.
238
     *
239
     * @throws Exception simply let any exception propagate
240
     */
241
    protected void onTearDownAfterTransaction() throws Exception {
242

    
243
    }
244

    
245
    /**
246
     * Cause the transaction to commit for this test method, even if the test
247
     * method is configured to {@link #isRollback() rollback}.
248
     *
249
     * @throws IllegalStateException if the operation cannot be set to complete
250
     *         as no transaction manager was provided
251
     */
252
    protected void setComplete() {
253

    
254
        if (this.transactionManager == null) {
255
            throw new IllegalStateException("No transaction manager set");
256
        }
257
        this.complete = true;
258
    }
259

    
260
    /**
261
     * Immediately force a commit or rollback of the transaction, according to
262
     * the <code>complete</code> and {@link #isRollback() rollback} flags.
263
     * <p>
264
     * Can be used to explicitly let the transaction end early, for example to
265
     * check whether lazy associations of persistent objects work outside of a
266
     * transaction (that is, have been initialized properly).
267
     *
268
     * @see #setComplete()
269
     */
270
    protected void endTransaction() {
271

    
272
        final boolean commit = this.complete || !isRollback();
273

    
274
        if (this.transactionStatus != null) {
275
            try {
276
                if (commit) {
277
                    this.transactionManager.commit(this.transactionStatus);
278
                    logger.debug("Committed transaction after execution of test");
279
                }
280
                else {
281
                    this.transactionManager.rollback(this.transactionStatus);
282
                    logger.debug("Rolled back transaction after execution of test.");
283
                }
284
            }
285
            finally {
286
                this.transactionStatus = null;
287
            }
288
        }
289
    }
290

    
291
    protected void rollback() {
292

    
293
        if (this.transactionStatus != null) {
294
            try {
295
                this.transactionManager.rollback(this.transactionStatus);
296
                logger.debug("Rolled back transaction after execution of test.");
297
            }
298
            finally {
299
                this.transactionStatus = null;
300
            }
301
        }
302
    }
303

    
304

    
305
    /**
306
     * Start a new transaction. Only call this method if
307
     * {@link #endTransaction()} has been called. {@link #setComplete()} can be
308
     * used again in the new transaction. The fate of the new transaction, by
309
     * default, will be the usual rollback.
310
     *
311
     * @throws TransactionException if starting the transaction failed
312
     */
313
    protected void startNewTransaction() throws TransactionException {
314

    
315
        if (this.transactionStatus != null) {
316
            throw new IllegalStateException("Cannot start new transaction without ending existing transaction: "
317
                    + "Invoke endTransaction() before startNewTransaction()");
318
        }
319
        if (this.transactionManager == null) {
320
            throw new IllegalStateException("No transaction manager set");
321
        }
322

    
323
        this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
324
        ++this.transactionsStarted;
325
        this.complete = !this.isRollback();
326

    
327
        if (logger.isDebugEnabled()) {
328
            logger.debug("Began transaction (" + this.transactionsStarted + "): transaction manager ["
329
                    + this.transactionManager + "]; rollback [" + this.isRollback() + "].");
330
        }
331
    }
332

    
333

    
334
    /**
335
     * @param tableNames IS IGNORED
336
     */
337
    protected void commitAndStartNewTransaction(final String[] tableNames) {
338
        setComplete();
339
        endTransaction();
340
//		printDataSet(System.out, tableNames);
341
        startNewTransaction();
342
    }
343

    
344
}
(2-2/4)