mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-13 14:50:43 +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.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||
import net.mamoe.mirai.utils.error
|
||||
import net.mamoe.mirai.utils.info
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNotEquals
|
||||
@ -45,7 +46,8 @@ public object PluginDependOnOther : KotlinPlugin(
|
||||
logger.info { "Gson located $gsonC <${gsonC.classLoader}>" }
|
||||
assertSame(gsonC, Class.forName(gsonC.name, false, pluginDepDynDownload.classLoader))
|
||||
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> {
|
||||
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}" }
|
||||
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")
|
||||
@ -210,7 +230,8 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
|
||||
findLoadedClass(name)?.let { return it }
|
||||
try {
|
||||
return super.findClass(name)
|
||||
} catch (ignored: ClassNotFoundException) {}
|
||||
} catch (ignored: ClassNotFoundException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
@ -265,11 +286,48 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
|
||||
|
||||
internal fun loadedClass(name: String): Class<*>? = super.findLoadedClass(name)
|
||||
|
||||
//// 只允许插件 getResource 时获取插件自身资源, https://github.com/mamoe/mirai-console/issues/205
|
||||
override fun getResources(name: String?): Enumeration<URL> = findResources(name)
|
||||
override fun getResource(name: String?): URL? = findResource(name)
|
||||
// getResourceAsStream 在 URLClassLoader 中通过 getResource 确定资源
|
||||
// 因此无需 override getResourceAsStream
|
||||
private fun getRes(name: String, shared: Boolean): Enumeration<URL> {
|
||||
val src = mutableListOf<Enumeration<URL>>(
|
||||
findResources(name),
|
||||
)
|
||||
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 {
|
||||
return "JvmPluginClassLoader{${file.name}}"
|
||||
@ -278,3 +336,35 @@ internal class JvmPluginClassLoaderN : URLClassLoader {
|
||||
|
||||
private fun String.pkgName(): String = substringBeforeLast('.', "")
|
||||
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