From 224255fd0f0f63d8bf79c5fc8bc651d60b864db3 Mon Sep 17 00:00:00 2001 From: Sincky <893723237@qq.com> Date: Sun, 5 Apr 2020 14:27:06 +0800 Subject: [PATCH 1/3] add PluginsClassLoader and fix hot-reload --- .../mirai/console/plugins/PluginManager.kt | 30 +++-- .../console/plugins/PluginsClassLoader.kt | 113 ++++++++++++++++++ 2 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt index 65a4a3175..6f2e602a0 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt @@ -22,8 +22,8 @@ import java.io.File import java.io.InputStream import java.lang.reflect.Constructor import java.lang.reflect.Method +import java.net.JarURLConnection import java.net.URL -import java.net.URLClassLoader import java.util.jar.JarFile @@ -40,6 +40,7 @@ object PluginManager { //已完成加载的 private val nameToPluginBaseMap: MutableMap = mutableMapOf() private val pluginDescriptions: MutableMap = mutableMapOf() + private val pluginsClassLoader: PluginsClassLoader = PluginsClassLoader(this.javaClass.classLoader) internal fun onCommand(command: Command, sender: CommandSender, args: List) { nameToPluginBaseMap.values.forEach { @@ -83,15 +84,20 @@ object PluginManager { val jar = JarFile(file) val pluginYml = jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull() + if (pluginYml == null) { logger.info("plugin.yml not found in jar " + jar.name + ", it will not be consider as a Plugin") } else { try { - val description = - PluginDescription.readFromContent( - URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.use { - it.readBytes().encodeToString() - }) + val description = PluginDescription.readFromContent( + URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().let { + val res = it.inputStream.use { input -> + input.readBytes().encodeToString() + } + // 关闭jarFile,解决热更新插件问题 + (it as JarURLConnection).jarFile.close() + res + }) pluginsFound[description.name] = description pluginsLocation[description.name] = file } catch (e: Exception) { @@ -101,8 +107,6 @@ object PluginManager { } } - val pluginsClassLoader = PluginsClassLoader(pluginsLocation.values,this.javaClass.classLoader) - //不仅要解决A->B->C->A, 还要解决A->B->C->A fun checkNoCircularDepends( target: PluginDescription, @@ -133,6 +137,9 @@ object PluginManager { checkNoCircularDepends(it, it.depends, mutableListOf()) } + //插件加载器导入插件jar + pluginsClassLoader.loadPlugins(pluginsLocation) + //load plugin fun loadPlugin(description: PluginDescription): Boolean { if (!description.noCircularDepend) { @@ -167,7 +174,7 @@ object PluginManager { } return try { - val subClass = pluginClass.asSubclass(PluginBase::class.java) + val subClass = pluginClass!!.asSubclass(PluginBase::class.java) lastPluginName = description.name val plugin: PluginBase = @@ -240,6 +247,7 @@ object PluginManager { plugin.disable(exception) nameToPluginBaseMap.remove(plugin.pluginName) pluginDescriptions.remove(plugin.pluginName) + pluginsClassLoader.remove(plugin.pluginName) } @@ -251,12 +259,14 @@ object PluginManager { } nameToPluginBaseMap.clear() pluginDescriptions.clear() + pluginsClassLoader.clear() } /** * 根据插件名字找Jar的文件 * null => 没找到 + * 这里的url的jarFile没关,热更新插件可能出事 */ fun getJarFileByName(pluginName: String): File? { File(pluginsPath).listFiles()?.forEach { file -> @@ -282,6 +292,7 @@ object PluginManager { /** * 根据插件名字找Jar中的文件 * null => 没找到 + * 这里的url的jarFile没关,热更新插件可能出事 */ fun getFileInJarByName(pluginName: String, toFind: String): InputStream? { val jarFile = getJarFileByName(pluginName) ?: return null @@ -308,4 +319,3 @@ private fun Constructor.againstPermission() { } } -internal class PluginsClassLoader(files: Collection, parent: ClassLoader) : URLClassLoader(files.map{it.toURI().toURL()}.toTypedArray(), parent) diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt new file mode 100644 index 000000000..914c35b87 --- /dev/null +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt @@ -0,0 +1,113 @@ +package net.mamoe.mirai.console.plugins + +import java.io.File +import java.net.URLClassLoader + +class PluginsClassLoader(parent: ClassLoader) : ClassLoader(parent) { + private val pluginLoaders = mutableMapOf() + + /** + * 加载多个插件 + */ + fun loadPlugins(pluginsLocation: Map) { + for((key, value) in pluginsLocation){ + pluginLoaders[key] = PluginClassLoader(value,this) + } + } + + + /** + * 清除所有插件加载器 + */ + fun clear() { + pluginLoaders.values.map { + it.close() + } + pluginLoaders.clear() + } + + /** + * 移除单个插件加载器 + */ + fun remove(pluginName: String){ + pluginLoaders[pluginName]!!.close() + pluginLoaders.remove(pluginName) + } + + + override fun loadClass(name: String): Class<*>? { + var c : Class<*>? = null + // 循环插件classloader loadClass + pluginLoaders.map { + try { + c = it.value.loadClass(name) + return@map + }catch (e: Throwable){ + } + } + // 如果为null,交给mirai的classloader进行加载 + if(c==null){ + c = parent.loadClass(name) // 如果无法加载这个类,这里会抛异常 + } + return c + } + + fun loadDependClass(name: String) : Class<*>? { + var c : Class<*>? = null + // 依赖问题先交给mirai ClassLoader来处理 + try { + c = parent.loadClass(name) + }catch (e : Throwable){ + } + // 如果mirai加载不了依赖则交给插件的classloader进行加载 + if(c==null){ + pluginLoaders.map { + try { + c = it.value.loadDependClass(name) + return@map + }catch (e: Throwable){ + } + + } + } + return c + } + + + + +} + +class PluginClassLoader(files: File, parent: PluginsClassLoader?) : + URLClassLoader(arrayOf((files.toURI().toURL())),parent) { + + override fun loadClass(name: String): Class<*>? { + synchronized(getClassLoadingLock(name)) { + // 看缓存中是否加载过此类 + var c = findLoadedClass(name) + if (c == null) { + c = try { + // 自己加载 + this.findClass(name) //ClassNotFoundException + } catch (e: ClassNotFoundException) { + // 交给父类去加载依赖 + (this.parent as PluginsClassLoader).loadDependClass(name) + } + } + return c + } + } + + fun loadDependClass(name: String): Class<*>? { + synchronized(getClassLoadingLock(name)) { + var c = findLoadedClass(name) + if (c == null) { + // 加载依赖类,没有返回null + c = this.findClass(name) // 这里会丢ClassNotFoundException + } + return c + } + } + +} + From a541289942cc4694d15eb1cefe1abfb721fb4825 Mon Sep 17 00:00:00 2001 From: Sincky <893723237@qq.com> Date: Sun, 5 Apr 2020 15:56:47 +0800 Subject: [PATCH 2/3] resolve conversation changes --- .../console/plugins/PluginsClassLoader.kt | 64 ++++++++----------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt index 914c35b87..0b5d8f372 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt @@ -3,15 +3,15 @@ package net.mamoe.mirai.console.plugins import java.io.File import java.net.URLClassLoader -class PluginsClassLoader(parent: ClassLoader) : ClassLoader(parent) { - private val pluginLoaders = mutableMapOf() +internal class PluginsClassLoader(parent: ClassLoader) : ClassLoader(parent) { + private val pluginLoaders = mutableMapOf() /** * 加载多个插件 */ fun loadPlugins(pluginsLocation: Map) { - for((key, value) in pluginsLocation){ - pluginLoaders[key] = PluginClassLoader(value,this) + for ((key, value) in pluginsLocation) { + pluginLoaders[key] = PluginClassLoader(value, this) } } @@ -29,57 +29,49 @@ class PluginsClassLoader(parent: ClassLoader) : ClassLoader(parent) { /** * 移除单个插件加载器 */ - fun remove(pluginName: String){ - pluginLoaders[pluginName]!!.close() + fun remove(pluginName: String): Boolean { + pluginLoaders[pluginName]?.close() ?: return false pluginLoaders.remove(pluginName) + return true } - override fun loadClass(name: String): Class<*>? { - var c : Class<*>? = null + var c: Class<*>? = null // 循环插件classloader loadClass - pluginLoaders.map { - try { - c = it.value.loadClass(name) - return@map - }catch (e: Throwable){ + pluginLoaders.values.forEach { + it.runCatching { + c = this.loadClass(name) + return@forEach } } // 如果为null,交给mirai的classloader进行加载 - if(c==null){ + if (c == null) { c = parent.loadClass(name) // 如果无法加载这个类,这里会抛异常 } return c } - fun loadDependClass(name: String) : Class<*>? { - var c : Class<*>? = null + fun loadDependClass(name: String): Class<*>? { + var c: Class<*>? = null // 依赖问题先交给mirai ClassLoader来处理 - try { + runCatching { c = parent.loadClass(name) - }catch (e : Throwable){ - } + } // 如果mirai加载不了依赖则交给插件的classloader进行加载 - if(c==null){ - pluginLoaders.map { - try { - c = it.value.loadDependClass(name) - return@map - }catch (e: Throwable){ + if (c == null) { + pluginLoaders.values.forEach { + it.runCatching { + c = this.loadDependClass(name) + return@forEach } - } } return c } - - - - } -class PluginClassLoader(files: File, parent: PluginsClassLoader?) : - URLClassLoader(arrayOf((files.toURI().toURL())),parent) { +internal class PluginClassLoader(files: File, parent: PluginsClassLoader?) : + URLClassLoader(arrayOf((files.toURI().toURL())), parent) { override fun loadClass(name: String): Class<*>? { synchronized(getClassLoadingLock(name)) { @@ -87,10 +79,10 @@ class PluginClassLoader(files: File, parent: PluginsClassLoader?) : var c = findLoadedClass(name) if (c == null) { c = try { - // 自己加载 + // 自己尝试加载 this.findClass(name) //ClassNotFoundException } catch (e: ClassNotFoundException) { - // 交给父类去加载依赖 + // 交给父类去加载非本插件的依赖 (this.parent as PluginsClassLoader).loadDependClass(name) } } @@ -102,8 +94,8 @@ class PluginClassLoader(files: File, parent: PluginsClassLoader?) : synchronized(getClassLoadingLock(name)) { var c = findLoadedClass(name) if (c == null) { - // 加载依赖类,没有返回null - c = this.findClass(name) // 这里会丢ClassNotFoundException + // 加载依赖类,没有则丢出异常 + c = this.findClass(name) // ClassNotFoundException } return c } From 1a7068940cd1a8a5cab317535f5386c7d650f0b8 Mon Sep 17 00:00:00 2001 From: Sincky <893723237@qq.com> Date: Sun, 5 Apr 2020 16:07:02 +0800 Subject: [PATCH 3/3] fix something --- .../net/mamoe/mirai/console/plugins/PluginsClassLoader.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt index 0b5d8f372..e7257f91e 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginsClassLoader.kt @@ -20,7 +20,7 @@ internal class PluginsClassLoader(parent: ClassLoader) : ClassLoader(parent) { * 清除所有插件加载器 */ fun clear() { - pluginLoaders.values.map { + pluginLoaders.values.forEach { it.close() } pluginLoaders.clear()