Update Plugin load mechanism for compatibility with Android

This commit is contained in:
Him188 2020-08-31 19:23:56 +08:00
parent 2ea86b377a
commit e8bb0f0bb4
3 changed files with 48 additions and 40 deletions

View File

@ -15,18 +15,20 @@ 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.util.childScopeContext
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.*
import kotlin.coroutines.CoroutineContext
import kotlin.streams.asSequence
internal object JarPluginLoaderImpl :
AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"),
@ -56,33 +58,35 @@ internal object JarPluginLoaderImpl :
override fun Sequence<File>.extractPlugins(): List<JvmPlugin> {
ensureActive()
fun <T> ServiceLoader<T>.loadAll(file: File?): Sequence<T> {
return stream().asSequence().mapNotNull {
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 {
it.type().kotlin.objectInstance ?: it.get()
}.onFailure {
logger.error("Cannot load plugin ${file ?: "<no-file>"}", it)
}.getOrNull()
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}." }
}
}
}
val inMemoryPlugins =
ServiceLoader.load(
JvmPlugin::class.java,
generateSequence(MiraiConsole::class.java.classLoader) { it.parent }.last()
).loadAll(null)
val filePlugins = this.associateWith {
val filePlugins = this.map {
URLClassLoader(arrayOf(it.toURI().toURL()), MiraiConsole::class.java.classLoader)
}.onEach { (_, classLoader) ->
}.onEach { classLoader ->
classLoaders.add(classLoader)
}.mapValues {
ServiceLoader.load(JvmPlugin::class.java, it.value)
}.flatMap { (file, loader) ->
loader.loadAll(file)
}
}.loadAll()
return (inMemoryPlugins + filePlugins).toSet().toList()
return filePlugins.toSet().toList()
}
@Throws(PluginLoadException::class)

View File

@ -16,6 +16,7 @@ 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
@ -26,11 +27,9 @@ 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.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.collections.ArrayList
import kotlin.streams.asSequence
internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsole.childScope("PluginManager") {
@ -138,23 +137,25 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
private fun loadPluginLoaderProvidedByPlugins() {
loadersLock.withLock {
JarPluginLoaderImpl.classLoaders.asSequence()
.flatMap { pluginClassLoader ->
ServiceLoader.load(PluginLoader::class.java, pluginClassLoader)
.stream().asSequence()
.asSequence()
.mapNotNull { pluginClassLoader ->
pluginClassLoader.getResourceAsStream(JvmPlugin::class.qualifiedName!!)?.use(InputStream::readBytes)
?.let(::String)?.let { it to pluginClassLoader }
}
.forEach { provider ->
.forEach { (pluginQualifiedName, classLoader) ->
val pluginLoader = kotlin.runCatching {
provider.get()
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 ${provider.type().canonicalName}." },
it
{ "Could not load PluginLoader ${pluginQualifiedName}." },
PluginLoadException("Could not load PluginLoader ${pluginQualifiedName}.", it)
)
return@forEach
}
_pluginLoaders.add(pluginLoader)
logger.info { "Successfully loaded PluginLoader ${provider.type().canonicalName}." }
logger.info { "Successfully loaded PluginLoader ${pluginQualifiedName}." }
}
}
}

View File

@ -23,10 +23,9 @@ package net.mamoe.mirai.console.pure
import com.vdurmont.semver4j.Semver
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import net.mamoe.mirai.console.ConsoleFrontEndImplementation
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription
import net.mamoe.mirai.console.MiraiConsoleImplementation
import net.mamoe.mirai.console.data.MultiFilePluginDataStorage
@ -72,7 +71,11 @@ class MiraiConsoleImplementationPure
override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
override val configStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")),
override val configStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config"))
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(NamedSupervisorJob("MiraiConsoleImplementationPure")) {
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(NamedSupervisorJob("MiraiConsoleImplementationPure") +
CoroutineExceptionHandler { coroutineContext, throwable ->
val coroutineName = coroutineContext[CoroutineName]?.name ?: "<unnamed>"
MiraiConsole.mainLogger.error("Exception in coroutine $coroutineName", throwable)
}) {
override val consoleInput: ConsoleInput get() = ConsoleInputImpl
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver {