Selaa lähdekoodia

Resources & ServiceLoader resolving

Karlatemp 4 vuotta sitten
vanhempi
sitoutus
d6f4e2e567
15 muutettua tiedostoa jossa 300 lisäystä ja 7 poistoa
  1. 3 1
      mirai-console/backend/integration-test/testers/plugin-depend-on-other/src/PluginDependOnOther.kt
  2. 0 0
      mirai-console/backend/integration-test/testers/service-loader/module-service-loader-impl/.nested-module.txt
  3. 27 0
      mirai-console/backend/integration-test/testers/service-loader/module-service-loader-impl/build.gradle.kts
  4. 10 0
      mirai-console/backend/integration-test/testers/service-loader/module-service-loader-impl/resources/META-INF/services/net.mamoe.console.integrationtest.mod.servicetypedef.ServiceTypedef
  5. 14 0
      mirai-console/backend/integration-test/testers/service-loader/module-service-loader-impl/src/ServiceImpl.kt
  6. 0 0
      mirai-console/backend/integration-test/testers/service-loader/module-service-loader-typedef/.nested-module.txt
  7. 13 0
      mirai-console/backend/integration-test/testers/service-loader/module-service-loader-typedef/src/ServiceTypedef.kt
  8. 10 0
      mirai-console/backend/integration-test/testers/service-loader/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin
  9. 0 0
      mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/.nested-module.txt
  10. 27 0
      mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/build.gradle.kts
  11. 10 0
      mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/resources/META-INF/services/net.mamoe.console.integrationtest.mod.servicetypedef.ServiceTypedef
  12. 10 0
      mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin
  13. 40 0
      mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/src/PMain.kt
  14. 40 0
      mirai-console/backend/integration-test/testers/service-loader/src/PMain.kt
  15. 96 6
      mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginClassLoader.kt

+ 3 - 1
mirai-console/backend/integration-test/testers/plugin-depend-on-other/src/PluginDependOnOther.kt

@@ -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
mirai-console/backend/integration-test/testers/service-loader/module-service-loader-impl/.nested-module.txt


+ 27 - 0
mirai-console/backend/integration-test/testers/service-loader/module-service-loader-impl/build.gradle.kts

@@ -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"))
+}

+ 10 - 0
mirai-console/backend/integration-test/testers/service-loader/module-service-loader-impl/resources/META-INF/services/net.mamoe.console.integrationtest.mod.servicetypedef.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.integrationtest.mod.serviceimpl.ServiceImpl

+ 14 - 0
mirai-console/backend/integration-test/testers/service-loader/module-service-loader-impl/src/ServiceImpl.kt

@@ -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
mirai-console/backend/integration-test/testers/service-loader/module-service-loader-typedef/.nested-module.txt


+ 13 - 0
mirai-console/backend/integration-test/testers/service-loader/module-service-loader-typedef/src/ServiceTypedef.kt

@@ -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 {
+}

+ 10 - 0
mirai-console/backend/integration-test/testers/service-loader/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin

@@ -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
mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/.nested-module.txt


+ 27 - 0
mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/build.gradle.kts

@@ -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"))
+}

+ 10 - 0
mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/resources/META-INF/services/net.mamoe.console.integrationtest.mod.servicetypedef.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.ndep.PS

+ 10 - 0
mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin

@@ -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

+ 40 - 0
mirai-console/backend/integration-test/testers/service-loader/service-loader-2dep-plugin/src/PMain.kt

@@ -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)
+    }
+}

+ 40 - 0
mirai-console/backend/integration-test/testers/service-loader/src/PMain.kt

@@ -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)
+    }
+}

+ 96 - 6
mirai-console/backend/mirai-console/src/internal/plugin/JvmPluginClassLoader.kt

@@ -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()
+            }
+        }
+    }
+}