/**
* Copyright (c) 2009 - 2012 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package org.candlepin.junit;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Collections;
import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
//import org.hibernate.ejb.Ejb3Configuration;
import org.junit.rules.ExternalResource;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.LiquibaseException;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.sql.visitor.SqlVisitor;
import liquibase.statement.SqlStatement;
import liquibase.statement.core.RawSqlStatement;
/**
* This Rule builds an HSQL Database based on the changelog file it is constructed with.
* In order to speed up the test runs, this class attempts to only run the DDL at the beginning
* of a test suite and simply truncates the schema before each test. Therefore,
* you should tag this rule as both a ClassRule and a Rule (requires JUnit >= 4.12) so that
* it will be invoked when the test suite begins and before every test. Additionally, the
* changelog run should not contain any DML statements as those would be lost after the
* tables are truncated.
*/
public class CandlepinLiquibaseResource extends ExternalResource {
private Liquibase liquibase;
private ResourceAccessor accessor;
private Database database;
private static final String TRUNCATE_SQL =
"TRUNCATE SCHEMA %s RESTART IDENTITY AND COMMIT NO CHECK";
private static final String DROP_SQL =
"DROP SCHEMA %s CASCADE";
public CandlepinLiquibaseResource() {
this("db/changelog/changelog-testing.xml");
}
public CandlepinLiquibaseResource(String changelogFile) {
try {
String connectionUrl = getJdbcUrl("testing");
Connection jdbcConnection = DriverManager.getConnection(connectionUrl, "sa", "");
DatabaseConnection conn = new JdbcConnection(jdbcConnection);
database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn);
accessor = new ClassLoaderResourceAccessor();
liquibase = new Liquibase(changelogFile, accessor, database);
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Override
public Statement apply(final Statement base, Description description) {
if (description.getChildren().isEmpty()) {
// Test level clean up
return new Statement() {
@Override
public void evaluate() throws Throwable {
/* Strictly speaking, calling runUpdate() here would be more correct.
* However, runUpdate() is a pretty expensive operation and imposes a
* significant performance hit. If the changelog for testing is ever
* modified to run DML statements, we'll have to revisit the issue.
* Currently the changelog only runs DDL not DML so we can get away with
* truncating the table after each test and not re-running runUpdate().
*/
try {
base.evaluate();
}
finally {
truncatePublicSchema();
}
}
};
}
else {
return super.apply(base, description);
}
}
private String getJdbcUrl(String persistenceUnit) {
for (ParsedPersistenceXmlDescriptor unit :
PersistenceXmlParser.locatePersistenceUnits(Collections.emptyMap())) {
if (unit.getName().equals("testing")) {
return unit.getProperties().getProperty("hibernate.connection.url");
}
}
throw new RuntimeException("Couldn't locate persistence unit [testing] in your persistence.xml!");
}
@Override
protected void before() throws Throwable {
createLiquibaseSchema();
runUpdate();
}
@Override
protected void after() {
dropPublicSchema();
dropLiquibaseSchema();
}
public void runUpdate() throws LiquibaseException {
liquibase.update("test");
}
private void exec(String sql) {
SqlStatement s = new RawSqlStatement(sql);
try {
database.execute(new SqlStatement[] { s }, Collections.<SqlVisitor>emptyList());
}
catch (LiquibaseException e) {
throw new RuntimeException(e);
}
}
public void dropPublicSchema() {
exec(String.format(DROP_SQL, "PUBLIC"));
}
public void dropLiquibaseSchema() {
exec(String.format(DROP_SQL, "LIQUIBASE"));
}
public void truncatePublicSchema() {
exec(String.format(TRUNCATE_SQL, "PUBLIC"));
}
public void truncateLiquibaseSchema() {
exec(String.format(TRUNCATE_SQL, "LIQUIBASE"));
}
public void createLiquibaseSchema() {
exec("CREATE SCHEMA LIQUIBASE");
database.setLiquibaseSchemaName("LIQUIBASE");
}
}