change PluginsClassLoader and change method name by 'loadPluginMainClass' to 'loadPluginMainClassByJarFile'

This commit is contained in:
Sincky 2020-04-07 22:30:14 +08:00 committed by Him188
parent c9f0195f8e
commit 6460de75a2
2 changed files with 149 additions and 56 deletions

View File

@ -48,9 +48,9 @@ object PluginManager {
private val pluginDescriptions: MutableMap<String, PluginDescription> = mutableMapOf() private val pluginDescriptions: MutableMap<String, PluginDescription> = mutableMapOf()
/** /**
* 加载插件的ClassLoader * 加载插件的PluginsLoader
*/ */
private val pluginsClassLoader: PluginsClassLoader = PluginsClassLoader(this.javaClass.classLoader) private val pluginsLoader: PluginsLoader = PluginsLoader(this.javaClass.classLoader)
/** /**
* 插件优先级队列 * 插件优先级队列
@ -181,9 +181,6 @@ object PluginManager {
checkNoCircularDepends(it, it.depends, mutableListOf()) checkNoCircularDepends(it, it.depends, mutableListOf())
} }
//插件加载器导入插件jar
pluginsClassLoader.loadPlugins(pluginsLocation)
//load plugin individually //load plugin individually
fun loadPlugin(description: PluginDescription): Boolean { fun loadPlugin(description: PluginDescription): Boolean {
if (!description.noCircularDepend) { if (!description.noCircularDepend) {
@ -210,41 +207,31 @@ object PluginManager {
logger.info("loading plugin " + description.name) logger.info("loading plugin " + description.name)
try { val jarFile = pluginsLocation[description.name]!!
val pluginClass = try{ val pluginClass = try{
pluginsClassLoader.loadPluginMainClass(description.basePath) pluginsLoader.loadPluginMainClassByJarFile(description.name,description.basePath,jarFile)
} catch (e: ClassNotFoundException) {
pluginsClassLoader.loadPluginMainClass("${description.basePath}Kt")
}
return try {
val subClass = pluginClass!!.asSubclass(PluginBase::class.java)
lastPluginName = description.name
val plugin: PluginBase =
subClass.kotlin.objectInstance ?: subClass.getDeclaredConstructor().apply {
againstPermission()
}.newInstance()
plugin.dataFolder // initialize right now
description.loaded = true
logger.info("successfully loaded plugin " + description.name + " version " + description.version + " by " + description.author)
logger.info(description.info)
nameToPluginBaseMap[description.name] = plugin
pluginDescriptions[description.name] = description
plugin.pluginName = description.name
pluginsSequence.add(plugin)//按照实际加载顺序加入队列
true
} catch (e: ClassCastException) {
logger.error("failed to load plugin " + description.name + " , Main class does not extends PluginBase ")
false
}
} catch (e: ClassNotFoundException) { } catch (e: ClassNotFoundException) {
logger.error("failed to load plugin " + description.name + " , Main class not found under " + description.basePath) pluginsLoader.loadPluginMainClassByJarFile(description.name,"${description.basePath}Kt",jarFile)
logger.error(e)
return false
} }
val subClass = pluginClass.asSubclass(PluginBase::class.java)
lastPluginName = description.name
val plugin: PluginBase =
subClass.kotlin.objectInstance ?: subClass.getDeclaredConstructor().apply {
againstPermission()
}.newInstance()
plugin.dataFolder // initialize right now
description.loaded = true
logger.info("successfully loaded plugin " + description.name + " version " + description.version + " by " + description.author)
logger.info(description.info)
nameToPluginBaseMap[description.name] = plugin
pluginDescriptions[description.name] = description
plugin.pluginName = description.name
pluginsSequence.add(plugin)//按照实际加载顺序加入队列
return true
} }
@ -252,7 +239,18 @@ object PluginManager {
pluginsSequence.clear() pluginsSequence.clear()
pluginsFound.values.forEach { pluginsFound.values.forEach {
loadPlugin(it) try{
// 尝试加载插件
loadPlugin(it)
}catch (e: Throwable) {
pluginsLoader.remove(it.name)
when(e){
is ClassCastException -> logger.error("failed to load plugin " + it.name + " , Main class does not extends PluginBase",e)
is ClassNotFoundException -> logger.error("failed to load plugin " + it.name + " , Main class not found under " + it.basePath,e)
is NoClassDefFoundError -> logger.error("failed to load plugin " + it.name + " , dependent class not found.",e)
else -> logger.error("failed to load plugin " + it.name,e)
}
}
} }
@ -297,7 +295,7 @@ object PluginManager {
plugin.disable(exception) plugin.disable(exception)
nameToPluginBaseMap.remove(plugin.pluginName) nameToPluginBaseMap.remove(plugin.pluginName)
pluginDescriptions.remove(plugin.pluginName) pluginDescriptions.remove(plugin.pluginName)
//pluginsClassLoader.remove(plugin.pluginName) pluginsLoader.remove(plugin.pluginName)
pluginsSequence.remove(plugin) pluginsSequence.remove(plugin)
} }
@ -310,7 +308,7 @@ object PluginManager {
} }
nameToPluginBaseMap.clear() nameToPluginBaseMap.clear()
pluginDescriptions.clear() pluginDescriptions.clear()
pluginsClassLoader.clear() pluginsLoader.clear()
pluginsSequence.clear() pluginsSequence.clear()
} }

View File

@ -1,33 +1,128 @@
package net.mamoe.mirai.console.plugins package net.mamoe.mirai.console.plugins
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.utils.SimpleLogger
import java.io.File import java.io.File
import java.io.IOException
import java.net.URLClassLoader import java.net.URLClassLoader
internal class PluginsClassLoader(private val parent: ClassLoader) { internal class PluginsLoader(private val parentClassLoader: ClassLoader) {
private val loggerName = "PluginsLoader"
private var cl : URLClassLoader? = null private val pluginLoaders = linkedMapOf<String,PluginClassLoader>()
private val classesCache = mutableMapOf<String, Class<*>>()
/** private val logger = SimpleLogger(loggerName) { p, message, e ->
* 加载多个插件 MiraiConsole.logger(p, "[${loggerName}]", 0, message)
*/ MiraiConsole.logger(p, "[${loggerName}]", 0, e)
fun loadPlugins(pluginsLocation: Map<String, File>) {
cl = URLClassLoader(
pluginsLocation.values.map { it.toURI().toURL() }.toTypedArray(),
parent)
} }
/** /**
* 清除所有插件加载器 * 清除所有插件加载器
*/ */
fun clear() { fun clear() {
cl?.close() val iterator = pluginLoaders.iterator()
while(iterator.hasNext()){
val plugin = iterator.next()
var cl = ""
try {
cl = plugin.value.toString()
plugin.value.close()
iterator.remove()
}catch (e: Throwable){
logger.error("Plugin(${plugin.key}) can't not close its ClassLoader(${cl})",e)
}
}
classesCache.clear()
} }
/** /**
* 加载 * 移除单个插件加载
*/ */
fun remove(pluginName: String): Boolean {
pluginLoaders[pluginName]?.close() ?: return false
pluginLoaders.remove(pluginName)
return true
}
fun loadPluginMainClass(name: String) : Class<*>{ fun loadPluginMainClassByJarFile(pluginName:String, mainClass: String, jarFile:File): Class<*> {
return cl?.loadClass(name) ?: error("PluginsClassLoader has not yet run the loadPlugins func.") try {
if(!pluginLoaders.containsKey(pluginName)){
pluginLoaders[pluginName] = PluginClassLoader(pluginName,jarFile, this, parentClassLoader)
}
return Class.forName(mainClass,true,pluginLoaders[pluginName])
}catch (e : ClassNotFoundException){
throw ClassNotFoundException("PluginsClassLoader(${pluginName}) can't load this pluginMainClass:${mainClass}",e)
}catch (e : Throwable){
throw Throwable("init or load class error",e)
}
}
/**
* 尝试加载插件的依赖,无则返回null
*/
fun loadDependentClass(name: String): Class<*>? {
var c: Class<*>? = null
// 尝试从缓存中读取
if (classesCache.containsKey(name)) {
c = classesCache[name]
}
// 然后再交给插件的classloader来加载依赖
if (c == null) {
pluginLoaders.values.forEach {
try {
c = it.findClass(name, false)
return@forEach
} catch (e: ClassNotFoundException) {/*nothing*/
}
}
}
return c
}
fun addClassCache(name: String, clz: Class<*>) {
synchronized(classesCache) {
if (!classesCache.containsKey(name)) {
classesCache[name] = clz
}
}
}
}
internal class PluginClassLoader(private val pluginName: String,files: File, private val pluginsLoader: PluginsLoader, parent: ClassLoader) :
URLClassLoader(arrayOf((files.toURI().toURL())), parent) {
private val classesCache = mutableMapOf<String, Class<*>?>()
override fun findClass(name: String): Class<*>? {
return this.findClass(name, true)
}
fun findClass(name: String, isSearchDependent: Boolean): Class<*>? {
var clz: Class<*>? = null
// 缓存中找
if (classesCache.containsKey(name)) {
return classesCache[name]
}
// 是否寻找依赖
if (isSearchDependent) {
clz = pluginsLoader.loadDependentClass(name)
}
// 交给super去findClass
if (clz == null) {
clz = super.findClass(name)
}
// 加入缓存
if (clz != null) {
pluginsLoader.addClassCache(name, clz)
}
// 加入缓存
synchronized(classesCache) {
classesCache[name] = clz
}
return clz
}
override fun close() {
super.close()
classesCache.clear()
} }
} }