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()
/**
* 加载插件的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())
}
//插件加载器导入插件jar
pluginsClassLoader.loadPlugins(pluginsLocation)
//load plugin individually
fun loadPlugin(description: PluginDescription): Boolean {
if (!description.noCircularDepend) {
@ -210,41 +207,31 @@ object PluginManager {
logger.info("loading plugin " + description.name)
try {
val pluginClass = try{
pluginsClassLoader.loadPluginMainClass(description.basePath)
} 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
}
val jarFile = pluginsLocation[description.name]!!
val pluginClass = try{
pluginsLoader.loadPluginMainClassByJarFile(description.name,description.basePath,jarFile)
} catch (e: ClassNotFoundException) {
logger.error("failed to load plugin " + description.name + " , Main class not found under " + description.basePath)
logger.error(e)
return false
pluginsLoader.loadPluginMainClassByJarFile(description.name,"${description.basePath}Kt",jarFile)
}
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()
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)
nameToPluginBaseMap.remove(plugin.pluginName)
pluginDescriptions.remove(plugin.pluginName)
//pluginsClassLoader.remove(plugin.pluginName)
pluginsLoader.remove(plugin.pluginName)
pluginsSequence.remove(plugin)
}
@ -310,7 +308,7 @@ object PluginManager {
}
nameToPluginBaseMap.clear()
pluginDescriptions.clear()
pluginsClassLoader.clear()
pluginsLoader.clear()
pluginsSequence.clear()
}

View File

@ -1,33 +1,128 @@
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.IOException
import java.net.URLClassLoader
internal class PluginsClassLoader(private val parent: ClassLoader) {
private var cl : URLClassLoader? = null
/**
* 加载多个插件
*/
fun loadPlugins(pluginsLocation: Map<String, File>) {
cl = URLClassLoader(
pluginsLocation.values.map { it.toURI().toURL() }.toTypedArray(),
parent)
internal class PluginsLoader(private val parentClassLoader: ClassLoader) {
private val loggerName = "PluginsLoader"
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 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<*>{
return cl?.loadClass(name) ?: error("PluginsClassLoader has not yet run the loadPlugins func.")
fun loadPluginMainClassByJarFile(pluginName:String, mainClass: String, jarFile:File): Class<*> {
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()
}
}