/*******************************************************************************
* Copyright (c) 2014 antoniomariasanchez at gmail.com.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* antoniomaria - initial API and implementation
******************************************************************************/
package net.sf.gazpachoquest.extractor.dbunit;
import java.io.FileOutputStream;
import java.sql.Connection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.DataSource;
import org.dbunit.database.AmbiguousTableNameException;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.database.search.TablesDependencyHelper;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.FlatXmlWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Extracts a DBUnit flat XML dataset from a database.
*
* @author Bill Siggelkow
*/
public class DBUnitDataExtractor {
private static final Logger logger = LoggerFactory.getLogger(DBUnitDataExtractor.class);
/**
* A regular expression that is used to get the table name from a SQL
* 'select' statement. This pattern matches a
* string that starts with any characters, followed by the case-insensitive
* word 'from', followed by a table name of
* the form 'foo' or 'schema.foo', followed by any number of remaining
* characters.
*/
private static final Pattern TABLE_MATCH_PATTERN = Pattern.compile("from\\s(?<tableName>\\w+)");
private String dataSetName = "dbunit-dataset.xml";
private DataSource dataSource;
private Map dbUnitFeatures;
private Map dbUnitProperties;
private List<String> queryList;
private String schema;
private List<String> tableList;
/**
* Performs the extraction. If no tables or queries are specified, data from
* entire database will be extracted.
* Otherwise, a partial extraction will be performed.
*
* @throws Exception
*/
public void extract() throws Exception {
Connection conn = null;
try {
conn = dataSource.getConnection();
logger.info("Beginning extraction from '" + conn.toString() + "'.");
IDatabaseConnection connection = new DatabaseConnection(conn, schema);
configConnection((DatabaseConnection) connection);
if (tableList != null || queryList != null) {
// partial database export
QueryDataSet partialDataSet = new QueryDataSet(connection);
addTables(partialDataSet);
addQueries(partialDataSet);
FlatXmlDataSet.write(partialDataSet, new FileOutputStream(dataSetName));
} else {
// full database export
IDataSet fullDataSet = connection.createDataSet();
FlatXmlDataSet.write(fullDataSet, new FileOutputStream(dataSetName));
// dependent tables database export: export table X and all
// tables that
// have a PK which is a FK on X, in the right order for
// insertion
String[] depTableNames = TablesDependencyHelper.getAllDependentTables(connection, "research");
IDataSet depDataset = connection.createDataSet(depTableNames);
FlatXmlWriter datasetWriter = new FlatXmlWriter(new FileOutputStream("target/dependents.xml"));
datasetWriter.write(depDataset);
}
} finally {
if (conn != null) {
conn.close();
}
}
logger.info("Completed extraction to '" + dataSetName + "'.");
}
/**
* Name of the XML file that will be created. Defaults to
* <code>dbunit-dataset.xml</code>.
*
* @param name
* file name.
*/
public void setDataSetName(final String name) {
dataSetName = name;
}
/**
* The data source of the database from which the data will be extracted.
* This property is required.
*
* @param ds
*/
public void setDataSource(final DataSource ds) {
dataSource = ds;
}
public void setDbUnitFeatures(final Map dbUnitFeatures) {
this.dbUnitFeatures = dbUnitFeatures;
}
public void setDbUnitProperties(final Map dbUnitProperties) {
this.dbUnitProperties = dbUnitProperties;
}
/**
* List of SQL queries (i.e. 'select' statements) that will be used executed
* to retrieve the data to be extracted.
* If the table being queried is also specified in the
* <code>tableList</code> property, the query will be ignored
* and all rows will be extracted from that table.
*
* @param list
* of SQL queries.
*/
public void setQueryList(final List<String> list) {
queryList = list;
}
/**
* Set the schema.
*
* @param schema
*/
public void setSchema(final String schema) {
this.schema = schema;
}
/**
* List of table names to extract data from.
*
* @param list
* of table names.
*/
public void setTableList(final List<String> list) {
tableList = list;
}
private void addQueries(final QueryDataSet dataSet) throws AmbiguousTableNameException {
if (queryList == null) {
return;
}
for (Object element : queryList) {
String query = (String) element;
Matcher m = TABLE_MATCH_PATTERN.matcher(query);
if (!m.matches()) {
logger.warn("Unable to parse query. Ignoring '" + query + "'.");
} else {
String table = m.group(1);
// only add if the table has not been added
if (tableList != null && tableList.contains(table)) {
logger.warn("Table '" + table + "' already added. Ignoring '" + query + "'.");
} else {
dataSet.addTable(table, query);
}
}
}
}
private void addTables(final QueryDataSet dataSet) throws AmbiguousTableNameException {
if (tableList == null) {
return;
}
for (Object element : tableList) {
String table = (String) element;
dataSet.addTable(table);
}
}
private void configConnection(final DatabaseConnection conn) {
DatabaseConfig config = conn.getConfig();
if (dbUnitProperties != null) {
for (Iterator k = dbUnitProperties.entrySet().iterator(); k.hasNext();) {
Map.Entry entry = (Map.Entry) k.next();
String name = (String) entry.getKey();
Object value = entry.getValue();
config.setProperty(name, value);
}
}
if (dbUnitFeatures != null) {
for (Iterator k = dbUnitFeatures.entrySet().iterator(); k.hasNext();) {
Map.Entry entry = (Map.Entry) k.next();
String name = (String) entry.getKey();
boolean value = Boolean.valueOf((String) entry.getValue()).booleanValue();
// config.setFeature(name, value);
config.setProperty(name, value);
}
}
}
}