diff --git a/src/main/scala/ScalatraBootstrap.scala b/src/main/scala/ScalatraBootstrap.scala index bdbd898..aa90776 100644 --- a/src/main/scala/ScalatraBootstrap.scala +++ b/src/main/scala/ScalatraBootstrap.scala @@ -1,4 +1,4 @@ -import _root_.servlet.{BasicAuthenticationFilter, TransactionFilter} +import _root_.servlet.{BasicAuthenticationFilter, TransactionFilter, PluginActionFilter} import app._ //import jp.sf.amateras.scalatra.forms.ValidationJavaScriptProvider import org.scalatra._ @@ -12,6 +12,8 @@ context.getFilterRegistration("transactionFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*") context.addFilter("basicAuthenticationFilter", new BasicAuthenticationFilter) context.getFilterRegistration("basicAuthenticationFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/git/*") + context.addFilter("pluginActionFilter", new PluginActionFilter) + context.getFilterRegistration("pluginActionFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*") // Register controllers context.mount(new AnonymousAccessController, "/*") diff --git a/src/main/scala/plugin/PluginRegistory.scala b/src/main/scala/plugin/PluginRegistory.scala index 9abb6bd..18658e5 100644 --- a/src/main/scala/plugin/PluginRegistory.scala +++ b/src/main/scala/plugin/PluginRegistory.scala @@ -2,9 +2,12 @@ import java.io.{FilenameFilter, File} import java.net.URLClassLoader +import javax.servlet.http.{HttpServletRequest, HttpServletResponse} import org.slf4j.LoggerFactory +import service.RepositoryService.RepositoryInfo import util.Directory._ +import util.JDBCUtil._ import scala.collection.mutable.ListBuffer @@ -12,6 +15,8 @@ private val plugins = new ListBuffer[PluginInfo] private val javaScripts = new ListBuffer[(String, String)] + private val globalActions = new ListBuffer[GlobalAction] + private val repositoryActions = new ListBuffer[RepositoryAction] def addPlugin(pluginInfo: PluginInfo): Unit = { plugins += pluginInfo @@ -19,17 +24,51 @@ def getPlugins(): List[PluginInfo] = plugins.toList + def addGlobalAction(method: String, path: String)(f: (HttpServletRequest, HttpServletResponse) => Any): Unit = { + globalActions += GlobalAction(method.toLowerCase, path, f) + } + + //def getGlobalActions(): List[GlobalAction] = globalActions.toList + + def getGlobalAction(method: String, path: String): Option[(HttpServletRequest, HttpServletResponse) => Any] = { + globalActions.find { globalAction => + globalAction.method == method.toLowerCase && path.matches(globalAction.path) + }.map(_.function) + } + + def addRepositoryAction(method: String, path: String)(f: (HttpServletRequest, HttpServletResponse, RepositoryInfo) => Any): Unit = { + repositoryActions += RepositoryAction(method.toLowerCase, path, f) + } + + //def getRepositoryActions(): List[RepositoryAction] = repositoryActions.toList + + def getRepositoryAction(method: String, path: String): Option[(HttpServletRequest, HttpServletResponse, RepositoryInfo) => Any] = { + // TODO + null + } + def addJavaScript(path: String, script: String): Unit = { javaScripts += Tuple2(path, script) } - def getJavaScripts(): List[(String, String)] = javaScripts.toList + //def getJavaScripts(): List[(String, String)] = javaScripts.toList def getJavaScript(currentPath: String): Option[String] = { - println(currentPath) - getJavaScripts().find(x => currentPath.matches(x._1)).map(_._2) + javaScripts.find(x => currentPath.matches(x._1)).map(_._2) } + private case class GlobalAction( + method: String, + path: String, + function: (HttpServletRequest, HttpServletResponse) => Any + ) + + private case class RepositoryAction( + method: String, + path: String, + function: (HttpServletRequest, HttpServletResponse, RepositoryInfo) => Any + ) + } object PluginRegistry { @@ -40,24 +79,37 @@ def apply(): PluginRegistry = instance - def initialize(): Unit = { - new File(PluginHome).listFiles(new FilenameFilter { - override def accept(dir: File, name: String): Boolean = name.endsWith(".jar") - }).foreach { pluginJar => - val classLoader = new URLClassLoader(Array(pluginJar.toURI.toURL), Thread.currentThread.getContextClassLoader) - try { - val plugin = classLoader.loadClass("Plugin").newInstance().asInstanceOf[Plugin] - plugin.initialize(instance) - instance.addPlugin(PluginInfo( - pluginId = plugin.pluginId, - pluginName = plugin.pluginName, - version = plugin.version, - description = plugin.description, - pluginClass = plugin - )) - } catch { - case e: Exception => { - logger.error(s"Error during plugin initialization", e) + def initialize(conn: java.sql.Connection): Unit = { + val pluginDir = new File(PluginHome) + if(pluginDir.exists && pluginDir.isDirectory){ + pluginDir.listFiles(new FilenameFilter { + override def accept(dir: File, name: String): Boolean = name.endsWith(".jar") + }).foreach { pluginJar => + val classLoader = new URLClassLoader(Array(pluginJar.toURI.toURL), Thread.currentThread.getContextClassLoader) + try { + val plugin = classLoader.loadClass("Plugin").newInstance().asInstanceOf[Plugin] + plugin.initialize(instance) + instance.addPlugin(PluginInfo( + pluginId = plugin.pluginId, + pluginName = plugin.pluginName, + version = plugin.version, + 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 c8ed683..6816bc3 100644 --- a/src/main/scala/servlet/AutoUpdateListener.scala +++ b/src/main/scala/servlet/AutoUpdateListener.scala @@ -13,6 +13,8 @@ import util.Directory import plugin._ +import scala.slick.jdbc.meta.MBestRowIdentifierColumn.Scope.Session + object AutoUpdate { /** @@ -103,8 +105,10 @@ conn.update("UPDATE ACTIVITY SET ADDITIONAL_INFO = ? WHERE ACTIVITY_ID = ?", newInfo, rs.getInt("ACTIVITY_ID")) } } - FileUtils.deleteDirectory(Directory.getPluginCacheDir()) - FileUtils.deleteDirectory(new File(Directory.PluginHome)) + ignore { + FileUtils.deleteDirectory(Directory.getPluginCacheDir()) + //FileUtils.deleteDirectory(new File(Directory.PluginHome)) + } } }, new Version(2, 2), @@ -235,10 +239,12 @@ } } logger.debug("End schema update") + + // Load plugins + + PluginRegistry.initialize(conn) } - // Load plugins - PluginRegistry.initialize() } def contextDestroyed(sce: ServletContextEvent): Unit = { diff --git a/src/main/scala/servlet/PluginActionFilter.scala b/src/main/scala/servlet/PluginActionFilter.scala new file mode 100644 index 0000000..d434aee --- /dev/null +++ b/src/main/scala/servlet/PluginActionFilter.scala @@ -0,0 +1,38 @@ +package servlet + +import javax.servlet._ +import javax.servlet.http.{HttpServletResponse, HttpServletRequest} + +import play.twirl.api.Html +import plugin.PluginRegistry + +class PluginActionFilter extends Filter { + + def init(config: FilterConfig) = {} + + def destroy(): Unit = {} + + def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = (req, res) match { + case (req: HttpServletRequest, res: HttpServletResponse) => { + val method = req.getMethod.toLowerCase + val path = req.getRequestURI.substring(req.getContextPath.length) + val registry = PluginRegistry() + registry.getGlobalAction(method, path).map { action => + action(req, res) match { + // TODO to be type classes? + case x: String => + res.setContentType("text/plain; charset=UTF-8") + res.getWriter.write(x) + res.getWriter.flush() + case x: Html => + res.setContentType("text/html; charset=UTF-8") + res.getWriter.write(x.body) + res.getWriter.flush() + } + }.getOrElse { + chain.doFilter(req, res) + } + } + } + +} \ No newline at end of file diff --git a/src/main/scala/util/ControlUtil.scala b/src/main/scala/util/ControlUtil.scala index c231fb0..7945f32 100644 --- a/src/main/scala/util/ControlUtil.scala +++ b/src/main/scala/util/ControlUtil.scala @@ -37,4 +37,10 @@ def using[T](treeWalk: TreeWalk)(f: TreeWalk => T): T = try f(treeWalk) finally treeWalk.release() + def ignore[T](f: => Unit): Unit = try { + f + } catch { + case e: Exception => () + } + } diff --git a/src/main/scala/util/JDBCUtil.scala b/src/main/scala/util/JDBCUtil.scala index 375ea14..5d880d0 100644 --- a/src/main/scala/util/JDBCUtil.scala +++ b/src/main/scala/util/JDBCUtil.scala @@ -18,6 +18,14 @@ } } + def find[T](sql: String, params: Any*)(f: ResultSet => T): Option[T] = { + execute(sql, params: _*){ stmt => + using(stmt.executeQuery()){ rs => + if(rs.next) Some(f(rs)) else None + } + } + } + def select[T](sql: String, params: Any*)(f: ResultSet => T): Seq[T] = { execute(sql, params: _*){ stmt => using(stmt.executeQuery()){ rs =>