Move Plugin.enable and Plugin.disable into PluginManager for a better java API.

This commit is contained in:
Him188 2020-08-23 17:18:09 +08:00
parent c5389201e1
commit 7ecffb239d
6 changed files with 81 additions and 37 deletions

View File

@ -13,6 +13,7 @@ import kotlinx.coroutines.*
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.data.PluginDataStorage import net.mamoe.mirai.console.data.PluginDataStorage
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
import net.mamoe.mirai.console.internal.data.createInstanceOrNull
import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
import net.mamoe.mirai.console.plugin.PluginLoadException import net.mamoe.mirai.console.plugin.PluginLoadException
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
@ -25,7 +26,6 @@ import net.mamoe.yamlkt.Yaml
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.reflect.full.createInstance
internal object JarPluginLoaderImpl : internal object JarPluginLoaderImpl :
AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"), AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"),
@ -90,14 +90,14 @@ internal object JarPluginLoaderImpl :
jarFile = file jarFile = file
).kotlin.run { ).kotlin.run {
objectInstance objectInstance
?: kotlin.runCatching { createInstance() }.getOrNull() ?: createInstanceOrNull()
?: (java.constructors + java.declaredConstructors) ?: (java.constructors + java.declaredConstructors)
.firstOrNull { it.parameterCount == 0 } .firstOrNull { it.parameterCount == 0 }
?.apply { kotlin.runCatching { isAccessible = true } } ?.apply { kotlin.runCatching { isAccessible = true } }
?.newInstance() ?.newInstance()
} ?: error("No Kotlin object or public no-arg constructor found") } ?: error("No Kotlin object or public no-arg constructor found for $mainClassName")
check(main is JvmPlugin) { "The main class of Jar plugin must extend JvmPlugin, recommending JavaPlugin or KotlinPlugin" } check(main is JvmPlugin) { "The main class of Jar plugin must extend JvmPlugin, recommended JavaPlugin or KotlinPlugin" }
if (main is JvmPluginInternal) { if (main is JvmPluginInternal) {
main._description = description main._description = description
@ -110,6 +110,7 @@ internal object JarPluginLoaderImpl :
} }
override fun enable(plugin: JvmPlugin) { override fun enable(plugin: JvmPlugin) {
if (plugin.isEnabled) throw IllegalStateException("Plugin is already enabled")
ensureActive() ensureActive()
if (plugin is JvmPluginInternal) { if (plugin is JvmPluginInternal) {
plugin.internalOnEnable() plugin.internalOnEnable()
@ -117,6 +118,8 @@ internal object JarPluginLoaderImpl :
} }
override fun disable(plugin: JvmPlugin) { override fun disable(plugin: JvmPlugin) {
if (!plugin.isEnabled) throw IllegalStateException("Plugin is already disabled")
if (plugin is JvmPluginInternal) { if (plugin is JvmPluginInternal) {
plugin.internalOnDisable() plugin.internalOnDisable()
} else plugin.onDisable() } else plugin.onDisable()

View File

@ -16,10 +16,10 @@ import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.internal.data.mkdir import net.mamoe.mirai.console.internal.data.mkdir
import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader
import net.mamoe.mirai.console.plugin.ResourceContainer.Companion.asResourceContainer import net.mamoe.mirai.console.plugin.ResourceContainer.Companion.asResourceContainer
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
import net.mamoe.mirai.console.plugin.safeLoader
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import java.io.InputStream import java.io.InputStream
import java.nio.file.Path import java.nio.file.Path

View File

@ -61,7 +61,7 @@ internal object PluginManagerImpl : PluginManager {
init { init {
MiraiConsole.coroutineContext[Job]!!.invokeOnCompletion { MiraiConsole.coroutineContext[Job]!!.invokeOnCompletion {
plugins.forEach(Plugin::disable) plugins.forEach { it.disable() }
} }
} }

View File

@ -12,6 +12,8 @@
package net.mamoe.mirai.console.plugin package net.mamoe.mirai.console.plugin
import net.mamoe.mirai.console.command.CommandOwner import net.mamoe.mirai.console.command.CommandOwner
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.disable
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
import net.mamoe.mirai.console.plugin.dsecription.PluginDescription import net.mamoe.mirai.console.plugin.dsecription.PluginDescription
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
@ -21,6 +23,10 @@ import java.nio.file.Path
/** /**
* 表示一个 mirai-console 插件. * 表示一个 mirai-console 插件.
* *
* @see PluginManager.enable 启用一个插件
* @see PluginManager.disable 禁用一个插件
* @see PluginManager.description 获取一个插件的 [描述][PluginDescription]
*
* @see PluginDescription 插件描述 需由 [PluginLoader] 帮助提供[PluginLoader.description] * @see PluginDescription 插件描述 需由 [PluginLoader] 帮助提供[PluginLoader.description]
* @see JvmPlugin Java, Kotlin 或其他 JVM 平台插件 * @see JvmPlugin Java, Kotlin 或其他 JVM 平台插件
* @see PluginFileExtensions 支持文件系统存储的扩展 * @see PluginFileExtensions 支持文件系统存储的扩展
@ -31,8 +37,8 @@ public interface Plugin : CommandOwner {
/** /**
* 判断此插件是否已启用 * 判断此插件是否已启用
* *
* @see disable 关闭这个插件 * @see PluginManager.enable 启用一个插件
* @see enable 启用这个插件 * @see PluginManager.disable 禁用一个插件
*/ */
public val isEnabled: Boolean public val isEnabled: Boolean
@ -42,33 +48,6 @@ public interface Plugin : CommandOwner {
public val loader: PluginLoader<*, *> public val loader: PluginLoader<*, *>
} }
/**
* 获取插件描述
*/
public val Plugin.description: PluginDescription get() = safeLoader.getDescription(this)
/**
* 禁用这个插件
*
* @see PluginLoader.disable
*/
public fun Plugin.disable(): Unit = safeLoader.disable(this)
/**
* 启用这个插件
*
* @see PluginLoader.enable
*/
public fun Plugin.enable(): Unit = safeLoader.enable(this)
/**
* 经过泛型类型转换的 [PluginLoader]
*/
@get:JvmSynthetic
@Suppress("UNCHECKED_CAST")
public inline val <P : Plugin> P.safeLoader: PluginLoader<P, PluginDescription>
get() = this.loader as PluginLoader<P, PluginDescription>
/** /**
* 支持文件系统存储的扩展. * 支持文件系统存储的扩展.
* *

View File

@ -38,7 +38,7 @@ public interface PluginLoader<P : Plugin, D : PluginDescription> {
* *
* console 启动时, [PluginManager] 会获取所有 [PluginDescription], 分析依赖关系, 确认插件加载顺序. * console 启动时, [PluginManager] 会获取所有 [PluginDescription], 分析依赖关系, 确认插件加载顺序.
* *
* **实现细节:** 此函数*应该* console 启动时被调用一次. 但取决于前端实现不同, 可能由于被一些插件需要, 此函数也可能会被多次调用. * **实现细节:** 此函数*应该* console 启动时被调用一次. 但取决于前端实现不同, 由于被一些插件需要, 此函数也可能会被多次调用.
*/ */
public fun listPlugins(): List<D> public fun listPlugins(): List<D>
@ -52,6 +52,7 @@ public interface PluginLoader<P : Plugin, D : PluginDescription> {
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如无法读取插件信息等). * @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如无法读取插件信息等).
* *
* @see PluginDescription 插件描述 * @see PluginDescription 插件描述
* @see getDescription receiver, 接受参数的版本.
*/ */
@get:JvmName("getPluginDescription") @get:JvmName("getPluginDescription")
@get:Throws(PluginLoadException::class) @get:Throws(PluginLoadException::class)
@ -71,17 +72,53 @@ public interface PluginLoader<P : Plugin, D : PluginDescription> {
/** /**
* 启用这个插件. * 启用这个插件.
* *
* **实现约定**: 若插件已经启用, 抛出 * **实现细节**: 此函数可抛出 [PluginLoadException] 作为正常失败原因, 其他任意异常都属于意外错误.
* 当异常发生时, 插件将会直接被放弃加载, 并影响依赖它的其他插件.
*
* @throws IllegalStateException 当插件已经启用时抛出
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如找不到主类等).
*/ */
@Throws(IllegalStateException::class, PluginLoadException::class)
public fun enable(plugin: P) public fun enable(plugin: P)
/**
* 禁用这个插件.
*
* **实现细节**: 此函数可抛出 [PluginLoadException] 作为正常失败原因, 其他任意异常都属于意外错误.
* 当异常发生时, 插件将会直接被放弃加载, 并影响依赖它的其他插件.
*
* @throws IllegalStateException 当插件已经禁用时抛出
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如找不到主类等).
*/
@Throws(IllegalStateException::class, PluginLoadException::class)
public fun disable(plugin: P) public fun disable(plugin: P)
} }
/**
* 获取此插件的描述.
*
* **实现细节**: 此函数只允许抛出 [PluginLoadException] 作为正常失败原因, 其他任意异常都属于意外错误.
*
* 若在 console 启动并加载所有插件的过程中, 本函数抛出异常, 则会放弃此插件的加载, 并影响依赖它的其他插件.
*
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如无法读取插件信息等).
*
* @see PluginDescription 插件描述
* @see PluginLoader.description
*/
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@JvmSynthetic @JvmSynthetic
public inline fun <D : PluginDescription, P : Plugin> PluginLoader<in P, out D>.getDescription(plugin: P): D = public inline fun <D : PluginDescription, P : Plugin> PluginLoader<in P, out D>.getDescription(plugin: P): D =
plugin.description plugin.description
/**
* 在加载插件过程中遇到的意料之中的问题.
*
* @see PluginLoader.load
* @see PluginLoader.enable
* @see PluginLoader.disable
* @see PluginLoader.description
*/
public open class PluginLoadException : RuntimeException { public open class PluginLoadException : RuntimeException {
public constructor() : super() public constructor() : super()
public constructor(message: String?) : super(message) public constructor(message: String?) : super(message)

View File

@ -89,11 +89,36 @@ public interface PluginManager {
*/ */
public val Plugin.description: PluginDescription public val Plugin.description: PluginDescription
/**
* 禁用这个插件
*
* @see PluginLoader.disable
*/
public fun Plugin.disable(): Unit = safeLoader.disable(this)
/**
* 启用这个插件
*
* @see PluginLoader.enable
*/
public fun Plugin.enable(): Unit = safeLoader.enable(this)
/**
* 经过泛型类型转换的 [PluginLoader]
*/
@get:JvmSynthetic
@Suppress("UNCHECKED_CAST")
public val <P : Plugin> P.safeLoader: PluginLoader<P, PluginDescription>
get() = this.loader as PluginLoader<P, PluginDescription>
public companion object INSTANCE : PluginManager by PluginManagerImpl { public companion object INSTANCE : PluginManager by PluginManagerImpl {
// due to Kotlin's bug // due to Kotlin's bug
public override val Plugin.description: PluginDescription get() = PluginManagerImpl.run { description } public override val Plugin.description: PluginDescription get() = PluginManagerImpl.run { description }
public override fun PluginLoader<*, *>.register(): Boolean = PluginManagerImpl.run { register() } public override fun PluginLoader<*, *>.register(): Boolean = PluginManagerImpl.run { register() }
public override fun PluginLoader<*, *>.unregister(): Boolean = PluginManagerImpl.run { unregister() } public override fun PluginLoader<*, *>.unregister(): Boolean = PluginManagerImpl.run { unregister() }
public override fun Plugin.disable(): Unit = PluginManagerImpl.run { disable() }
public override fun Plugin.enable(): Unit = PluginManagerImpl.run { enable() }
public override val <P : Plugin> P.safeLoader: PluginLoader<P, PluginDescription> get() = PluginManagerImpl.run { safeLoader }
} }
} }