Project

General

Profile

« Previous | Next » 

Revision a067b731

Added by Andreas Müller almost 9 years ago

Solve merge conflict for CdmTransactionalIntegrationTest

View differences:

cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/test/integration/CdmTransactionalIntegrationTest.java.orig
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.Level;
13
import org.apache.log4j.Logger;
14
import org.junit.After;
15
import org.junit.Before;
16
import org.junit.BeforeClass;
17
import org.springframework.security.core.context.SecurityContextHolder;
18
import org.springframework.transaction.PlatformTransactionManager;
19
import org.springframework.transaction.TransactionDefinition;
20
import org.springframework.transaction.TransactionException;
21
import org.springframework.transaction.TransactionStatus;
22
import org.springframework.transaction.support.DefaultTransactionDefinition;
23
import org.unitils.database.annotations.Transactional;
24
import org.unitils.database.util.TransactionMode;
25
import org.unitils.spring.annotation.SpringBeanByType;
26

  
27

  
28
/**
29
 * Abstract base class for integration testing a spring / hibernate application using
30
 * the unitils testing framework and dbunit.
31
 *
32
 * This base class extends the {@link CdmItegrationTest} by transactions management features.
33
 *
34
 */
35
@Transactional(TransactionMode.DISABLED) // NOTE: we are handling transaction by ourself in this class, thus we prevent unitils from creating transactions
36
public abstract class CdmTransactionalIntegrationTest extends CdmIntegrationTest {
37

  
38
    protected static final Logger logger = Logger.getLogger(CdmTransactionalIntegrationTest.class);
39

  
40
    /**
41
     * The transaction manager to use
42
     */
43
    @SpringBeanByType
44
    PlatformTransactionManager transactionManager;
45

  
46
    /**
47
     * Should we roll back by default?
48
     */
49
    private boolean defaultRollback = true;
50

  
51
    /**
52
     * Should we commit the current transaction?
53
     */
54
    private boolean	complete = false;
55

  
56
    /**
57
     * Number of transactions started
58
     */
59
    private int	transactionsStarted = 0;
60

  
61
    /**
62
     * Transaction definition used by this test class: by default, a plain
63
     * DefaultTransactionDefinition. Subclasses can change this to cause
64
     * different behavior.
65
     */
66
    protected TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
67

  
68
    /**
69
     * TransactionStatus for this test. Typical subclasses won't need to use it.
70
     */
71
    protected TransactionStatus	transactionStatus;
72

  
73
    /**
74
     * Get the <em>default rollback</em> flag for this test.
75
     *
76
     * @see #setDefaultRollback(boolean)
77
     * @return The <em>default rollback</em> flag.
78
     */
79
    protected boolean isDefaultRollback() {
80
        return this.defaultRollback;
81
    }
82

  
83
    /**
84
     * Subclasses can set this value in their constructor to change the default,
85
     * which is always to roll the transaction back.
86
     */
87
    public void setDefaultRollback(final boolean defaultRollback) {
88
        this.defaultRollback = defaultRollback;
89
    }
90

  
91
    /**
92
     * <p>
93
     * Determines whether or not to rollback transactions for the current test.
94
     * </p>
95
     * <p>
96
     * The default implementation delegates to {@link #isDefaultRollback()}.
97
     * Subclasses can override as necessary.
98
     * </p>
99
     *
100
     * @return The <em>rollback</em> flag for the current test.
101
     */
102
    protected boolean isRollback() {
103
        return isDefaultRollback();
104
    }
105

  
106
    /**
107
     * Call this method in an overridden {@link #runBare()} method to prevent
108
     * transactional execution.
109
     */
110
    protected void preventTransaction() {
111
        this.transactionDefinition = null;
112
    }
113

  
114
    /**
115
     * Call this method in an overridden {@link #runBare()} method to override
116
     * the transaction attributes that will be used, so that {@link #setUp()}
117
     * and {@link #tearDown()} behavior is modified.
118
     *
119
     * @param customDefinition the custom transaction definition
120
     */
121
    protected void setTransactionDefinition(final TransactionDefinition customDefinition) {
122
        this.transactionDefinition = customDefinition;
123
    }
124

  
125
    @BeforeClass
126
    public static void beforeClass() {
127
        logger.debug("before test class");
128
    }
129

  
130
    /**
131
     * This implementation creates a transaction before test execution.
132
     * <p>
133
     * Override {@link #onSetUpBeforeTransaction()} and/or
134
     * {@link #onSetUpInTransaction()} to add custom set-up behavior for
135
     * transactional execution. Alternatively, override this method for general
136
     * set-up behavior, calling <code>super.onSetUp()</code> as part of your
137
     * method implementation.
138
     *
139
     * @throws Exception simply let any exception propagate
140
     * @see #onTearDown()
141
     */
142
    @Before
143
    public void onSetUp() throws Exception {
144

  
145
        this.complete = !this.isRollback();
146

  
147
        if (this.transactionManager == null) {
148
            logger.info("No transaction manager set: test will NOT run within a transaction");
149
        }
150
        else if (this.transactionDefinition == null) {
151
            logger.info("No transaction definition set: test will NOT run within a transaction");
152
        }
153
        else {
154
            onSetUpBeforeTransaction();
155
            startNewTransaction();
156
            try {
157
                onSetUpInTransaction();
158
            }
159
            catch (final Exception ex) {
160
                endTransaction();
161
                throw ex;
162
            }
163
        }
164
    }
165

  
166
    @After
167
    @Before
168
    public void clearAuthentication() {
169
        SecurityContextHolder.getContext().setAuthentication(null);
170
    }
171

  
172
    /**
173
     * Subclasses can override this method to perform any setup operations, such
174
     * as populating a database table, <i>before</i> the transaction created by
175
     * this class. Only invoked if there <i>is</i> a transaction: that is, if
176
     * {@link #preventTransaction()} has not been invoked in an overridden
177
     * {@link #runTest()} method.
178
     *
179
     * @throws Exception simply let any exception propagate
180
     */
181
    protected void onSetUpBeforeTransaction() throws Exception {
182

  
183
    }
184

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

  
203
    }
204

  
205
    /**
206
     * This implementation ends the transaction after test execution.
207
     * <p>
208
     * Override {@link #onTearDownInTransaction()} and/or
209
     * {@link #onTearDownAfterTransaction()} to add custom tear-down behavior
210
     * for transactional execution. Alternatively, override this method for
211
     * general tear-down behavior, calling <code>super.onTearDown()</code> as
212
     * part of your method implementation.
213
     * <p>
214
     * Note that {@link #onTearDownInTransaction()} will only be called if a
215
     * transaction is still active at the time of the test shutdown. In
216
     * particular, it will <i>not</i> be called if the transaction has been
217
     * completed with an explicit {@link #endTransaction()} call before.
218
     *
219
     * @throws Exception simply let any exception propagate
220
     * @see #onSetUp()
221
     */
222
    @After
223
    public void onTearDown() throws Exception {
224

  
225
        // Call onTearDownInTransaction and end transaction if the transaction
226
        // is still active.
227
        if (this.transactionStatus != null && !this.transactionStatus.isCompleted()) {
228
            try {
229
                onTearDownInTransaction();
230
            }
231
            finally {
232
                endTransaction();
233
            }
234
        }
235
        // Call onTearDownAfterTransaction if there was at least one
236
        // transaction, even if it has been completed early through an
237
        // endTransaction() call.
238
        if (this.transactionsStarted > 0) {
239
            onTearDownAfterTransaction();
240
        }
241
    }
242

  
243
    /**
244
     * Subclasses can override this method to run invariant tests here. The
245
     * transaction is <i>still active</i> at this point, so any changes made in
246
     * the transaction will still be visible. However, there is no need to clean
247
     * up the database, as a rollback will follow automatically.
248
     * <p>
249
     * <b>NB:</b> Not called if there is no actual transaction, for example due
250
     * to no transaction manager being provided in the application context.
251
     *
252
     * @throws Exception simply let any exception propagate
253
     */
254
    protected void onTearDownInTransaction() throws Exception {
255

  
256
    }
257

  
258
    /**
259
     * Subclasses can override this method to perform cleanup after a
260
     * transaction here. At this point, the transaction is <i>not active anymore</i>.
261
     *
262
     * @throws Exception simply let any exception propagate
263
     */
264
    protected void onTearDownAfterTransaction() throws Exception {
265

  
266
    }
267

  
268
    /**
269
     * Cause the transaction to commit for this test method, even if the test
270
     * method is configured to {@link #isRollback() rollback}.
271
     *
272
     * @throws IllegalStateException if the operation cannot be set to complete
273
     *         as no transaction manager was provided
274
     */
275
    protected void setComplete() {
276

  
277
        if (this.transactionManager == null) {
278
            throw new IllegalStateException("No transaction manager set");
279
        }
280
        this.complete = true;
281
        logger.debug("set complete = true");
282
    }
283

  
284
    /**
285
     * Immediately force a commit or rollback of the transaction, according to
286
     * the <code>complete</code> and {@link #isRollback() rollback} flags.
287
     * <p>
288
     * Can be used to explicitly let the transaction end early, for example to
289
     * check whether lazy associations of persistent objects work outside of a
290
     * transaction (that is, have been initialized properly).
291
     *
292
     * @see #setComplete()
293
     */
294
    protected void endTransaction() {
295

  
296
        final boolean commit = this.complete || !isRollback();
297

  
298
        if (this.transactionStatus != null) {
299
            try {
300
                logger.debug("Trying to commit or rollback");
301
                if (commit) {
302
                    this.transactionManager.commit(this.transactionStatus);
303
                    logger.debug("Committed transaction after execution of test");
304
                }
305
                else {
306
                    this.transactionManager.rollback(this.transactionStatus);
307
                    logger.debug("Rolled back transaction after execution of test.");
308
                }
309
            }
310
            finally {
311
                logger.debug("Clearing transactionStatus");
312
                this.transactionStatus = null;
313
            }
314
        }
315
    }
316

  
317
    protected void rollback() {
318

  
319
        if (this.transactionStatus != null) {
320
            try {
321
                logger.debug("trying to rollback");
322
                this.transactionManager.rollback(this.transactionStatus);
323
                logger.debug("Rolled back transaction after execution of test.");
324
            }
325
            finally {
326
                logger.debug("Clearing transactionStatus");
327
                this.transactionStatus = null;
328
            }
329
        }
330
    }
331

  
332

  
333
    /**
334
     * Start a new transaction. Only call this method if
335
     * {@link #endTransaction()} has been called. {@link #setComplete()} can be
336
     * used again in the new transaction. The fate of the new transaction, by
337
     * default, will be the usual rollback.
338
     *
339
     * @throws TransactionException if starting the transaction failed
340
     */
341
    protected void startNewTransaction() throws TransactionException {
342

  
343
        if (this.transactionStatus != null) {
344
            throw new IllegalStateException("Cannot start new transaction without ending existing transaction: "
345
                    + "Invoke endTransaction() before startNewTransaction()");
346
        }
347
        if (this.transactionManager == null) {
348
            throw new IllegalStateException("No transaction manager set");
349
        }
350

  
351
        this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
352
        ++this.transactionsStarted;
353
        this.complete = !this.isRollback();
354

  
355
        if (logger.isDebugEnabled()) {
356
            logger.debug("Began transaction (" + this.transactionsStarted + "): transaction manager ["
357
                    + this.transactionManager + "]; rollback [" + this.isRollback() + "].");
358
        }
359
    }
360

  
361

  
362
    /**
363
     * @param tableNames the tables supplied by this array will be <b>printed after</b> the transaction has committed
364
     * and ended <b>only if the logging level is set to debug</b>, e.g.:
365
     * <pre>
366
     *  log4j.logger.eu.etaxonomy.cdm.test.integration=DEBUG
367
     * </pre>
368
     */
369
    protected void commitAndStartNewTransaction(final String[] tableNames) {
370
        commit();
371
        if(logger.isEnabledFor(Level.DEBUG)){
372
            printDataSet(System.out, tableNames);
373
//          careful, the following will overwrite existing files:
374
//          writeDbUnitDataSetFile(tableNames);
375
        }
376
        startNewTransaction();
377
    }
378

  
379
    /**
380
     * Commit and end transaction
381
     */
382
    protected void commit() {
383
        setComplete();
384
        endTransaction();
385
    }
386

  
387
}

Also available in: Unified diff