Project

General

Profile

Download (20.6 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 java.io.ByteArrayOutputStream;
13
import java.io.File;
14
import java.io.FileNotFoundException;
15
import java.io.FileOutputStream;
16
import java.io.IOException;
17
import java.io.OutputStream;
18
import java.io.OutputStreamWriter;
19
import java.sql.SQLException;
20
import java.util.ArrayList;
21
import java.util.List;
22

    
23
import javax.sql.DataSource;
24
import javax.xml.transform.Result;
25
import javax.xml.transform.Source;
26
import javax.xml.transform.Transformer;
27
import javax.xml.transform.TransformerException;
28
import javax.xml.transform.TransformerFactory;
29
import javax.xml.transform.stream.StreamResult;
30

    
31
import org.apache.log4j.Logger;
32
import org.dbunit.database.DatabaseConfig;
33
import org.dbunit.database.DatabaseConnection;
34
import org.dbunit.database.DatabaseDataSet;
35
import org.dbunit.database.IDatabaseConnection;
36
import org.dbunit.dataset.IDataSet;
37
import org.dbunit.dataset.ITableIterator;
38
import org.dbunit.dataset.filter.ExcludeTableFilter;
39
import org.dbunit.dataset.filter.ITableFilterSimple;
40
import org.dbunit.dataset.xml.FlatDtdDataSet;
41
import org.dbunit.dataset.xml.FlatXmlDataSet;
42
import org.dbunit.dataset.xml.FlatXmlWriter;
43
import org.dbunit.ext.h2.H2DataTypeFactory;
44
import org.h2.tools.Server;
45
import org.junit.Before;
46
import org.springframework.transaction.PlatformTransactionManager;
47
import org.springframework.transaction.support.DefaultTransactionDefinition;
48
import org.unitils.UnitilsJUnit4;
49
import org.unitils.database.annotations.TestDataSource;
50
import org.unitils.dbunit.util.MultiSchemaXmlDataSetReader;
51
import org.unitils.orm.hibernate.annotation.HibernateSessionFactory;
52
import org.unitils.spring.annotation.SpringApplicationContext;
53
import org.unitils.spring.annotation.SpringBeanByType;
54

    
55
import eu.etaxonomy.cdm.test.unitils.FlatFullXmlWriter;
56

    
57
/**
58
 * Abstract base class for integration testing a spring / hibernate application using
59
 * the unitils testing framework and dbunit.
60
 *
61
 * <h2>Data base server</h2>
62
 * The database being used is configured in
63
 * the maven module specific unitils.properties file. By default the database is a H2 in memory data base.
64
 * <p>
65
 * The H2 can be monitored during the test execution if the system property <code>h2Server</code> is given as
66
 * java virtual machine argument:
67
 * <code>-Dh2Server</code>.
68
 * An internal h2 database application will be started and listens at port 8082.
69
 * The port to listen on can be specified by passing a second argument, e.g.: <code>-Dh2Server 8083</code>.
70
 *
71
 * <h2>Creating create DbUnit dataset files</h2>
72
 * In order to create DbUnit datasets  for integration tests it is highly recommended method to use the
73
 * {@link #writeDbUnitDataSetFile(String[])} method.
74
 *
75
 * From {@link http://www.unitils.org/tutorial-database.html}, by default every test is executed in a transaction,
76
 * which is committed at the end of the test. This can be disabled using @Transactional(TransactionMode.DISABLED)
77
 *
78
 * @see <a href="http://www.unitils.org">unitils home page</a>
79
 *
80
 * @author ben.clark
81
 * @author a.kohlbecker (2013)
82
 */
83
@SpringApplicationContext("file:./target/test-classes/eu/etaxonomy/cdm/applicationContext-test.xml")
84
// @HibernateSessionFactory is only needed for test phases like afterTestTearDown
85
// which are configured to run without a spring application context.
86
// for further details, see /cdmlib-test/src/main/resources/eu/etaxonomy/cdm/hibernate-test.cfg.xml
87
@HibernateSessionFactory({"/eu/etaxonomy/cdm/hibernate.cfg.xml", "/eu/etaxonomy/cdm/hibernate-test.cfg.xml"})
88
public abstract class CdmIntegrationTest extends UnitilsJUnit4 {
89
    protected static final Logger logger = Logger.getLogger(CdmIntegrationTest.class);
90

    
91
    private static final String PROPERTY_H2_SERVER = "h2Server";
92

    
93
    /**
94
     * List of the tables which are initially being populated during term loading. {@link PersistentTermInitializer}
95
     */
96
    public static final String[] termLoadingTables = new String[]{
97
        "DEFINEDTERMBASE",
98
        "DEFINEDTERMBASE_AUD",
99
        "DEFINEDTERMBASE_CONTINENT",
100
        "DEFINEDTERMBASE_REPRESENTATION",
101
        "DEFINEDTERMBASE_REPRESENTATION_AUD",
102
        "HIBERNATE_SEQUENCES",
103
        "RELATIONSHIPTERMBASE_INVERSEREPRESENTATION",
104
        "RELATIONSHIPTERMBASE_INVERSEREPRESENTATION_AUD",
105
        "REPRESENTATION",
106
        "REPRESENTATION_AUD",
107
        "TERMVOCABULARY",
108
        "TERMVOCABULARY_AUD",
109
        "TERMVOCABULARY_REPRESENTATION",
110
        "TERMVOCABULARY_REPRESENTATION_AUD"};
111

    
112
    @TestDataSource
113
    protected DataSource dataSource;
114

    
115
    private PlatformTransactionManager transactionManager;
116

    
117
    protected DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
118

    
119
    @SpringBeanByType
120
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
121
        this.transactionManager = transactionManager;
122
    }
123

    
124
    protected IDatabaseConnection getConnection() {
125
        IDatabaseConnection connection = null;
126
        try {
127
            /// FIXME must use unitils.properties: database.schemaNames
128
            connection = new DatabaseConnection(dataSource.getConnection(), "PUBLIC");
129

    
130
            DatabaseConfig config = connection.getConfig();
131

    
132
            // FIXME must use unitils.properties: org.unitils.core.dbsupport.DbSupport.implClassName
133
            //       & database.dialect to find configured DataTypeFactory
134
            config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
135
                    new H2DataTypeFactory());
136
        } catch (Exception e) {
137
            logger.error(e);
138
        }
139
        return connection;
140
    }
141

    
142
//  @SpringBeanByType
143
//  private IAgentDao agentDao;
144
//
145
//  @Before
146
//  public void debugExistingUsers(){
147
//      StringBuilder agentstr = new StringBuilder();
148
//      for(AgentBase agent : agentDao.list(null, null)) {
149
//          agentstr.append(agent.getId()).append(", ");
150
//      }
151
//      System.err.println("####" +  agentstr);
152
//  }
153

    
154
    @Before
155
    public void startH2Server() throws Exception {
156

    
157
        if(System.getProperty(PROPERTY_H2_SERVER) != null){
158
            try {
159
                List<String> args = new ArrayList<String>();
160
                try {
161
                    Integer port = Integer.parseInt(System.getProperty(PROPERTY_H2_SERVER));
162
                    args.add("-webPort");
163
                    args.add(port.toString());
164
                } catch (Exception e) {
165
                    // will start at port 8082 by default
166
                }
167
                Server.createWebServer(args.toArray(new String[]{})).start();
168
            } catch (SQLException e) {
169
                e.printStackTrace();
170
            }
171
        }
172
    }
173

    
174

    
175
    /**
176
     * Prints the data set to an output stream, using the
177
     * {@link FlatXmlDataSet}.
178
     * <p>
179
     * <h2>NOTE: for compatibility with unitils 3.x you may
180
     * want to use the {@link #printDataSetWithNull(OutputStream)}
181
     * method instead.</h2>
182
     * <p>
183
     * Remember, if you've just called save() or
184
     * update(), the data isn't written to the database until the
185
     * transaction is committed, and that isn't until after the
186
     * method exits. Consequently, if you want to test writing to
187
     * the database, either use the {@literal @ExpectedDataSet}
188
     * annotation (that executes after the test is run), or use
189
     * {@link CdmTransactionalIntegrationTest}.
190
     *
191
     * @param out The OutputStream to write to.
192
     * @see FlatFullXmlWriter
193
     */
194
    public void printDataSet(OutputStream out) {
195
        IDatabaseConnection connection = null;
196

    
197
        try {
198
            connection = getConnection();
199
            IDataSet actualDataSet = connection.createDataSet();
200
            FlatXmlDataSet.write(actualDataSet, out);
201
        } catch (Exception e) {
202
            logger.error(e);
203
        } finally {
204
            try {
205
                if (connection != null){
206
                    connection.close();
207
                }
208
            } catch (SQLException sqle) {
209
                logger.error(sqle);
210
            }
211
        }
212
    }
213

    
214
    /**
215
     * Prints the data set to an output stream, using the
216
     * {@link FlatFullXmlWriter}.
217
     * which is a variant of the {@link org.dbunit.dataset.xml.FlatXmlWriter}. It
218
     * inserts '[null]' place holders for null values instead of skipping them.
219
     * This was necessary to make this xml database export compatible to the
220
     * {@link MultiSchemaXmlDataSetReader} which is used in Unitils since version 3.x
221
     * <p>
222
     * @param out out The OutputStream to write to.
223
     * @param includeTableNames
224
     */
225
    public void printDataSetWithNull(OutputStream out, String[] includeTableNames) {
226
        printDataSetWithNull(out, null, null, includeTableNames);
227
    }
228

    
229
    /**
230
     * Prints the data set to an output stream, using the
231
     * {@link FlatFullXmlWriter}.
232
     * which is a variant of the {@link org.dbunit.dataset.xml.FlatXmlWriter}. It
233
     * inserts '[null]' place holders for null values instead of skipping them.
234
     * This was necessary to make this xml database export compatible to the
235
     * {@link MultiSchemaXmlDataSetReader} which is used in Unitils since version 3.x
236
     * <p>
237
     * Remember, if you've just called save() or
238
     * update(), the data isn't written to the database until the
239
     * transaction is committed, and that isn't until after the
240
     * method exits. Consequently, if you want to test writing to
241
     * the database, either use the {@literal @ExpectedDataSet}
242
     * annotation (that executes after the test is run), or use
243
     * {@link CdmTransactionalIntegrationTest}.
244
     *
245
     * @param out The OutputStream to write to.
246
     * @see FlatFullXmlWriter
247
     */
248
    public void printDataSetWithNull(OutputStream out) {
249
        printDataSetWithNull(out, null, null, null);
250
    }
251

    
252
    /**
253
     * @param out
254
     * @param excludeTermLoadingTables
255
     * @param excludeFilter the tables to be <em>excluded</em>
256
     */
257
    public void printDataSetWithNull(OutputStream out, Boolean excludeTermLoadingTables,
258
            ITableFilterSimple excludeFilterOrig, String[] includeTableNames) {
259

    
260
        ITableFilterSimple excludeFilter = excludeFilterOrig;
261
        if(excludeTermLoadingTables != null && excludeTermLoadingTables.equals(true)){
262
            ExcludeTableFilter excludeTableFilter = new ExcludeTableFilter();
263

    
264
            for(String tname : termLoadingTables){
265
                excludeTableFilter.excludeTable(tname);
266
            }
267
            excludeFilter = excludeTableFilter;
268
        }
269

    
270
        if( excludeFilter != null && includeTableNames != null){
271
            throw new RuntimeException("Ambiguous parameters: excludeFilter can not be used together with includeTableNames or excludeTermLoadingTable.");
272
        }
273

    
274
        IDatabaseConnection connection = null;
275
        try {
276
            connection = getConnection();
277
            IDataSet dataSet;
278
            if (includeTableNames != null) {
279
                dataSet = connection.createDataSet(includeTableNames);
280
            } else {
281
                if (excludeFilter == null){
282
                    excludeFilter = new ExcludeTableFilter();
283
                }
284
                dataSet = new DatabaseDataSet(connection, false, excludeFilter);
285
            }
286
            FlatFullXmlWriter writer = new FlatFullXmlWriter(out);
287
            writer.write(dataSet);
288
        } catch (Exception e) {
289
            logger.error("Error on writing dataset:", e);
290
        } finally {
291
            try {
292
                if (connection != null){
293
                    connection.close();
294
                }
295
            } catch (SQLException sqle) {
296
                logger.error(sqle);
297
            }
298
        }
299
    }
300

    
301
    /**
302
     *
303
     * @param out
304
     * @param formatString can be null, otherwise a format string like eg. "&lt; %1$s /&gt;" see also {@link String#format(String, Object...)}
305
     */
306
    public void printTableNames(OutputStream out, String formatString) {
307
        IDatabaseConnection connection = null;
308
        OutputStreamWriter writer = new OutputStreamWriter(out);
309

    
310
        try {
311
            connection = getConnection();
312
            IDataSet actualDataSet = connection.createDataSet();
313
            ITableIterator tableIterator = actualDataSet.iterator();
314
            String tableName = null;
315
            while(tableIterator.next()){
316
                tableName = tableIterator.getTable().getTableMetaData().getTableName();
317
                if(formatString != null){
318
                    tableName = String.format(formatString, tableName);
319
                }
320
                writer.append(tableName).append("\n");
321
            }
322
        } catch (Exception e) {
323
            logger.error(e);
324
        } finally {
325
            try {
326
                writer.close();
327
            } catch (IOException ioe) {
328
                logger.error(ioe);
329
            }
330
            try {
331
                if (connection != null){
332
                    connection.close();
333
                }
334
            } catch (SQLException sqle) {
335
                logger.error(sqle);
336
            }
337
        }
338
    }
339

    
340
    /**
341
     * Prints the named tables to an output stream, using dbunit's
342
     * {@link org.dbunit.dataset.xml.FlatXmlDataSet}.
343
     * <p>
344
     * <h2>NOTE: for compatibility with unitils 3.x you may
345
     * want to use the {@link #printDataSetWithNull(OutputStream)}
346
     * method instead.</h2>
347
     *
348
     * <p>
349
     * Remember, if you've just called save() or
350
     * update(), the data isn't written to the database until the
351
     * transaction is committed, and that isn't until after the
352
     * method exits. Consequently, if you want to test writing to
353
     * the database, either use the {@literal @ExpectedDataSet}
354
     * annotation (that executes after the test is run), or use
355
     * {@link CdmTransactionalIntegrationTest}.
356
     *
357
     * @see {@link #printDataSet(OutputStream)}
358
     * @see FlatFullXmlWriter
359
     *
360
     * @param out
361
     * 		the OutputStream to write the XML to
362
     * @param includeTableNames
363
     * 		the names of tables to print (should be in upper case letters)
364
     */
365
    public void printDataSet(OutputStream out, String[] includeTableNames) {
366
        IDatabaseConnection connection = null;
367

    
368
        if(includeTableNames == null){
369
            return;
370
        }
371

    
372
        try {
373
            connection = getConnection();
374
            IDataSet actualDataSet = connection.createDataSet(includeTableNames);
375
            FlatXmlDataSet.write(actualDataSet, out);
376

    
377
        } catch (Exception e) {
378
            logger.error(e);
379
        } finally {
380
            try {
381
                if (connection != null){
382
                    connection.close();
383
                }
384
            } catch (SQLException sqle) {
385
                logger.error(sqle);
386
            }
387
        }
388
    }
389

    
390
    /**
391
     * Prints the named tables to an output stream, using dbunit's
392
     * {@link org.dbunit.dataset.xml.FlatXmlWriter}.
393
     * <p>
394
     * <h2>NOTE: for compatibility with unitils 3.x you may
395
     * want to use the {@link #printDataSetWithNull(OutputStream)}
396
     * method instead.</h2>
397
     *
398
     * <p>
399
     * Remember, if you've just called save() or
400
     * update(), the data isn't written to the database until the
401
     * transaction is committed, and that isn't until after the
402
     * method exits. Consequently, if you want to test writing to
403
     * the database, either use the {@literal @ExpectedDataSet}
404
     * annotation (that executes after the test is run), or use
405
     * {@link CdmTransactionalIntegrationTest}.
406
     *
407
     * @see {@link #printDataSet(OutputStream)}
408
     * @see FlatFullXmlWriter
409
     * @param out
410
     * @param filter
411
     */
412
    public void printDataSet(OutputStream out, ITableFilterSimple filter) {
413
        if (filter == null){
414
            filter = new ExcludeTableFilter();
415
        }
416

    
417
        IDatabaseConnection connection = null;
418

    
419
        try {
420
            connection = getConnection();
421
//            FlatXmlDataSet.write(actualDataSet, out);
422

    
423
            IDataSet dataSet = new DatabaseDataSet(connection, false, filter);
424

    
425
            FlatXmlWriter writer = new FlatXmlWriter(out);
426
            writer.write(dataSet);
427

    
428
        } catch (Exception e) {
429
            logger.error(e);
430
        } finally {
431
            try {
432
                if (connection != null){
433
                    connection.close();
434
                }
435
            } catch (SQLException sqle) {
436
                logger.error(sqle);
437
            }
438
        }
439
    }
440

    
441

    
442
    /**
443
     * Prints a dtd to an output stream, using dbunit's
444
     * {@link org.dbunit.dataset.xml.FlatDtdDataSet}.
445
     *
446
     * @param out The OutputStream to write to.
447
     * @see org.dbunit.dataset.xml.FlatDtdDataSet
448
     */
449
    public void printDtd(OutputStream out) {
450
        IDatabaseConnection connection = null;
451

    
452
        try {
453
            connection = getConnection();
454
            IDataSet actualDataSet = connection.createDataSet();
455
            FlatDtdDataSet.write(actualDataSet, out);
456
        } catch (Exception e) {
457
            logger.error(e);
458
        } finally {
459
            try {
460
                if (connection != null){
461
                    connection.close();
462
                }
463
            } catch (SQLException sqle) {
464
                logger.error(sqle);
465
            }
466
        }
467
    }
468

    
469
    /**
470
     * <b>WARNING</b> read this doc before using this method.
471
     * <p>
472
     * This is the recommended method to create DbUnit data set for integration tests.
473
     * This method will create the DbUnit data set file for the specific test class
474
     * it has been called from. This happens in full compliance with the DbUnit conventions,
475
     * so that the test class will immediately be able to use the
476
     * newly created file during the next run.
477
     * This also means that using <code>writeDbUnitDataSetFile()</code>
478
     * in a test class <b>will overwrite the existing data set file</b>. This is not
479
     * considered to be harmful since we expect that any development always is
480
     * backed up by a VCS like git, svn and therefore recovery of overwritten
481
     * data source files should not cause any problems.
482
     * <p>
483
     * Writes a  DbUnit dataset file from the current data base connection using the
484
     * {@link FlatFullXmlWriter} which is a variant of the
485
     * {@link org.dbunit.dataset.xml.FlatXmlWriter}. It
486
     * inserts <code>'[null]'</code> place holders for null values instead of skipping them.
487
     *
488
     *
489
     * @param includeTableNames
490
     * @throws FileNotFoundException
491
     */
492
    public void writeDbUnitDataSetFile(String[] includeTableNames) throws FileNotFoundException {
493
        writeDbUnitDataSetFile(includeTableNames, null);
494
    }
495

    
496
    /**
497
     *
498
     * Extension of method mentioned in "see also" where you can specify an appendix for the
499
     * generated DbUnit data set file.
500
     *
501
     * @param includeTableNames
502
     * @param fileAppendix the appendix of the generated DbUnit dataset file
503
     * @throws FileNotFoundException
504
     * @see #writeDbUnitDataSetFile(String[])
505
     */
506
    public void writeDbUnitDataSetFile(String[] includeTableNames, String fileAppendix) throws FileNotFoundException {
507

    
508
        String pathname = "src" + File.separator + "test" + File.separator + "resources" + File.separator + this.getClass().getName().replace(".", File.separator);
509
        if(fileAppendix!=null){
510
            pathname += "."+fileAppendix;
511
        }
512
        pathname += ".xml";
513
        File file = new File(pathname);
514

    
515
        if (file.exists()){
516
            logger.warn("** OVERWRITING DbUnit dataset file " + file.getAbsolutePath());
517
        } else {
518
            logger.warn("Writing new DbUnit dataset file to " + file.getAbsolutePath());
519
        }
520

    
521
        printDataSetWithNull(
522
            new FileOutputStream(file),
523
            false,
524
            null,
525
            includeTableNames
526
         );
527
    }
528

    
529
    /**
530
     * Transforms a javax.xml.transform.Source to a java.lang.String (useful for comparison in
531
     * XmlUnit tests etc).
532
     *
533
     * @param source
534
     * @return
535
     * @throws TransformerException
536
     */
537
    protected String transformSourceToString(Source source) throws TransformerException {
538
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
539
        Transformer transformer = transformerFactory.newTransformer();
540
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
541
        Result result = new StreamResult(outputStream);
542
        transformer.transform(source, result);
543

    
544
        return new String(outputStream.toByteArray());
545
    }
546

    
547

    
548
    /**
549
     * This is the common method to create test data xml files for integration tests.
550
     *
551
     * With {@link CdmTransactionalIntegrationTestExample#createTestDataSet} an example
552
     * implementation of this method is provided.
553
     *
554
     * @throws FileNotFoundException
555
     */
556
    public abstract void createTestDataSet() throws FileNotFoundException;
557
}
(1-1/6)