mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-14 07:10:09 +08:00
Resources & ServiceLoader resolving
This commit is contained in:
parent
94828544cb
commit
d6f4e2e567
@ -12,6 +12,7 @@ package net.mamoe.console.integrationtest.ep.dependonother
|
|||||||
import net.mamoe.console.integrationtest.ep.mcitselftest.MCITSelfTestPlugin
|
import net.mamoe.console.integrationtest.ep.mcitselftest.MCITSelfTestPlugin
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||||
|
import net.mamoe.mirai.utils.error
|
||||||
import net.mamoe.mirai.utils.info
|
import net.mamoe.mirai.utils.info
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertNotEquals
|
import kotlin.test.assertNotEquals
|
||||||
@ -45,7 +46,8 @@ public object PluginDependOnOther : KotlinPlugin(
|
|||||||
logger.info { "Gson located $gsonC <${gsonC.classLoader}>" }
|
logger.info { "Gson located $gsonC <${gsonC.classLoader}>" }
|
||||||
assertSame(gsonC, Class.forName(gsonC.name, false, pluginDepDynDownload.classLoader))
|
assertSame(gsonC, Class.forName(gsonC.name, false, pluginDepDynDownload.classLoader))
|
||||||
assertFailsWith<ClassNotFoundException> {
|
assertFailsWith<ClassNotFoundException> {
|
||||||
Class.forName("com.zaxxer.sparsebits.SparseBitSet") // private in dynamic-dep-download
|
val c = Class.forName("com.zaxxer.sparsebits.SparseBitSet") // private in dynamic-dep-download
|
||||||
|
logger.error { "C: $c, from: ${c.classLoader}" }
|
||||||
}
|
}
|
||||||
assertFailsWith<ClassNotFoundException> {
|
assertFailsWith<ClassNotFoundException> {
|
||||||
Class.forName("net.mamoe.assertion.something.not.existing")
|
Class.forName("net.mamoe.assertion.something.not.existing")
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("UnusedImport")
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
kotlin("plugin.serialization")
|
||||||
|
id("java")
|
||||||
|
}
|
||||||
|
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
explicitApiWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(":mirai-console.integration-test"))
|
||||||
|
api(parent!!.project("module-service-loader-typedef"))
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
net.mamoe.console.integrationtest.mod.serviceimpl.ServiceImpl
|
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* 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.console.integrationtest.mod.serviceimpl
|
||||||
|
|
||||||
|
import net.mamoe.console.integrationtest.mod.servicetypedef.ServiceTypedef
|
||||||
|
|
||||||
|
public class ServiceImpl : ServiceTypedef {
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* 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.console.integrationtest.mod.servicetypedef
|
||||||
|
|
||||||
|
public interface ServiceTypedef {
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
net.mamoe.console.itest.serviceloader.PMain
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("UnusedImport")
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
kotlin("plugin.serialization")
|
||||||
|
id("java")
|
||||||
|
}
|
||||||
|
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
explicitApiWarning()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(":mirai-console.integration-test"))
|
||||||
|
api(parent!!.project("module-service-loader-typedef"))
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
net.mamoe.console.itest.serviceloader.ndep.PS
|
@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
net.mamoe.console.itest.serviceloader.ndep.PMain
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.console.itest.serviceloader.ndep
|
||||||
|
|
||||||
|
import net.mamoe.console.integrationtest.mod.servicetypedef.ServiceTypedef
|
||||||
|
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||||
|
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||||
|
import net.mamoe.mirai.utils.info
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
|
internal class PS : ServiceTypedef
|
||||||
|
|
||||||
|
internal object PMain : KotlinPlugin(JvmPluginDescription("net.mamoe.console.itest.serviceloader-ndp", "0.0.0") {
|
||||||
|
dependsOn("net.mamoe.console.itest.serviceloader")
|
||||||
|
}) {
|
||||||
|
override fun onEnable() {
|
||||||
|
val loader = ServiceLoader.load(
|
||||||
|
Class.forName("net.mamoe.console.integrationtest.mod.servicetypedef.ServiceTypedef"),
|
||||||
|
javaClass.classLoader,
|
||||||
|
)
|
||||||
|
val services = loader.asSequence().map { it.javaClass.name }.toMutableList()
|
||||||
|
services.sort()
|
||||||
|
services.forEach { service ->
|
||||||
|
logger.info { "Service: $service" }
|
||||||
|
}
|
||||||
|
assertEquals(mutableListOf(
|
||||||
|
"net.mamoe.console.integrationtest.mod.serviceimpl.ServiceImpl",
|
||||||
|
"net.mamoe.console.itest.serviceloader.ndep.PS",
|
||||||
|
), services)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
|
|
||||||
|
package net.mamoe.console.itest.serviceloader
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.internal.plugin.DynLibClassLoader
|
||||||
|
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||||
|
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||||
|
import net.mamoe.mirai.utils.info
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
internal object PMain : KotlinPlugin(JvmPluginDescription("net.mamoe.console.itest.serviceloader", "0.0.0")) {
|
||||||
|
init {
|
||||||
|
val cl = PMain.javaClass.classLoader.parent as DynLibClassLoader
|
||||||
|
cl.addLib(File("modules/module-service-loader-typedef-0.0.0.jar"))
|
||||||
|
cl.addLib(File("modules/module-service-loader-impl-0.0.0.jar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnable() {
|
||||||
|
val loader = ServiceLoader.load(
|
||||||
|
Class.forName("net.mamoe.console.integrationtest.mod.servicetypedef.ServiceTypedef"),
|
||||||
|
javaClass.classLoader,
|
||||||
|
)
|
||||||
|
val services = loader.asSequence().map { it.javaClass.name}.toMutableList()
|
||||||
|
services.forEach { service ->
|
||||||
|
logger.info { "Service: $service" }
|
||||||
|
}
|
||||||
|
assertEquals(mutableListOf("net.mamoe.console.integrationtest.mod.serviceimpl.ServiceImpl"), services)
|
||||||
|
}
|
||||||
|
}
|
@ -82,6 +82,26 @@ internal class DynLibClassLoader(
|
|||||||
clName?.let { return "DynLibClassLoader{$it}" }
|
clName?.let { return "DynLibClassLoader{$it}" }
|
||||||
return "DynLibClassLoader@" + hashCode()
|
return "DynLibClassLoader@" + hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getResource(name: String?): URL? {
|
||||||
|
if (name == null) return null
|
||||||
|
findResource(name)?.let { return it }
|
||||||
|
if (parent is DynLibClassLoader) {
|
||||||
|
return parent.getResource(name)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getResources(name: String?): Enumeration<URL> {
|
||||||
|
if (name == null) return Collections.emptyEnumeration()
|
||||||
|
val res = findResources(name)
|
||||||
|
return if (parent is DynLibClassLoader) {
|
||||||
|
res + parent.getResources(name)
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("JoinDeclarationAndAssignment")
|
@Suppress("JoinDeclarationAndAssignment")
|
||||||
@ -210,7 +230,8 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
|
|||||||
findLoadedClass(name)?.let { return it }
|
findLoadedClass(name)?.let { return it }
|
||||||
try {
|
try {
|
||||||
return super.findClass(name)
|
return super.findClass(name)
|
||||||
} catch (ignored: ClassNotFoundException) {}
|
} catch (ignored: ClassNotFoundException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@ -265,11 +286,48 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
|
|||||||
|
|
||||||
internal fun loadedClass(name: String): Class<*>? = super.findLoadedClass(name)
|
internal fun loadedClass(name: String): Class<*>? = super.findLoadedClass(name)
|
||||||
|
|
||||||
//// 只允许插件 getResource 时获取插件自身资源, https://github.com/mamoe/mirai-console/issues/205
|
private fun getRes(name: String, shared: Boolean): Enumeration<URL> {
|
||||||
override fun getResources(name: String?): Enumeration<URL> = findResources(name)
|
val src = mutableListOf<Enumeration<URL>>(
|
||||||
override fun getResource(name: String?): URL? = findResource(name)
|
findResources(name),
|
||||||
// getResourceAsStream 在 URLClassLoader 中通过 getResource 确定资源
|
)
|
||||||
// 因此无需 override getResourceAsStream
|
if (dependencies.isEmpty()) {
|
||||||
|
if (shared) {
|
||||||
|
src.add(sharedLibrariesLogger.getResources(name))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dependencies.forEach { dep ->
|
||||||
|
src.add(dep.getRes(name, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src.add(pluginIndependentCL.getResources(name))
|
||||||
|
|
||||||
|
val resolved = mutableSetOf<URL>()
|
||||||
|
src.forEach { nested -> nested.iterator().forEach { resolved.add(it) } }
|
||||||
|
|
||||||
|
return Collections.enumeration(resolved)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getResources(name: String?): Enumeration<URL> {
|
||||||
|
name ?: return Collections.emptyEnumeration()
|
||||||
|
|
||||||
|
if (name.startsWith("META-INF/mirai-console-plugin/"))
|
||||||
|
return findResources(name)
|
||||||
|
|
||||||
|
return getRes(name, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getResource(name: String?): URL? {
|
||||||
|
name ?: return null
|
||||||
|
if (name.startsWith("META-INF/mirai-console-plugin/"))
|
||||||
|
return findResource(name)
|
||||||
|
findResource(name)?.let { return it }
|
||||||
|
// parent: ctx.sharedLibrariesLoader
|
||||||
|
sharedLibrariesLogger.getResource(name)?.let { return it }
|
||||||
|
dependencies.forEach { dep ->
|
||||||
|
dep.getResource(name)?.let { return it }
|
||||||
|
}
|
||||||
|
return pluginIndependentCL.getResource(name)
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "JvmPluginClassLoader{${file.name}}"
|
return "JvmPluginClassLoader{${file.name}}"
|
||||||
@ -278,3 +336,35 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
|
|||||||
|
|
||||||
private fun String.pkgName(): String = substringBeforeLast('.', "")
|
private fun String.pkgName(): String = substringBeforeLast('.', "")
|
||||||
internal fun Artifact.depId(): String = "$groupId:$artifactId"
|
internal fun Artifact.depId(): String = "$groupId:$artifactId"
|
||||||
|
|
||||||
|
private operator fun <E> Enumeration<E>.plus(next: Enumeration<E>): Enumeration<E> {
|
||||||
|
return compoundEnumerations(listOf(this, next).iterator())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <E> compoundEnumerations(iter: Iterator<Enumeration<E>>): Enumeration<E> {
|
||||||
|
return object : Enumeration<E> {
|
||||||
|
private lateinit var crt: Enumeration<E>
|
||||||
|
override fun hasMoreElements(): Boolean {
|
||||||
|
return (::crt.isInitialized && crt.hasMoreElements()) || iter.hasNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun nextElement(): E {
|
||||||
|
if (::crt.isInitialized) {
|
||||||
|
val c = crt
|
||||||
|
return if (c.hasMoreElements()) {
|
||||||
|
c.nextElement()
|
||||||
|
} else if (iter.hasNext()) {
|
||||||
|
crt = iter.next()
|
||||||
|
nextElement()
|
||||||
|
} else {
|
||||||
|
throw NoSuchElementException()
|
||||||
|
}
|
||||||
|
} else if (iter.hasNext()) {
|
||||||
|
crt = iter.next()
|
||||||
|
return nextElement()
|
||||||
|
} else {
|
||||||
|
throw NoSuchElementException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user