From e8bb0f0bb499b67da2dfac760e1eced2495c3eed Mon Sep 17 00:00:00 2001 From: Him188 Date: Mon, 31 Aug 2020 19:23:56 +0800 Subject: [PATCH] Update Plugin load mechanism for compatibility with Android --- .../internal/plugin/JarPluginLoaderImpl.kt | 52 ++++++++++--------- .../internal/plugin/PluginManagerImpl.kt | 25 ++++----- .../pure/MiraiConsoleImplementationPure.kt | 11 ++-- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt index 2b54d9392..333e6009a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JarPluginLoaderImpl.kt @@ -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(".jar"), @@ -56,33 +58,35 @@ internal object JarPluginLoaderImpl : override fun Sequence.extractPlugins(): List { ensureActive() - fun ServiceLoader.loadAll(file: File?): Sequence { - return stream().asSequence().mapNotNull { + fun Sequence.loadAll(): Sequence { + 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 ?: ""}", it) - }.getOrNull() + val clazz = + Class.forName(pluginQualifiedName, true, classLoader).cast>() + 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) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt index 5aa1b3c8d..3e4a36f85 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/PluginManagerImpl.kt @@ -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>>() + 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}." } } } } diff --git a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt index 19e5305f1..cdbdbb898 100644 --- a/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt +++ b/frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt @@ -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 ?: "" + MiraiConsole.mainLogger.error("Exception in coroutine $coroutineName", throwable) + }) { override val consoleInput: ConsoleInput get() = ConsoleInputImpl override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver {