mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Improve JarPluginLoaderImp
This commit is contained in:
parent
bc2b91e150
commit
1d1974be6c
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user