diff --git a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala index dfd659a..22264fe 100644 --- a/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala +++ b/src/main/scala/gitbucket/core/plugin/PluginRegistory.scala @@ -24,7 +24,6 @@ import scala.collection.mutable import scala.collection.mutable.ListBuffer -import com.github.zafarkhaja.semver.{Version => Semver} class PluginRegistry { @@ -190,6 +189,7 @@ private var instance = new PluginRegistry() private var watcher: PluginWatchThread = null + private var extraWatcher: PluginWatchThread = null /** * Returns the PluginRegistry singleton instance. @@ -242,6 +242,8 @@ }).toSeq.sortBy(_.getName).reverse } + lazy val extraPluginDir: Option[String] = Option(System.getProperty("gitbucket.pluginDir")) + /** * Initializes all installed plugins. */ @@ -256,57 +258,65 @@ } installedDir.mkdir() - if(pluginDir.exists && pluginDir.isDirectory) { - pluginDir.listFiles(new FilenameFilter { - override def accept(dir: File, name: String): Boolean = name.endsWith(".jar") - }).toSeq.sortBy(_.getName).reverse.foreach { pluginJar => + val pluginJars = listPluginJars(pluginDir) + val extraJars = extraPluginDir.map { extraDir => listPluginJars(new File(extraDir)) }.getOrElse(Nil) - val installedJar = new File(installedDir, pluginJar.getName) - FileUtils.copyFile(pluginJar, installedJar) + (extraJars ++ pluginJars).foreach { pluginJar => + val installedJar = new File(installedDir, pluginJar.getName) + FileUtils.copyFile(pluginJar, installedJar) - logger.info(s"Initialize ${pluginJar.getName}") - val classLoader = new URLClassLoader(Array(installedJar.toURI.toURL), Thread.currentThread.getContextClassLoader) - try { - val plugin = classLoader.loadClass("Plugin").getDeclaredConstructor().newInstance().asInstanceOf[Plugin] - val pluginId = plugin.pluginId - // Check duplication - instance.getPlugins().find(_.pluginId == pluginId).foreach { x => - throw new IllegalStateException(s"Plugin ${pluginId} is duplicated. ${x.pluginJar.getName} is available.") + logger.info(s"Initialize ${pluginJar.getName}") + val classLoader = new URLClassLoader(Array(installedJar.toURI.toURL), Thread.currentThread.getContextClassLoader) + try { + val plugin = classLoader.loadClass("Plugin").getDeclaredConstructor().newInstance().asInstanceOf[Plugin] + val pluginId = plugin.pluginId + + // Check duplication + instance.getPlugins().find(_.pluginId == pluginId) match { + case Some(x) => { + logger.warn(s"Plugin ${pluginId} is duplicated. ${x.pluginJar.getName} is available.") } + case None => { + // Migration + val solidbase = new Solidbase() + solidbase.migrate(conn, classLoader, DatabaseConfig.liquiDriver, new Module(plugin.pluginId, plugin.versions: _*)) - // Migration - val solidbase = new Solidbase() - solidbase.migrate(conn, classLoader, DatabaseConfig.liquiDriver, new Module(plugin.pluginId, plugin.versions: _*)) + // Check database version + val databaseVersion = manager.getCurrentVersion(plugin.pluginId) + val pluginVersion = plugin.versions.last.getVersion + if (databaseVersion != pluginVersion) { + throw new IllegalStateException(s"Plugin version is ${pluginVersion}, but database version is ${databaseVersion}") + } - // Check database version - val databaseVersion = manager.getCurrentVersion(plugin.pluginId) - val pluginVersion = plugin.versions.last.getVersion - if (databaseVersion != pluginVersion) { - throw new IllegalStateException(s"Plugin version is ${pluginVersion}, but database version is ${databaseVersion}") + // Initialize + plugin.initialize(instance, context, settings) + instance.addPlugin(PluginInfo( + pluginId = plugin.pluginId, + pluginName = plugin.pluginName, + pluginVersion = plugin.versions.last.getVersion, + description = plugin.description, + pluginClass = plugin, + pluginJar = pluginJar, + classLoader = classLoader + )) } - - // Initialize - plugin.initialize(instance, context, settings) - instance.addPlugin(PluginInfo( - pluginId = plugin.pluginId, - pluginName = plugin.pluginName, - pluginVersion = plugin.versions.last.getVersion, - description = plugin.description, - pluginClass = plugin, - pluginJar = pluginJar, - classLoader = classLoader - )) - - } catch { - case e: Throwable => logger.error(s"Error during plugin initialization: ${pluginJar.getName}", e) } + } catch { + case e: Throwable => logger.error(s"Error during plugin initialization: ${pluginJar.getName}", e) } } if(watcher == null){ - watcher = new PluginWatchThread(context) + watcher = new PluginWatchThread(context, PluginHome) watcher.start() } + + extraPluginDir.foreach { extraDir => + if(extraWatcher == null){ + extraWatcher = new PluginWatchThread(context, extraDir) + extraWatcher.start() + } + } } def shutdown(context: ServletContext, settings: SystemSettings): Unit = synchronized { @@ -349,14 +359,14 @@ classLoader: URLClassLoader ) extends PluginInfoBase(pluginId, pluginName, pluginVersion, description) -class PluginWatchThread(context: ServletContext) extends Thread with SystemSettingsService { +class PluginWatchThread(context: ServletContext, dir: String) extends Thread with SystemSettingsService { import gitbucket.core.model.Profile.profile.blockingApi._ import scala.collection.JavaConverters._ private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread]) override def run(): Unit = { - val path = Paths.get(PluginHome) + val path = Paths.get(dir) if(!Files.exists(path)){ Files.createDirectories(path) } @@ -374,7 +384,9 @@ try { while (watchKey.isValid()) { val detectedWatchKey = watcher.take() - val events = detectedWatchKey.pollEvents.asScala.filter(_.context.toString != ".installed") + val events = detectedWatchKey.pollEvents.asScala.filter { e => + e.context.toString != ".installed" && !e.context.toString.endsWith(".bak") + } if(events.nonEmpty){ events.foreach { event => logger.info(event.kind + ": " + event.context) diff --git a/src/main/scala/gitbucket/core/util/Directory.scala b/src/main/scala/gitbucket/core/util/Directory.scala index 73adcfd..e4d3b4a 100644 --- a/src/main/scala/gitbucket/core/util/Directory.scala +++ b/src/main/scala/gitbucket/core/util/Directory.scala @@ -32,7 +32,7 @@ val DatabaseHome = s"${GitBucketHome}/data" - val PluginHome = Option(System.getProperty("gitbucket.pluginDir")).getOrElse(s"${GitBucketHome}/plugins") + val PluginHome = s"${GitBucketHome}/plugins" val TemporaryHome = s"${GitBucketHome}/tmp"