Automated database migration with Izpack and Liquibase

In a previous blog article, Willem Dekker explained how to use izpack to build an application installer. In my current project I thought it would be nice to use such an installer and let it execute the liquibase migration scripts automatically during installation. In this article I will show a way to achieve this goal.

The project I work on uses the Java Repository Bridge (Jarb) to address database related problems. One of the features of Jarb is that it uses liquibase migrations to migrate the database schema over different application versions. For each new application version that is released there are zero or more groovy scripts that describe the needed database upgrade. Combined with the fact that I like the idea of providing the systems department with an izpack installer instead of a deployment manual, I decided to extend the installer with automated database migration.

Four steps to automated migration


1. The java main class to run the liquibase migrations


Jarb uses the org.jarb.migrations.LiquibaseMigrator to execute the liquibase groovy scripts. Our main class will make use of this class aswell:
public class LiquibaseMigratorMain {

public static void main(String[] args)
throws SQLException {
LiquibaseMigratorMainArgs params =
new LiquibaseMigratorMainArgs();
JCommander cmd = new JCommander(params);
Connection conn = null;
try {
cmd.parse(args);
conn = params.createConnection();
DatabaseMigrator migrator =
params.createMigrator();
migrator.migrate(conn);
conn.commit();
} catch (ParameterException pe) {
cmd.usage();
} finally {
JdbcUtils.closeQuietly(conn);
}
}
}

The commandline arguments are parsed using JCommander:
@Parameters(separators = "= ")
public class LiquibaseMigratorMainArgs {
@Parameter(names = "-driverClass", required = true,
description = "Fully qualified name of the"+
"jdbc driver class")
private String driverClassName;

@Parameter(names = "-dbUrl", required = true,
description = "Database url")
private String dbUrl;

@Parameter(names = "-dbUser", required = true,
description = "Database username")
private String dbUserName;

@Parameter(names = "-dbPassword", required = true,
description = "Database password")
private String dbPassword;

@Parameter(names = "-changelogPath", required = true,
description = "Liquibase changelog path")
private String changelogPath;

@Parameter(names = "-sqlLogPath", required = false,
description = "Sql log filepath (optional)")
private String sqlLogPath;

// getters omitted

/**
* Factory method that creates a DatabaseMigrator based
* on given args.
* @return DatabaseMigrator
*/
public DatabaseMigrator createMigrator() {
LiquibaseMigrator migrator =
new LiquibaseMigrator();
migrator.setChangeLogPath(getChangelogPath());
migrator.setOutputFilePath(getSqlLogPath());
return migrator;
}

/**
* Factory method that creates a sql Connection based
* on given args.
* @return Connection
*/
public Connection createConnection() {
Connection conn = null;
try {
// register appropriate jdbc driver
Class.forName(getDriverClassName());
conn = DriverManager.getConnection(
getDbUrl(), getDbUserName(),
getDbPassword());
} catch (SQLException sqle) {
throw new RuntimeException(sqle);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
return conn;
}
}

2. An executable jar


The java main class is packed in an executable jar with all its dependencies (a.o: Jarb and JCommander) during the maven build by the maven assembly plugin.

The jar is called dbmigrator.jar and will be used in the next step.

3. The script to be used by izpack


As the target environment will be a Windows 2003 server, we had to create a batch file to fire the java main (dollar prefixed strings are user provided variables during izpack install):
call java -classpath "$tomcat_install_path\lib\*";^
"$tomcat_install_path\db\dbmigrator.jar";^
"$tomcat_install_path\db" ^
com.mycompany.myproject.support.commandline.LiquibaseMigratorMain ^
-changelogPath "$tomcat_install_path\db\changelog.groovy" ^
-dbPassword $database.migrator.password ^
-dbUrl "$database.url" ^
-dbUser $database.migrator.username ^
-driverClass $jdbc.driverclass ^
-sqlLogPath "$tomcat_install_path\db\sql.txt"

The script is called migrate.bat and will be used next step.

4. IzPack configuration


IzPack is configured with several “packs” where each such pack stands for a step in the installation (e.g. tomcat-installation and database-migration). The database migration pack is configured as follows:
            Execute database migration scripts

Jarb future release


Soon, steps 1 and 2 won’t be necessary anymore, Jarb will integrate the java main in one of its jar files!  By that time, implementing automatic database migration with liquibase and izPack will be nothing more that providing the script to execute the Jarb main with correct parameters and a simple izPack configuration to wrap it all together.