diff --git a/src/main/scala/plugin/Plugin.scala b/src/main/scala/plugin/Plugin.scala index 0c8164e..48ae955 100644 --- a/src/main/scala/plugin/Plugin.scala +++ b/src/main/scala/plugin/Plugin.scala @@ -1,11 +1,13 @@ package plugin +import util.Version + trait Plugin { val pluginId: String val pluginName: String val description: String - val version: String + val versions: List[Version] def initialize(registry: PluginRegistry): Unit diff --git a/src/main/scala/plugin/PluginRegistory.scala b/src/main/scala/plugin/PluginRegistory.scala index 18658e5..5d94010 100644 --- a/src/main/scala/plugin/PluginRegistory.scala +++ b/src/main/scala/plugin/PluginRegistory.scala @@ -8,6 +8,7 @@ import service.RepositoryService.RepositoryInfo import util.Directory._ import util.JDBCUtil._ +import util.{Version, Versions} import scala.collection.mutable.ListBuffer @@ -88,24 +89,36 @@ val classLoader = new URLClassLoader(Array(pluginJar.toURI.toURL), Thread.currentThread.getContextClassLoader) try { val plugin = classLoader.loadClass("Plugin").newInstance().asInstanceOf[Plugin] + + // Migration + val headVersion = plugin.versions.head + val currentVersion = conn.find("SELECT * FROM PLUGIN WHERE PLUGIN_ID = ?", plugin.pluginId)(_.getString("VERSION")) match { + case Some(x) => { + val dim = x.split("\\.") + Version(dim(0).toInt, dim(1).toInt) + } + case None => Version(0, 0) + } + + Versions.update(conn, headVersion, currentVersion, plugin.versions, new URLClassLoader(Array(pluginJar.toURI.toURL))){ conn => + currentVersion.versionString match { + case "0.0" => + conn.update("INSERT INTO PLUGIN (PLUGIN_ID, VERSION) VALUES (?, ?)", plugin.pluginId, headVersion.versionString) + case _ => + conn.update("UPDATE PLUGIN SET VERSION = ? WHERE PLUGIN_ID = ?", headVersion.versionString, plugin.pluginId) + } + } + + // Initialize plugin.initialize(instance) instance.addPlugin(PluginInfo( pluginId = plugin.pluginId, pluginName = plugin.pluginName, - version = plugin.version, + version = plugin.versions.head.versionString, description = plugin.description, pluginClass = plugin )) - conn.find("SELECT * FROM PLUGIN WHERE PLUGIN_ID = ?", plugin.pluginId)(_.getString("VERSION")) match { - // Update if the plugin is already registered - case Some(currentVersion) => conn.update("UPDATE PLUGIN SET VERSION = ? WHERE PLUGIN_ID = ?", plugin.version, plugin.pluginId) - // Insert if the plugin does not exist - case None => conn.update("INSERT INTO PLUGIN (PLUGIN_ID, VERSION) VALUES (?, ?)", plugin.pluginId, plugin.version) - } - - // TODO Migration - } catch { case e: Exception => { logger.error(s"Error during plugin initialization", e) diff --git a/src/main/scala/servlet/AutoUpdateListener.scala b/src/main/scala/servlet/AutoUpdateListener.scala index 6816bc3..07edf7e 100644 --- a/src/main/scala/servlet/AutoUpdateListener.scala +++ b/src/main/scala/servlet/AutoUpdateListener.scala @@ -13,8 +13,6 @@ import util.Directory import plugin._ -import scala.slick.jdbc.meta.MBestRowIdentifierColumn.Scope.Session - object AutoUpdate { /** diff --git a/src/main/scala/util/Version.scala b/src/main/scala/util/Version.scala new file mode 100644 index 0000000..00d4854 --- /dev/null +++ b/src/main/scala/util/Version.scala @@ -0,0 +1,67 @@ +package util + +import java.sql.Connection + +import org.apache.commons.io.IOUtils +import org.slf4j.LoggerFactory +import util.ControlUtil._ + +case class Version(majorVersion: Int, minorVersion: Int) { + + private val logger = LoggerFactory.getLogger(classOf[Version]) + + /** + * Execute update/MAJOR_MINOR.sql to update schema to this version. + * If corresponding SQL file does not exist, this method do nothing. + */ + def update(conn: Connection, cl: ClassLoader): Unit = { + val sqlPath = s"update/${majorVersion}_${minorVersion}.sql" + + using(cl.getResourceAsStream(sqlPath)){ in => + if(in != null){ + val sql = IOUtils.toString(in, "UTF-8") + using(conn.createStatement()){ stmt => + logger.debug(sqlPath + "=" + sql) + stmt.executeUpdate(sql) + } + } + } + } + + + /** + * MAJOR.MINOR + */ + val versionString = s"${majorVersion}.${minorVersion}" + +} + +object Versions { + + private val logger = LoggerFactory.getLogger(Versions.getClass) + + def update(conn: Connection, headVersion: Version, currentVersion: Version, versions: List[Version], cl: ClassLoader) + (save: Connection => Unit): Unit = { + logger.debug("Start schema update") + try { + if(currentVersion == headVersion){ + logger.debug("No update") + } else if(currentVersion.versionString != "0.0" && !versions.contains(currentVersion)){ + logger.warn(s"Skip migration because ${currentVersion.versionString} is illegal version.") + } else { + versions.takeWhile(_ != currentVersion).reverse.foreach(_.update(conn, cl)) + save(conn) + logger.debug(s"Updated from ${currentVersion.versionString} to ${headVersion.versionString}") + } + } catch { + case ex: Throwable => { + logger.error("Failed to schema update", ex) + ex.printStackTrace() + conn.rollback() + } + } + logger.debug("End schema update") + } + +} +