Improve JarPluginLoaderImp

This commit is contained in:
Him188 2020-09-01 12:48:39 +08:00
parent bc2b91e150
commit 1d1974be6c
4 changed files with 114 additions and 54 deletions

View File

@ -15,20 +15,17 @@ import kotlinx.coroutines.ensureActive
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.data.PluginDataStorage
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
import net.mamoe.mirai.console.internal.data.cast
import net.mamoe.mirai.console.internal.data.createInstanceOrNull
import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
import net.mamoe.mirai.console.plugin.PluginLoadException
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
import net.mamoe.mirai.console.plugin.jvm.*
import net.mamoe.mirai.console.util.ServiceHelper.findServices
import net.mamoe.mirai.console.util.ServiceHelper.loadAllServices
import net.mamoe.mirai.console.util.childScope
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.error
import net.mamoe.mirai.utils.info
import java.io.File
import java.io.InputStream
import java.net.URLClassLoader
import java.util.concurrent.ConcurrentHashMap
internal object JarPluginLoaderImpl :
AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"),
@ -52,38 +49,37 @@ internal object JarPluginLoaderImpl :
override val JvmPlugin.description: JvmPluginDescription
get() = this.description
private val pluginFileToInstanceMap: MutableMap<File, JvmPlugin> = ConcurrentHashMap()
override fun Sequence<File>.extractPlugins(): List<JvmPlugin> {
ensureActive()
fun Sequence<ClassLoader>.loadAll(): Sequence<JvmPlugin> {
return mapNotNull { pluginClassLoader ->
pluginClassLoader.getResourceAsStream(JvmPlugin::class.qualifiedName!!)?.use(InputStream::readBytes)
?.let(::String)?.let { it to pluginClassLoader }
}.mapNotNull { (pluginQualifiedName, classLoader) ->
kotlin.runCatching {
val clazz =
Class.forName(pluginQualifiedName, true, classLoader).cast<Class<out JvmPlugin>>()
clazz.kotlin.objectInstance
?: clazz.kotlin.createInstanceOrNull() ?: clazz.newInstance()
}.getOrElse {
logger.error(
{ "Could not load PluginLoader ${pluginQualifiedName}." },
PluginLoadException("Could not load PluginLoader ${pluginQualifiedName}.", it)
)
return@mapNotNull null
}.also {
logger.info { "Successfully loaded PluginLoader ${pluginQualifiedName}." }
}
fun Sequence<Map.Entry<File, ClassLoader>>.findAllInstances(): Sequence<Map.Entry<File, JvmPlugin>> {
return map { (f, pluginClassLoader) ->
f to pluginClassLoader.findServices(
JvmPlugin::class,
KotlinPlugin::class,
AbstractJvmPlugin::class,
JavaPlugin::class
).loadAllServices()
}.flatMap { (f, list) ->
list.associateBy { f }.asSequence()
}
}
val filePlugins = this.map {
val filePlugins = this.filterNot {
pluginFileToInstanceMap.containsKey(it)
}.associateWith {
URLClassLoader(arrayOf(it.toURI().toURL()), MiraiConsole::class.java.classLoader)
}.onEach { classLoader ->
}.onEach { (_, classLoader) ->
classLoaders.add(classLoader)
}.loadAll()
}.asSequence().findAllInstances().onEach { loaded ->
logger.info { "Successfully initialized JvmPlugin ${loaded}." }
}.onEach { (file, plugin) ->
pluginFileToInstanceMap[file] = plugin
}
return filePlugins.toSet().toList()
return filePlugins.toSet().map { it.value }
}
@Throws(PluginLoadException::class)

View File

@ -16,18 +16,17 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.internal.data.cast
import net.mamoe.mirai.console.internal.data.createInstanceOrNull
import net.mamoe.mirai.console.internal.data.mkdir
import net.mamoe.mirai.console.plugin.*
import net.mamoe.mirai.console.plugin.description.PluginDependency
import net.mamoe.mirai.console.plugin.description.PluginDescription
import net.mamoe.mirai.console.plugin.description.PluginKind
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.util.ServiceHelper.findServices
import net.mamoe.mirai.console.util.ServiceHelper.loadAllServices
import net.mamoe.mirai.console.util.childScope
import net.mamoe.mirai.utils.error
import net.mamoe.mirai.utils.info
import java.io.File
import java.io.InputStream
import java.nio.file.Path
import java.util.concurrent.locks.ReentrantLock
@ -41,7 +40,9 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
override val pluginsConfigFolder: File = pluginsConfigPath.toFile()
@Suppress("ObjectPropertyName")
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
private val _pluginLoaders: MutableList<PluginLoader<*, *>> by lazy {
MiraiConsole.builtInPluginLoaders.toMutableList()
}
private val loadersLock: ReentrantLock = ReentrantLock()
private val logger = MiraiConsole.createLogger("plugin")
@ -130,38 +131,36 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
loadPluginLoaderProvidedByPlugins()
loadersLock.withLock {
_pluginLoaders.listAllPlugins().flatMap { it.second }
.sortByDependencies().loadAndEnableAllInOrder()
.also {
logger.debug("All plugins: ${it.joinToString { (_, desc, _) -> desc.name }}")
}
.sortByDependencies()
.also {
logger.debug("Sorted plugins: ${it.joinToString { (_, desc, _) -> desc.name }}")
}
.loadAndEnableAllInOrder()
}
}
private fun loadPluginLoaderProvidedByPlugins() {
loadersLock.withLock {
JarPluginLoaderImpl.classLoaders.asSequence()
.mapNotNull { pluginClassLoader ->
pluginClassLoader.getResourceAsStream(JvmPlugin::class.qualifiedName!!)?.use(InputStream::readBytes)
?.let(::String)?.let { it to pluginClassLoader }
.flatMap { pluginClassLoader ->
pluginClassLoader.findServices<PluginLoader<*, *>>().loadAllServices()
}
.forEach { (pluginQualifiedName, classLoader) ->
val pluginLoader = kotlin.runCatching {
val clazz =
Class.forName(pluginQualifiedName, true, classLoader).cast<Class<out PluginLoader<*, *>>>()
clazz.kotlin.objectInstance
?: clazz.kotlin.createInstanceOrNull() ?: clazz.newInstance()
}.getOrElse {
logger.error(
{ "Could not load PluginLoader ${pluginQualifiedName}." },
PluginLoadException("Could not load PluginLoader ${pluginQualifiedName}.", it)
)
return@forEach
}
_pluginLoaders.add(pluginLoader)
logger.info { "Successfully loaded PluginLoader ${pluginQualifiedName}." }
.onEach { loaded ->
logger.info { "Successfully loaded PluginLoader ${loaded}." }
}
.forEach {
_pluginLoaders.add(it)
}
}
}
private fun List<PluginDescriptionWithLoader>.loadAndEnableAllInOrder() {
return this.forEach { (loader, _, plugin) ->
loader.loadPluginNoEnable(plugin)
loader.enablePlugin(plugin)
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.util
import net.mamoe.mirai.console.internal.data.cast
import net.mamoe.mirai.console.internal.data.createInstanceOrNull
import java.io.InputStream
import kotlin.reflect.KClass
internal class ServiceList<T>(
internal val classLoader: ClassLoader,
internal val delegate: List<String>
)
internal object ServiceHelper {
inline fun <reified T : Any> ClassLoader.findServices(): ServiceList<T> = findServices(T::class)
fun <T : Any> ClassLoader.findServices(vararg serviceTypes: KClass<out T>): ServiceList<T> =
serviceTypes.flatMap { serviceType ->
getResourceAsStream("META-INF/services/" + serviceType.qualifiedName!!)
?.use(InputStream::readBytes)
?.let(::String)?.lines()?.filter(String::isNotBlank).orEmpty()
}.let { ServiceList(this, it) }
fun <T : Any> ServiceList<T>.loadAllServices(): List<T> {
return delegate.mapNotNull { classLoader.loadService<T>(it) }
}
fun <T : Any> ClassLoader.loadService(
classname: String
): T? {
return kotlin.runCatching {
val clazz =
Class.forName(classname, true, this).cast<Class<out T>>()
@Suppress("UNCHECKED_CAST")
clazz.kotlin.objectInstance
?: clazz.kotlin.createInstanceOrNull()
?: clazz.constructors.firstOrNull { it.parameterCount == 0 }?.newInstance() as T?
?: error("Cannot find a no-arg constructor")
}.getOrElse {
throw ServiceLoadException("Could not load service ${classname}.", it)
/*
logger.error(
{ "Could not load PluginLoader ${pluginQualifiedName}." },
PluginLoadException("Could not load PluginLoader ${pluginQualifiedName}.", it)
)*/
}
}
}
@Suppress("unused", "RedundantVisibilityModifier")
internal open class ServiceLoadException : RuntimeException {
public constructor() : super()
public constructor(message: String?) : super(message)
public constructor(message: String?, cause: Throwable?) : super(message, cause)
public constructor(cause: Throwable?) : super(cause)
}

View File

@ -18,7 +18,7 @@
object Versions {
const val core = "1.2.2"
const val console = "1.0-M3-dev-4"
const val console = "1.0-M4-dev-1"
const val consoleGraphical = "0.0.7"
const val consoleTerminal = "0.1.0"
const val consolePure = console