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