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