JvmPluginClassLoaderAccess

This commit is contained in:
Karlatemp 2022-05-23 18:08:18 +08:00
parent b0711ed9ab
commit 237320317e
No known key found for this signature in database
GPG Key ID: BA173CA2B9956C59
5 changed files with 146 additions and 4 deletions

View File

@ -11,7 +11,6 @@
package net.mamoe.console.itest.serviceloader
import net.mamoe.mirai.console.internal.plugin.JvmPluginClassLoaderN
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
import net.mamoe.mirai.utils.info
@ -21,9 +20,10 @@ import kotlin.test.assertEquals
internal object PMain : KotlinPlugin(JvmPluginDescription("net.mamoe.console.itest.serviceloader", "0.0.0")) {
init {
val cl = PMain.javaClass.classLoader as JvmPluginClassLoaderN
cl.pluginSharedCL.addLib(File("modules/module-service-loader-typedef-0.0.0.jar"))
cl.pluginSharedCL.addLib(File("modules/module-service-loader-impl-0.0.0.jar"))
val access = jvmPluginClassLoaderAccess
val sharedCL = access.pluginSharedLibrariesClassLoader
access.addToPath(sharedCL, File("modules/module-service-loader-typedef-0.0.0.jar"))
access.addToPath(sharedCL, File("modules/module-service-loader-impl-0.0.0.jar"))
}
override fun onEnable() {

View File

@ -2157,6 +2157,7 @@ public abstract class net/mamoe/mirai/console/plugin/jvm/AbstractJvmPlugin : net
public synthetic fun <init> (Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getAutoSaveIntervalMillis ()Lkotlin/ranges/LongRange;
public final fun getDataHolderName ()Ljava/lang/String;
protected final fun getJvmPluginClassLoaderAccess ()Lnet/mamoe/mirai/console/plugin/jvm/JvmPluginClassLoaderAccess;
public final fun getLoader ()Lnet/mamoe/mirai/console/plugin/jvm/JvmPluginLoader;
public synthetic fun getLoader ()Lnet/mamoe/mirai/console/plugin/loader/PluginLoader;
public final fun permissionId (Ljava/lang/String;)Lnet/mamoe/mirai/console/permission/PermissionId;
@ -2213,6 +2214,15 @@ public final class net/mamoe/mirai/console/plugin/jvm/JvmPlugin$Companion {
public final synthetic fun onLoad (Lnet/mamoe/mirai/console/plugin/jvm/JvmPlugin;Lnet/mamoe/mirai/console/extension/PluginComponentStorage;)V
}
public abstract interface class net/mamoe/mirai/console/plugin/jvm/JvmPluginClassLoaderAccess {
public abstract fun addToPath (Ljava/lang/ClassLoader;Ljava/io/File;)V
public abstract fun downloadAndAddToPath (Ljava/lang/ClassLoader;Ljava/util/Collection;)V
public abstract fun getPluginClassLoader ()Ljava/lang/ClassLoader;
public abstract fun getPluginFile ()Ljava/io/File;
public abstract fun getPluginIndependentLibrariesClassLoader ()Ljava/lang/ClassLoader;
public abstract fun getPluginSharedLibrariesClassLoader ()Ljava/lang/ClassLoader;
}
public abstract interface class net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription : net/mamoe/mirai/console/plugin/description/PluginDescription {
public static final field Companion Lnet/mamoe/mirai/console/plugin/jvm/JvmPluginDescription$Companion;
public static fun loadFromResource ()Lnet/mamoe/mirai/console/plugin/jvm/JvmPluginDescription;

View File

@ -12,6 +12,7 @@ package net.mamoe.mirai.console.internal.plugin
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugin.jvm.ExportManager
import net.mamoe.mirai.console.plugin.jvm.JvmPluginClassLoaderAccess
import net.mamoe.mirai.utils.*
import org.eclipse.aether.artifact.Artifact
import org.eclipse.aether.graph.DependencyFilter
@ -163,6 +164,7 @@ internal class DynLibClassLoader : URLClassLoader {
@Suppress("JoinDeclarationAndAssignment")
internal class JvmPluginClassLoaderN : URLClassLoader {
val openaccess: JvmPluginClassLoaderAccess = OpenAccess()
val file: File
val ctx: JvmPluginsLoadingCtx
val sharedLibrariesLogger: DynLibClassLoader
@ -224,10 +226,12 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
internal var declaredFilter: ExportManager? = null
val sharedClLoadedDependencies = mutableSetOf<String>()
val privateClLoadedDependencies = mutableSetOf<String>()
internal fun containsSharedDependency(
dependency: String
): Boolean {
if (dependency in sharedClLoadedDependencies) return true
if (dependency in privateClLoadedDependencies) return true
return dependencies.any { it.containsSharedDependency(dependency) }
}
@ -282,6 +286,7 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
sharedClLoadedDependencies.add(artifact.depId())
} else {
pluginIndependentCL.addLib(lib)
privateClLoadedDependencies.add(artifact.depId())
}
logger.debug { "Linked $artifact $linkType <${if (shared) pluginSharedCL else pluginIndependentCL}>" }
}
@ -432,6 +437,50 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
return "JvmPluginClassLoader{${file.name}}"
}
inner class OpenAccess : JvmPluginClassLoaderAccess {
override val pluginFile: File
get() = this@JvmPluginClassLoaderN.file
override val pluginClassLoader: ClassLoader
get() = this@JvmPluginClassLoaderN
override val pluginSharedLibrariesClassLoader: ClassLoader
get() = pluginSharedCL
override val pluginIndependentLibrariesClassLoader: ClassLoader
get() = pluginIndependentCL
private val permitted by lazy {
arrayOf(
this@JvmPluginClassLoaderN,
pluginSharedCL,
pluginIndependentCL,
)
}
override fun addToPath(classLoader: ClassLoader, file: File) {
if (classLoader !in permitted) {
throw IllegalArgumentException("Unsupported classloader or cross plugin accessing: $classLoader")
}
if (classLoader == this@JvmPluginClassLoaderN) {
this@JvmPluginClassLoaderN.addURL(file.toURI().toURL())
return
}
classLoader as DynLibClassLoader
classLoader.addLib(file)
}
override fun downloadAndAddToPath(classLoader: ClassLoader, dependencies: Collection<String>) {
if (classLoader !in permitted) {
throw IllegalArgumentException("Unsupported classloader or cross plugin accessing: $classLoader")
}
if (classLoader === this@JvmPluginClassLoaderN) {
throw IllegalArgumentException("Only support download dependencies to `plugin[Shared/Independent]LibrariesClassLoader`")
}
this@JvmPluginClassLoaderN.linkLibraries(
linkedLogger, dependencies, classLoader === pluginSharedCL
)
}
}
}
private val JavaSystemPlatformClassLoader: ClassLoader by lazy {

View File

@ -14,6 +14,7 @@ package net.mamoe.mirai.console.plugin.jvm
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
import net.mamoe.mirai.console.data.PluginConfig
import net.mamoe.mirai.console.data.PluginData
import net.mamoe.mirai.console.internal.plugin.JvmPluginClassLoaderN
import net.mamoe.mirai.console.internal.plugin.JvmPluginInternal
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.console.permission.PermissionService
@ -77,6 +78,21 @@ public abstract class AbstractJvmPlugin @JvmOverloads constructor(
@ConsoleExperimentalApi
public override val autoSaveIntervalMillis: LongRange = 30.secondsToMillis..10.minutesToMillis
/**
* 获取 [JvmPluginClassLoaderAccess]
*
* : 仅插件通过 console 内置插件加载器加载时可用
*
* @since 2.12
*/
protected val jvmPluginClassLoaderAccess: JvmPluginClassLoaderAccess by lazy {
val classLoader = this@AbstractJvmPlugin.javaClass.classLoader
if (classLoader is JvmPluginClassLoaderN) {
return@lazy classLoader.openaccess
}
error("jvmPluginClassLoaderAccess not available for $classLoader")
}
}
/**

View File

@ -0,0 +1,67 @@
/*
* Copyright 2019-2022 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/dev/LICENSE
*/
package net.mamoe.mirai.console.plugin.jvm
import java.io.File
import java.io.IOException
/**
* 动态注册类搜索路径
*
* @since 2.12
* @see AbstractJvmPlugin.jvmPluginClassLoaderAccess
*/
public interface JvmPluginClassLoaderAccess {
public val pluginFile: File
/**
* 插件本体的 [ClassLoader], 负责加载插件本身的类数据
*
* JVM NAME: `${pluginFile.name}`
*/
public val pluginClassLoader: ClassLoader
/**
* 插件所使用的依赖, 可共享, 当其他插件依赖于当前插件时, 其他插件可以从此 [ClassLoader] 搜索类引用
*
* JVM NAME: ${pluginFile.name}&#91;shared]
*/
public val pluginSharedLibrariesClassLoader: ClassLoader
/**
* 插件内部使用的依赖, 不公开给其他插件, 其他插件搜索时不会搜索到此插件的依赖
*
* JVM NAME: ${pluginFile.name}&#91;private]
*/
public val pluginIndependentLibrariesClassLoader: ClassLoader
/**
* [file] 加入 [classLoader] 的搜索路径内
*
* @throws IllegalArgumentException [classLoader] 不是 [pluginClassLoader],
* [pluginIndependentLibrariesClassLoader], [pluginSharedLibrariesClassLoader] 时抛出
*/
@kotlin.jvm.Throws(IllegalArgumentException::class)
public fun addToPath(classLoader: ClassLoader, file: File)
/**
* 下载依赖, 并注册进 [classLoader]
*
* : 如果需要同时修改 [pluginSharedLibrariesClassLoader] [pluginIndependentLibrariesClassLoader],
* 请先对 [pluginSharedLibrariesClassLoader] 以避免出现不和预期的问题
*
* @throws IllegalArgumentException [classLoader] 不是
* [pluginSharedLibrariesClassLoader], [pluginIndependentLibrariesClassLoader]
* 时抛出
* @throws Exception 网络错误 / 依赖未找到
*/
@kotlin.jvm.Throws(IllegalArgumentException::class, Exception::class)
public fun downloadAndAddToPath(classLoader: ClassLoader, dependencies: Collection<String>)
}