diff --git a/src/main/resources/migration/1_0.sql b/src/main/resources/migration/1_0.sql new file mode 100644 index 0000000..9b25eeb --- /dev/null +++ b/src/main/resources/migration/1_0.sql @@ -0,0 +1,11 @@ +-- Test Script for Database Migration +CREATE TABLE ACCOUNT ( + USER_ID INT PRIMARY KEY, + USER_NAME VARCHAR(100) NOT NULL +); + +CREATE TABLE PROJECT ( + PROJECT_ID INT PRIMARY KEY, + PROJECT_NAME VARCHAR(100) NOT NULL +); + diff --git a/src/main/scala/util/AutoUpdate.scala b/src/main/scala/util/AutoUpdate.scala new file mode 100644 index 0000000..897eb59 --- /dev/null +++ b/src/main/scala/util/AutoUpdate.scala @@ -0,0 +1,102 @@ +package util + +import java.io.File +import java.sql.Connection +import org.apache.commons.io.FileUtils +import javax.servlet.ServletContextEvent +import org.apache.commons.io.IOUtils +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +object AutoUpdate { + + case class Version(majorVersion: Int, minorVersion: Int){ + + val logger = LoggerFactory.getLogger(classOf[Version]) + + def update(conn: Connection): Unit = { + val sqlPath = "update/%d.%d.sq".format(majorVersion, minorVersion) + val in = Thread.currentThread.getContextClassLoader.getResourceAsStream(sqlPath) + if(in != null){ + val sql = IOUtils.toString(in) + val stmt = conn.createStatement() + try { + logger.debug(sqlPath + "=" + sql) + stmt.executeUpdate(sql) + } finally { + stmt.close() + } + } + } + + val versionString = "%d.%d".format(majorVersion, minorVersion) + } + + /** + * The history of versions. A head of this sequence is the current BitBucket version. + */ + val versions = Seq( + Version(1, 0) + ) + + /** + * The head version of BitBucket. + */ + val headVersion = versions.head + + /** + * The version file (GITBUCKET_HOME/version). + */ + val versionFile = new File(Directory.GitBucketHome, "version") + + /** + * Returns the current version from the version file. + */ + def getCurrentVersion(): Version = { + if(versionFile.exists){ + FileUtils.readFileToString(versionFile).split(".") match { + case Array(majorVersion, minorVersion) => { + versions.find { v => v.majorVersion == majorVersion.toInt && v.minorVersion == minorVersion.toInt }.get + } + case _ => Version(0, 0) + } + } else { + Version(0, 0) + } + + } + +} + +/** + * Start H2 database and update schema automatically. + */ +class AutoUpdateListener extends org.h2.server.web.DbStarter { + import AutoUpdate._ + val logger = LoggerFactory.getLogger(classOf[AutoUpdateListener]) + + override def contextInitialized(event: ServletContextEvent): Unit = { + super.contextInitialized(event) + logger.debug("H2 started") + + logger.debug("Start migration") + val conn = getConnection() + try { + val currentVersion = getCurrentVersion() + + versions.takeWhile(_ != currentVersion).reverse.foreach(_.update(conn)) + FileUtils.writeStringToFile(versionFile, headVersion.majorVersion + "." + headVersion.minorVersion) + + logger.debug("Migrate from " + currentVersion.versionString + " to " + headVersion.versionString) + + conn.commit() + } catch { + case ex: Throwable => { + logger.error("Failed to migrate", ex) + conn.rollback() + } + } + logger.debug("End migration") + } + +} \ No newline at end of file diff --git a/src/main/scala/util/Migration.scala b/src/main/scala/util/Migration.scala deleted file mode 100644 index 1d674ff..0000000 --- a/src/main/scala/util/Migration.scala +++ /dev/null @@ -1,65 +0,0 @@ -package util - -import java.io._ -import org.apache.commons.io.FileUtils - -object Migration { - - /** - * Define Migrator interface. - */ - trait Migrator { - def migrate(): Unit - } - - /** - * Migrate H2 database by SQL files. - */ - case class DatabaseMigrator(sqlPath: String) extends Migrator { - def migrate(): Unit = { - // TODO - } - } - - case class Version(majorVersion: Int, minorVersion: Int, migrators: Migrator*) - - /** - * The history of versions. A head of this sequence is the current BitBucket version. - * Migration#migrate() updates the data directory to move to the head version. - */ - val versions = Seq( - Version(1, 0, DatabaseMigrator("migration/1.0/createdb.sql")) - ) - - /** - * The head version of BitBucket. - */ - val headVersion = versions.head - - /** - * The version file (GITBUCKET_HOME/version). - */ - val versionFile = new File(Directory.GitBucketHome, "version") - - /** - * Returns the current version - */ - def getCurrentVersion(): Version = { - FileUtils.readFileToString(versionFile).split(".") match { - case Array(majorVersion, minorVersion) => { - versions.find { v => v.majorVersion == majorVersion.toInt && v.minorVersion == minorVersion.toInt }.get - } - } - } - - /** - * Do migrate old data directory to the head version. - */ - def migrate(): Unit = { - val currentVersion = getCurrentVersion() - versions.takeWhile(_ != currentVersion).reverse.foreach(_.migrators.foreach(_.migrate())) - FileUtils.writeStringToFile(versionFile, headVersion.majorVersion + "." + headVersion.minorVersion) - } - - -} \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index d2b646a..93bc19d 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -35,7 +35,7 @@ - org.h2.server.web.DbStarter + util.AutoUpdateListener