Rearrange JvmPlugin implementations

This commit is contained in:
Him188 2020-05-23 18:46:33 +08:00
parent d37d6881fc
commit 11bdb438eb
4 changed files with 238 additions and 70 deletions

View File

@ -0,0 +1,83 @@
package net.mamoe.mirai.console.plugins.builtin
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugins.AbstractFilePluginLoader
import net.mamoe.mirai.console.plugins.PluginLoadException
import net.mamoe.mirai.console.plugins.PluginsLoader
import net.mamoe.mirai.utils.MiraiLogger
import java.io.File
import kotlin.coroutines.CoroutineContext
import kotlin.reflect.full.createInstance
/**
* 内建的 Jar (JVM) 插件加载器
*/
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar"),
CoroutineScope {
private val logger: MiraiLogger by lazy {
MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
}
override val coroutineContext: CoroutineContext by lazy {
MiraiConsole.coroutineContext + SupervisorJob(
MiraiConsole.coroutineContext[Job]
) + CoroutineExceptionHandler { _, throwable ->
logger.error("Unhandled Jar plugin exception: ${throwable.message}", throwable)
}
}
private val classLoader: PluginsLoader =
PluginsLoader(this.javaClass.classLoader)
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
TODO(
"""
CHECK IS JAR FILE AND CAN BE READ
READ JAR FILE, EXTRACT PLUGIN DESCRIPTION
SET JvmPluginDescription._file
RETURN PLUGIN
""".trimIndent()
)
}
@Throws(PluginLoadException::class)
override fun load(description: JvmPluginDescription): JvmPlugin = description.runCatching {
val main = classLoader.loadPluginMainClassByJarFile(name, mainClassName, file).kotlin.run {
objectInstance
?: kotlin.runCatching { createInstance() }.getOrNull()
?: (java.constructors + java.declaredConstructors)
.firstOrNull { it.parameterCount == 0 }
?.apply { kotlin.runCatching { isAccessible = true } }
?.newInstance()
} ?: error("No Kotlin object or public no-arg constructor found")
check(main is JvmPlugin) { "The main class of Jar plugin must extend JvmPlugin, recommending JavaPlugin or KotlinPlugin" }
if (main is JvmPluginImpl) {
main._description = description
}
TODO(
"""
FIND PLUGIN MAIN, THEN LOAD
SET JvmPluginImpl._description
SET JvmPluginImpl._intrinsicCoroutineContext
""".trimIndent()
)
// no need to check dependencies
}.getOrElse {
throw PluginLoadException(
"Exception while loading ${description.name}",
it
)
}
override fun enable(plugin: JvmPlugin) = plugin.onEnable()
override fun disable(plugin: JvmPlugin) = plugin.onDisable()
}

View File

@ -9,24 +9,31 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS")
package net.mamoe.mirai.console.plugins
package net.mamoe.mirai.console.plugins.builtin
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.scheduler.PluginScheduler
import net.mamoe.mirai.console.plugins.Plugin
import net.mamoe.mirai.console.utils.JavaPluginScheduler
import net.mamoe.mirai.utils.MiraiLogger
import java.io.File
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/**
* Java Kotlin Jar 插件
*
* @see JavaPlugin Java 插件
* @see KotlinPlugin Kotlin 插件
*/
interface JvmPlugin : Plugin, CoroutineScope {
/** 日志 */
val logger: MiraiLogger
/** 插件描述 */
val description: JvmPluginDescription
@JvmDefault
@ -42,7 +49,9 @@ interface JvmPlugin : Plugin, CoroutineScope {
}
}
/**
* Java 插件的父类
*/
abstract class JavaPlugin @JvmOverloads constructor(
coroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
@ -50,7 +59,8 @@ abstract class JavaPlugin @JvmOverloads constructor(
/**
* Java API Scheduler
*/
val scheduler: PluginScheduler? = PluginScheduler(this.coroutineContext)
val scheduler: JavaPluginScheduler =
JavaPluginScheduler(this.coroutineContext)
}
abstract class KotlinPlugin @JvmOverloads constructor(
@ -59,41 +69,6 @@ abstract class KotlinPlugin @JvmOverloads constructor(
// that's it
}
@Serializable
data class JvmPluginDescription internal constructor( // serializer 可以用这个构造器
override val kind: PluginKind,
override val name: String,
override val author: String,
override val version: String,
override val info: String,
override val loadBefore: List<String>,
override val dependencies: List<PluginDependency>
) : PluginDescription, FilePluginDescription {
/**
* 在手动实现时使用这个构造器.
*/
@Suppress("unused")
constructor(
kind: PluginKind,
name: String,
author: String,
version: String,
info: String,
loadBefore: List<String>,
depends: List<PluginDependency>,
file: File
) : this(kind, name, author, version, info, loadBefore, depends) {
this._file = file
}
@Suppress("PropertyName")
@Transient
internal var _file: File? = null
override val file: File
get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null")
}
internal abstract class JvmPluginImpl(
parentCoroutineContext: CoroutineContext
) : JvmPlugin, CoroutineScope {
@ -102,6 +77,11 @@ internal abstract class JvmPluginImpl(
*/
@Suppress("PropertyName")
internal lateinit var _description: JvmPluginDescription
// for future use
@Suppress("PropertyName")
internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext
override val description: JvmPluginDescription get() = _description
final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) }
@ -109,37 +89,10 @@ internal abstract class JvmPluginImpl(
final override val coroutineContext: CoroutineContext by lazy {
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
.plus(parentCoroutineContext)
.plus(SupervisorJob(parentCoroutineContext[Job]))
.plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext
}
}
/**
* 内建的 Jar (JVM) 插件加载器
*/
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar") {
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
TODO(
"""
CHECK IS JAR FILE AND CAN BE READ
READ JAR FILE, EXTRACT PLUGIN DESCRIPTION
SET JvmPluginDescription._file
RETURN PLUGIN
""".trimIndent()
)
}
@Throws(PluginLoadException::class)
override fun load(description: JvmPluginDescription): JvmPlugin {
TODO("FIND PLUGIN MAIN, THEN LOAD")
// no need to check dependencies
}
override fun enable(plugin: JvmPlugin) = plugin.onEnable()
override fun disable(plugin: JvmPlugin) = plugin.onDisable()
}
/*
object PluginManagerOld {
/**

View File

@ -0,0 +1,48 @@
package net.mamoe.mirai.console.plugins.builtin
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.mamoe.mirai.console.plugins.FilePluginDescription
import net.mamoe.mirai.console.plugins.PluginDependency
import net.mamoe.mirai.console.plugins.PluginDescription
import net.mamoe.mirai.console.plugins.PluginKind
import java.io.File
@Serializable
class JvmPluginDescription internal constructor(
override val kind: PluginKind,
override val name: String,
@SerialName("main")
val mainClassName: String,
override val author: String,
override val version: String,
override val info: String,
override val dependencies: List<PluginDependency>
) : PluginDescription, FilePluginDescription {
/**
* 在手动实现时使用这个构造器.
*/
@Suppress("unused")
constructor(
kind: PluginKind, name: String, mainClassName: String, author: String,
version: String, info: String, depends: List<PluginDependency>,
file: File
) : this(kind, name, mainClassName, author, version, info, depends) {
this._file = file
}
override val file: File
get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null")
@Suppress("PropertyName")
@Transient
@JvmField
internal var _file: File? = null
override fun toString(): String {
return "JvmPluginDescription(kind=$kind, name='$name', mainClassName='$mainClassName', author='$author', version='$version', info='$info', dependencies=$dependencies, _file=$_file)"
}
}

View File

@ -0,0 +1,84 @@
/*
* 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.plugins
import kotlinx.serialization.Serializable
import java.io.File
/** 插件类型 */
enum class PluginKind {
/** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */
LOADER,
/** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */
NORMAL
}
/**
* 插件描述
*/
interface PluginDescription {
val kind: PluginKind
val name: String
val author: String
val version: String
val info: String
/** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */
val dependencies: List<PluginDependency>
}
/** 插件的一个依赖的信息 */
@Serializable
data class PluginDependency(
/** 依赖插件名 */
val name: String,
/**
* 依赖版本号
* @see versionKind 版本号类型
*/
val version: String,
/** 版本号类型 */
val versionKind: VersionKind,
/**
* 若为 `false`, 插件在找不到此依赖时也能正常加载.
*/
val isOptional: Boolean
) {
enum class VersionKind {
/** 要求依赖精确的版本 */
EXACT,
/** 要求依赖最低版本 */
AT_LEAST,
/** 要求依赖最高版本 */
AT_MOST
}
override fun toString(): String {
return "$name ${versionKind.toEnglishString()}v$version"
}
}
/**
* 基于文件的插件的描述
*/
interface FilePluginDescription : PluginDescription {
val file: File
}
internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) {
PluginDependency.VersionKind.EXACT -> ""
PluginDependency.VersionKind.AT_LEAST -> "at least "
PluginDependency.VersionKind.AT_MOST -> "at most "
}