diff --git a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala index e47d0fc..efc1cd8 100644 --- a/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/SystemSettingsController.scala @@ -190,6 +190,15 @@ redirect("/admin/plugins") }) + post("/admin/plugins/:pluginId/_uninstall")(adminOnly { + val pluginId = params("pluginId") + PluginRegistry().getPlugins().find(_.pluginId == pluginId).foreach { plugin => + PluginRegistry.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn) + flash += "info" -> s"${pluginId} was uninstalled." + } + redirect("/admin/plugins") + }) + get("/admin/users")(adminOnly { val includeRemoved = params.get("includeRemoved").map(_.toBoolean).getOrElse(false) val users = getAllUsers(includeRemoved) diff --git a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala index b024265..40da20c 100644 --- a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala +++ b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala @@ -160,7 +160,7 @@ private var instance = new PluginRegistry() - private var watcher: PluginWatchThread = null +// private var watcher: PluginWatchThread = null /** * Returns the PluginRegistry singleton instance. @@ -177,6 +177,19 @@ } /** + * Uninstall a specified plugin. + */ + def uninstall(pluginId: String, context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit = synchronized { + instance.getPlugins().find(_.pluginId == pluginId).foreach { plugin => + shutdown(context, settings) + // TODO kick uninstall action here? + plugin.pluginJar.delete() + instance = new PluginRegistry() + initialize(context, settings, conn) + } + } + + /** * Initializes all installed plugins. */ def initialize(context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit = synchronized { @@ -209,7 +222,9 @@ pluginName = plugin.pluginName, pluginVersion = plugin.versions.last.getVersion, description = plugin.description, - pluginClass = plugin + pluginClass = plugin, + pluginJar = pluginJar, + classLoader = classLoader )) } catch { @@ -220,10 +235,10 @@ } } - if(watcher == null){ - watcher = new PluginWatchThread(context) - watcher.start() - } +// if(watcher == null){ +// watcher = new PluginWatchThread(context) +// watcher.start() +// } } def shutdown(context: ServletContext, settings: SystemSettings): Unit = synchronized { @@ -234,6 +249,8 @@ case e: Exception => { logger.error(s"Error during plugin shutdown", e) } + } finally { + pluginInfo.classLoader.close() } } } @@ -247,47 +264,50 @@ pluginName: String, pluginVersion: String, description: String, - pluginClass: Plugin + pluginClass: Plugin, + pluginJar: File, + classLoader: URLClassLoader ) -class PluginWatchThread(context: ServletContext) extends Thread with SystemSettingsService { - import gitbucket.core.model.Profile.profile.blockingApi._ - - private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread]) - - override def run(): Unit = { - val path = Paths.get(PluginHome) - val fs = path.getFileSystem - val watcher = fs.newWatchService - - val watchKey = path.register(watcher, - StandardWatchEventKinds.ENTRY_CREATE, - StandardWatchEventKinds.ENTRY_MODIFY, - StandardWatchEventKinds.ENTRY_DELETE, - StandardWatchEventKinds.OVERFLOW) - - logger.info("Start PluginWatchThread: " + path) - - try { - while (watchKey.isValid()) { - val detectedWatchKey = watcher.take() - if(detectedWatchKey != null){ - val events = detectedWatchKey.pollEvents() - events.forEach { event => - logger.info(event.kind + ": " + event.context) - } - gitbucket.core.servlet.Database() withTransaction { session => - logger.info("Reloading plugins...") - PluginRegistry.reload(context, loadSystemSettings(), session.conn) - } - } - detectedWatchKey.reset() - } - } catch { - case _: InterruptedException => () - } - - logger.info("Shutdown PluginWatchThread") - } - -} +//class PluginWatchThread(context: ServletContext) extends Thread with SystemSettingsService { +// import gitbucket.core.model.Profile.profile.blockingApi._ +// +// private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread]) +// +// override def run(): Unit = { +// val path = Paths.get(PluginHome) +// val fs = path.getFileSystem +// val watcher = fs.newWatchService +// +// val watchKey = path.register(watcher, +// StandardWatchEventKinds.ENTRY_CREATE, +// StandardWatchEventKinds.ENTRY_MODIFY, +// StandardWatchEventKinds.ENTRY_DELETE, +// StandardWatchEventKinds.OVERFLOW) +// +// logger.info("Start PluginWatchThread: " + path) +// +// try { +// while (watchKey.isValid()) { +// val detectedWatchKey = watcher.take() +// val events = detectedWatchKey.pollEvents() +// +// events.forEach { event => +// logger.info(event.kind + ": " + event.context) +// } +// +// gitbucket.core.servlet.Database() withTransaction { session => +// logger.info("Reloading plugins...") +// PluginRegistry.reload(context, loadSystemSettings(), session.conn) +// } +// +// detectedWatchKey.reset() +// } +// } catch { +// case _: InterruptedException => watchKey.cancel() +// } +// +// logger.info("Shutdown PluginWatchThread") +// } +// +//} diff --git a/src/main/twirl/gitbucket/core/admin/plugins.scala.html b/src/main/twirl/gitbucket/core/admin/plugins.scala.html index 446bdfc..4603d2b 100644 --- a/src/main/twirl/gitbucket/core/admin/plugins.scala.html +++ b/src/main/twirl/gitbucket/core/admin/plugins.scala.html @@ -2,10 +2,10 @@ @gitbucket.core.html.main("Plugins"){ @gitbucket.core.admin.html.menu("plugins") { @gitbucket.core.helper.html.information(info) -

Installed plugins

+

Installed plugins

@if(plugins.size > 0) {