package marubinotto.piggydb.impl.db;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import marubinotto.piggydb.impl.PigDump;
import marubinotto.util.RdbUtils;
import marubinotto.util.procedure.Procedure;
import marubinotto.util.procedure.Transaction;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dbunit.dataset.DataSetException;
import org.springframework.beans.factory.InitializingBean;
public class H2DbUpgrade implements InitializingBean {
private static Log log = LogFactory.getLog(H2DbUpgrade.class);
private H2JdbcUrl h2JdbcUrl;
private DataSource dataSource;
private String username;
private String password;
private Transaction transaction;
private DatabaseSchema databaseSchema;
private File databaseDir;
private String oldVersion;
private String newVersion;
public H2DbUpgrade() {
}
public void setH2JdbcUrl(H2JdbcUrl h2JdbcUrl) {
this.h2JdbcUrl = h2JdbcUrl;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public void setDatabaseSchema(DatabaseSchema databaseSchema) {
this.databaseSchema = databaseSchema;
}
public void afterPropertiesSet() throws Exception {
if (!isDatabaseFileFormatVersion1_1()) {
log.debug("No need for upgrading: the database file format is not v1.1");
return;
}
log.info("Upgrading the H2 database files ...");
File exportFilePath = exportDatabase();
renameDatabaseFiles(exportFilePath);
restoreDatabaseWithNewVersion(exportFilePath);
log.info("Completed upgrading the database: " + this.oldVersion + " -> " + this.newVersion);
}
protected boolean isDatabaseFileFormatVersion1_1() throws MalformedURLException {
String databasePath = this.h2JdbcUrl.getDatabasePath();
if (!databasePath.startsWith("file:")) return false;
this.databaseDir = FileUtils.toFile(new URL(this.h2JdbcUrl.getDatabasePrefix()));
if (!this.databaseDir.isDirectory()) return false;
if (FileUtils.toFile(new URL(databasePath + ".h2.db")).isFile()) return false;
if (!FileUtils.toFile(new URL(databasePath + ".data.db")).isFile()) return false;
return true;
}
private static final String JDBC_URL_PREFIX_V1_1 = "jdbc:h2v1_1:";
protected Connection connectWithVersion1_1()
throws SQLException, IOException, ClassNotFoundException {
// DB_CLOSE_DELAY=0 because it will avoid file locking after exporting
String url = JDBC_URL_PREFIX_V1_1 +
this.h2JdbcUrl.getDatabasePath() + ";DB_CLOSE_DELAY=0";
log.info("Connecting to the v1.1 database: " + url);
Class.forName("org.h2.upgrade.v1_1.Driver");
Properties info = new Properties();
info.setProperty("user", this.username);
info.setProperty("password", this.password);
return DriverManager.getConnection(url, info);
}
protected File getExportFilePath() throws MalformedURLException {
return FileUtils.toFile(new URL(this.h2JdbcUrl.getDatabasePath() + ".dump.xml"));
}
protected File exportDatabase()
throws SQLException, IOException, DataSetException, ClassNotFoundException {
File exportFilePath = getExportFilePath();
exportFilePath.delete();
log.info("Exporting the old dababase to: " + exportFilePath);
Connection connection = connectWithVersion1_1();
this.oldVersion = connection.getMetaData().getDatabaseProductVersion();
log.info("oldVersion: " + this.oldVersion);
OutputStream output = new BufferedOutputStream(new FileOutputStream(exportFilePath, false));
try {
RdbUtils.exportAsXml(connection, PigDump.TABLES, output);
}
finally {
output.close();
connection.close();
}
return exportFilePath;
}
private static final String BACKUP_SUFFIX = ".v1_1";
protected void renameDatabaseFiles(File exportFilePath) {
log.info("Renaming the old database files ...");
String databaseName = this.h2JdbcUrl.getDatabaseName();
for (File file : this.databaseDir.listFiles()) {
if (file.isDirectory()) continue;
if (file.equals(exportFilePath)) continue;
if (file.getName().startsWith(databaseName + ".")) {
file.renameTo(new File(file.getPath() + BACKUP_SUFFIX));
}
}
}
protected void restoreDatabaseWithNewVersion(File exportFilePath) throws Exception {
log.info("Restoring the database with new version ...");
final InputStream fileInput = FileUtils.openInputStream(exportFilePath);
try {
this.transaction.execute(new Procedure() {
public Object execute(Object input) throws Exception {
databaseSchema.update();
log.info("Importing the exported XML file ...");
Connection connection = RdbUtils.getSpringTransactionalConnection(dataSource);
newVersion = connection.getMetaData().getDatabaseProductVersion();
log.info("newVersion: " + newVersion);
RdbUtils.cleanImportXml(connection, fileInput);
return null;
}
});
}
finally {
fileInput.close();
}
}
}