View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2004, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  package org.dbunit.ant;
22  
23  import java.sql.Connection;
24  import java.sql.Driver;
25  import java.sql.SQLException;
26  import java.util.ArrayList;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Properties;
30  
31  import org.apache.tools.ant.AntClassLoader;
32  import org.apache.tools.ant.BuildException;
33  import org.apache.tools.ant.Project;
34  import org.apache.tools.ant.Task;
35  import org.apache.tools.ant.types.Path;
36  import org.apache.tools.ant.types.Reference;
37  import org.dbunit.DatabaseUnitException;
38  import org.dbunit.database.DatabaseConfig;
39  import org.dbunit.database.DatabaseConnection;
40  import org.dbunit.database.IDatabaseConnection;
41  import org.dbunit.dataset.datatype.IDataTypeFactory;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  /**
46   * <code>DbUnitTask</code> is the task definition for an Ant
47   * interface to <code>DbUnit</code>.   DbUnit is a JUnit extension
48   * which sets your database to a known state before executing your
49   * tasks.
50   *
51   * @author Timothy Ruppert
52   * @author Ben Cox
53   * @version $Revision$
54   * @since Jun 10, 2002
55   * @see org.apache.tools.ant.Task
56   */
57  public class DbUnitTask extends Task
58  {
59      private static final Logger logger = LoggerFactory.getLogger(DbUnitTask.class);
60  
61      /**
62       * Database connection
63       */
64      private Connection conn = null;
65  
66      /**
67       * DB driver.
68       */
69      private String driver = null;
70  
71      /**
72       * DB url.
73       */
74      private String url = null;
75  
76      /**
77       * User name.
78       */
79      private String userId = null;
80  
81      /**
82       * Password
83       */
84      private String password = null;
85  
86      /**
87       * DB schema.
88       */
89      private String schema = null;
90  
91      /**
92       * Steps
93       */
94      private List steps = new ArrayList();
95  
96      private Path classpath;
97  
98      private AntClassLoader loader;
99  
100     /**
101      * DB configuration child element to configure {@link DatabaseConfig} properties
102      * in a generic way.
103      */
104     private DbConfig dbConfig;
105 
106     /**
107      * Flag for using the qualified table names.
108      * @deprecated since 2.4. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
109      */
110     @Deprecated
111     private Boolean useQualifiedTableNames = null;
112 
113     /**
114      * Flag for using batched statements.
115      * @deprecated since 2.4. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
116      */
117     @Deprecated
118     private Boolean supportBatchStatement = null;
119 
120     /**
121      * Flag for datatype warning.
122      * @deprecated since 2.4. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
123      */
124     @Deprecated
125     private Boolean datatypeWarning = null;
126 
127     /**
128      * @deprecated since 2.4. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
129      */
130     @Deprecated
131     private String escapePattern = null;
132 
133     /**
134      * @deprecated since 2.4. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
135      */
136     @Deprecated
137     private String dataTypeFactory = null;
138 
139     /**
140      * @deprecated since 2.4. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
141      */
142     @Deprecated
143     private String batchSize = null;
144 
145     /**
146      * @deprecated since 2.4. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
147      */
148     @Deprecated
149     private String fetchSize = null;
150 
151     /**
152      * @deprecated since 2.4. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
153      */
154     @Deprecated
155     private Boolean skipOracleRecycleBinTables = null;
156 
157     /**
158      * @deprecated since 2.5.1. Use {@link #dbConfig} instead. Only here because of backwards compatibility should be removed in the next major release.
159      */
160     @Deprecated
161     private Boolean allowEmptyFields = null;
162 
163     /**
164      * Set the JDBC driver to be used.
165      */
166     public void setDriver(final String driver)
167     {
168         logger.trace("setDriver(driver={}) - start", driver);
169         this.driver = driver;
170     }
171 
172     /**
173      * Set the DB connection url.
174      */
175     public void setUrl(final String url)
176     {
177         logger.trace("setUrl(url={}) - start", url);
178         this.url = url;
179     }
180 
181     /**
182      * Set the user name for the DB connection.
183      */
184     public void setUserid(final String userId)
185     {
186         logger.trace("setUserid(userId={}) - start", userId);
187         this.userId = userId;
188     }
189 
190     /**
191      * Set the password for the DB connection.
192      */
193     public void setPassword(final String password)
194     {
195         logger.trace("setPassword(password=*****) - start");
196         this.password = password;
197     }
198 
199     /**
200      * Set the schema for the DB connection.
201      */
202     public void setSchema(final String schema)
203     {
204         logger.trace("setSchema(schema={}) - start", schema);
205         this.schema = schema;
206     }
207 
208     /**
209      * Set the flag for using the qualified table names.
210      */
211     public void setUseQualifiedTableNames(final Boolean useQualifiedTableNames)
212     {
213         logger.trace("setUseQualifiedTableNames(useQualifiedTableNames={}) - start", String.valueOf(useQualifiedTableNames));
214         this.useQualifiedTableNames = useQualifiedTableNames;
215     }
216 
217     /**
218      * Set the flag for supporting batch statements.
219      * NOTE: This property cannot be used to force the usage of batch
220      *       statement if your database does not support it.
221      */
222     public void setSupportBatchStatement(final Boolean supportBatchStatement)
223     {
224         logger.trace("setSupportBatchStatement(supportBatchStatement={}) - start", String.valueOf(supportBatchStatement));
225         this.supportBatchStatement = supportBatchStatement;
226     }
227 
228     public void setDatatypeWarning(final Boolean datatypeWarning)
229     {
230         logger.trace("setDatatypeWarning(datatypeWarning={}) - start", String.valueOf(datatypeWarning));
231         this.datatypeWarning = datatypeWarning;
232     }
233 
234     public void setDatatypeFactory(final String datatypeFactory)
235     {
236         logger.trace("setDatatypeFactory(datatypeFactory={}) - start", datatypeFactory);
237         this.dataTypeFactory = datatypeFactory;
238     }
239 
240     public void setEscapePattern(final String escapePattern)
241     {
242         logger.trace("setEscapePattern(escapePattern={}) - start", escapePattern);
243         this.escapePattern = escapePattern;
244     }
245 
246     public DbConfig getDbConfig()
247     {
248         return dbConfig;
249     }
250 
251     //    public void setDbConfig(DbConfig dbConfig)
252     //    {
253     //        logger.debug("setDbConfig(dbConfig={}) - start", dbConfig);
254     //        this.dbConfig = dbConfig;
255     //    }
256 
257     public void addDbConfig(final DbConfig dbConfig)
258     {
259         logger.trace("addDbConfig(dbConfig={}) - start", dbConfig);
260         this.dbConfig = dbConfig;
261     }
262 
263     /**
264      * Set the classpath for loading the driver.
265      */
266     public void setClasspath(final Path classpath)
267     {
268         logger.trace("setClasspath(classpath={}) - start", classpath);
269         if (this.classpath == null)
270         {
271             this.classpath = classpath;
272         }
273         else
274         {
275             this.classpath.append(classpath);
276         }
277     }
278 
279     /**
280      * Create the classpath for loading the driver.
281      */
282     public Path createClasspath()
283     {
284         logger.trace("createClasspath() - start");
285 
286         if (this.classpath == null)
287         {
288             this.classpath = new Path(getProject());
289         }
290         return this.classpath.createPath();
291     }
292 
293     /**
294      * Set the classpath for loading the driver using the classpath reference.
295      */
296     public void setClasspathRef(final Reference r)
297     {
298         logger.trace("setClasspathRef(r={}) - start", r);
299 
300         createClasspath().setRefid(r);
301     }
302 
303     /**
304      * Gets the Steps.
305      */
306     public List getSteps()
307     {
308         return steps;
309     }
310 
311     /**
312      * Adds an Operation.
313      */
314     public void addOperation(final Operation operation)
315     {
316         logger.trace("addOperation({}) - start", operation);
317 
318         steps.add(operation);
319     }
320 
321     /**
322      * Adds a Compare to the steps List.
323      */
324     public void addCompare(final Compare compare)
325     {
326         logger.trace("addCompare({}) - start", compare);
327 
328         steps.add(compare);
329     }
330 
331     /**
332      * Adds an Export to the steps List.
333      */
334     public void addExport(final Export export)
335     {
336         logger.trace("addExport(export={}) - start", export);
337 
338         steps.add(export);
339     }
340 
341 
342     public String getBatchSize()
343     {
344         return batchSize;
345     }
346 
347     /**
348      * sets the size of batch inserts.
349      * @param batchSize
350      */
351     public void setBatchSize(final String batchSize)
352     {
353         this.batchSize = batchSize;
354     }
355 
356 
357     public String getFetchSize()
358     {
359         return fetchSize;
360     }
361 
362     public void setFetchSize(final String fetchSize)
363     {
364         this.fetchSize = fetchSize;
365     }
366 
367     public void setSkipOracleRecycleBinTables(final Boolean skipOracleRecycleBinTables)
368     {
369         this.skipOracleRecycleBinTables = skipOracleRecycleBinTables;
370     }
371 
372     /**
373      * Load the step and then execute it.
374      */
375     @Override
376     public void execute() throws BuildException
377     {
378         logger.trace("execute() - start");
379 
380         try
381         {
382             final IDatabaseConnection connection = createConnection();
383 
384             final Iterator stepIter = steps.listIterator();
385             while (stepIter.hasNext())
386             {
387                 final DbUnitTaskStep step = (DbUnitTaskStep)stepIter.next();
388                 log(step.getLogMessage(), Project.MSG_INFO);
389                 step.execute(connection);
390             }
391         }
392         catch (DatabaseUnitException | SQLException e)
393         {
394             throw new BuildException(e, getLocation());
395         }
396         finally
397         {
398             try
399             {
400                 if (conn != null)
401                 {
402                     conn.close();
403                 }
404             }
405             catch (final SQLException e)
406             {
407                 logger.error("execute()", e);
408             }
409         }
410     }
411 
412     protected IDatabaseConnection createConnection() throws SQLException
413     {
414         logger.trace("createConnection() - start");
415 
416         if (driver == null)
417         {
418             throw new BuildException("Driver attribute must be set!", getLocation());
419         }
420         if (userId == null)
421         {
422             throw new BuildException("User Id attribute must be set!", getLocation());
423         }
424         if (password == null)
425         {
426             throw new BuildException("Password attribute must be set!", getLocation());
427         }
428         if (url == null)
429         {
430             throw new BuildException("Url attribute must be set!", getLocation());
431         }
432         if (steps.size() == 0)
433         {
434             throw new BuildException("Must declare at least one step in a <dbunit> task!", getLocation());
435         }
436 
437         // Instantiate JDBC driver
438         Driver driverInstance = null;
439         try
440         {
441             Class dc;
442             if (classpath != null)
443             {
444                 log("Loading " + driver + " using AntClassLoader with classpath " + classpath,
445                         Project.MSG_VERBOSE);
446 
447                 loader = new AntClassLoader(getProject(), classpath);
448                 dc = loader.loadClass(driver);
449             }
450             else
451             {
452                 log("Loading " + driver + " using system loader.", Project.MSG_VERBOSE);
453                 dc = Class.forName(driver);
454             }
455             driverInstance = (Driver)dc.newInstance();
456         }
457         catch (final ClassNotFoundException e)
458         {
459             throw new BuildException("Class Not Found: JDBC driver "
460                     + driver + " could not be loaded", e, getLocation());
461         }
462         catch (final IllegalAccessException e)
463         {
464             throw new BuildException("Illegal Access: JDBC driver "
465                     + driver + " could not be loaded", e, getLocation());
466         }
467         catch (final InstantiationException e)
468         {
469             throw new BuildException("Instantiation Exception: JDBC driver "
470                     + driver + " could not be loaded", e, getLocation());
471         }
472 
473         log("connecting to " + url, Project.MSG_VERBOSE);
474         final Properties info = new Properties();
475         info.put("user", userId);
476         info.put("password", password);
477         conn = driverInstance.connect(url, info);
478 
479         if (conn == null)
480         {
481             // Driver doesn't understand the URL
482             throw new SQLException("No suitable Driver for " + url);
483         }
484         conn.setAutoCommit(true);
485 
486         final IDatabaseConnection connection = createDatabaseConnection(conn, schema);
487         return connection;
488     }
489 
490     /**
491      * Creates the dbunit connection using the two given arguments. The configuration
492      * properties of the dbunit connection are initialized using the fields of this class.
493      *
494      * @param jdbcConnection
495      * @param dbSchema
496      * @return The dbunit connection
497      */
498     protected IDatabaseConnection createDatabaseConnection(final Connection jdbcConnection,
499             final String dbSchema)
500     {
501         logger.trace("createDatabaseConnection(jdbcConnection={}, dbSchema={}) - start", jdbcConnection, dbSchema);
502 
503         IDatabaseConnection connection = null;
504         try
505         {
506             connection = new DatabaseConnection(jdbcConnection, dbSchema);
507         }
508         catch(final DatabaseUnitException e)
509         {
510             throw new BuildException("Could not create dbunit connection object", e);
511         }
512         final DatabaseConfig config = connection.getConfig();
513 
514         if(this.dbConfig != null){
515             try {
516                 this.dbConfig.copyTo(config);
517             }
518             catch(final DatabaseUnitException e)
519             {
520                 throw new BuildException("Could not populate dbunit config object", e, getLocation());
521             }
522         }
523 
524         // For backwards compatibility (old mode overrides the new one) copy the other attributes to the config
525         copyAttributes(config);
526 
527         log("Created connection for schema '" + schema + "' with config: " + config, Project.MSG_VERBOSE);
528 
529         return connection;
530     }
531 
532     /**
533      * @param config
534      * @deprecated since 2.4. Only here because of backwards compatibility should be removed in the next major release.
535      */
536     @Deprecated
537     private void copyAttributes(final DatabaseConfig config)
538     {
539         if(supportBatchStatement!=null)
540             config.setFeature(DatabaseConfig.FEATURE_BATCHED_STATEMENTS, supportBatchStatement.booleanValue());
541         if(useQualifiedTableNames!=null)
542             config.setFeature(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, useQualifiedTableNames.booleanValue());
543         if(datatypeWarning!=null)
544             config.setFeature(DatabaseConfig.FEATURE_DATATYPE_WARNING, datatypeWarning.booleanValue());
545         if(skipOracleRecycleBinTables!=null)
546             config.setFeature(DatabaseConfig.FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES, skipOracleRecycleBinTables.booleanValue());
547         if(allowEmptyFields!=null)
548             config.setFeature(DatabaseConfig.FEATURE_ALLOW_EMPTY_FIELDS, allowEmptyFields.booleanValue());
549 
550         if(escapePattern!=null)
551         {
552             config.setProperty(DatabaseConfig.PROPERTY_ESCAPE_PATTERN, escapePattern);
553         }
554         if (batchSize != null)
555         {
556             final Integer batchSizeInteger = new Integer(batchSize);
557             config.setProperty(DatabaseConfig.PROPERTY_BATCH_SIZE, batchSizeInteger);
558         }
559         if (fetchSize != null)
560         {
561             config.setProperty(DatabaseConfig.PROPERTY_FETCH_SIZE, new Integer(fetchSize));
562         }
563 
564         // Setup data type factory
565         if(this.dataTypeFactory!=null) {
566             try
567             {
568                 final IDataTypeFactory dataTypeFactory = (IDataTypeFactory)Class.forName(
569                         this.dataTypeFactory).newInstance();
570                 config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, dataTypeFactory);
571             }
572             catch (final ClassNotFoundException e)
573             {
574                 throw new BuildException("Class Not Found: DataType factory "
575                         + driver + " could not be loaded", e, getLocation());
576             }
577             catch (final IllegalAccessException e)
578             {
579                 throw new BuildException("Illegal Access: DataType factory "
580                         + driver + " could not be loaded", e, getLocation());
581             }
582             catch (final InstantiationException e)
583             {
584                 throw new BuildException("Instantiation Exception: DataType factory "
585                         + driver + " could not be loaded", e, getLocation());
586             }
587         }
588 
589     }
590 }