mirror of
https://github.com/LamGC/ScalaBot.git
synced 2025-04-29 22:27:31 +00:00
feat(extension): 添加 FoundExtensionPackage 接口.
使用 FoundExtensionPackage 接口, ExtensionPackageFinder 可以将加载扩展包的时机推迟到需要加载的时候, 这么做可以让搜索器预先找好扩展包而无需立即加载, 避免了资源浪费. Issue #1
This commit is contained in:
parent
5556613087
commit
2e49a1ec12
@ -12,9 +12,12 @@ import org.jdom2.input.SAXBuilder
|
|||||||
import org.jdom2.xpath.XPathFactory
|
import org.jdom2.xpath.XPathFactory
|
||||||
import org.telegram.abilitybots.api.util.AbilityExtension
|
import org.telegram.abilitybots.api.util.AbilityExtension
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
|
import java.net.URLDecoder
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.jar.JarEntry
|
import java.util.jar.JarEntry
|
||||||
@ -32,48 +35,60 @@ internal class ExtensionLoader(
|
|||||||
MavenMetaInformationFinder
|
MavenMetaInformationFinder
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getExtensions(): Set<ExtensionEntry> {
|
fun getExtensions(): Set<LoadedExtensionEntry> {
|
||||||
val extensionEntries = mutableSetOf<ExtensionEntry>()
|
val extensionEntries = mutableSetOf<LoadedExtensionEntry>()
|
||||||
for (extensionArtifact in bot.extensions) {
|
for (extensionArtifact in bot.extensions) {
|
||||||
val extensionFilesMap = findExtensionPackageFile(extensionArtifact)
|
val extensionFilesMap = findExtensionPackageFile(extensionArtifact)
|
||||||
val extensionFiles = filesMapToSet(extensionFilesMap)
|
val foundedNumber = allFoundedPackageNumber(extensionFilesMap)
|
||||||
if (extensionFiles.size > 1) {
|
if (foundedNumber > 1) {
|
||||||
printExtensionFileConflictError(extensionArtifact, extensionFilesMap)
|
printExtensionFileConflictError(extensionArtifact, extensionFilesMap)
|
||||||
continue
|
continue
|
||||||
} else if (extensionFiles.isEmpty()) {
|
} else if (foundedNumber == 0) {
|
||||||
log.warn { "[Bot ${bot.botUsername}] 找不到符合的扩展包文件: $extensionArtifact" }
|
log.warn { "[Bot ${bot.botUsername}] 找不到符合的扩展包文件: $extensionArtifact" }
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
extensionEntries.addAll(getExtensionFactories(extensionArtifact, extensionFiles.first()))
|
val files = loadFoundExtensionPackage(extensionFilesMap)
|
||||||
|
extensionEntries.addAll(getExtensionFactories(extensionArtifact, files.first()))
|
||||||
}
|
}
|
||||||
return extensionEntries.toSet()
|
return extensionEntries.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExtensionFactories(extensionArtifact: Artifact, extensionFile: File): Set<ExtensionEntry> {
|
private fun loadFoundExtensionPackage(packageMap: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>): Set<File> {
|
||||||
|
val files = mutableSetOf<File>()
|
||||||
|
for (set in packageMap.values) {
|
||||||
|
for (foundedExtensionPackage in set) {
|
||||||
|
files.add(foundedExtensionPackage.loadExtension())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExtensionFactories(extensionArtifact: Artifact, extensionFile: File): Set<LoadedExtensionEntry> {
|
||||||
val extClassLoader =
|
val extClassLoader =
|
||||||
ExtensionClassLoaderCleaner.getOrCreateExtensionClassLoader(extensionArtifact, extensionFile)
|
ExtensionClassLoaderCleaner.getOrCreateExtensionClassLoader(extensionArtifact, extensionFile)
|
||||||
val factories = mutableSetOf<ExtensionEntry>()
|
val factories = mutableSetOf<LoadedExtensionEntry>()
|
||||||
for (factory in extClassLoader.serviceLoader) {
|
for (factory in extClassLoader.serviceLoader) {
|
||||||
val extension =
|
val extension =
|
||||||
factory.createExtensionInstance(bot, getExtensionDataFolder(extensionArtifact))
|
factory.createExtensionInstance(bot, getExtensionDataFolder(extensionArtifact))
|
||||||
factories.add(ExtensionEntry(extensionArtifact, factory::class.java, extension))
|
factories.add(LoadedExtensionEntry(extensionArtifact, factory::class.java, extension))
|
||||||
}
|
}
|
||||||
return factories.toSet()
|
return factories.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun filesMapToSet(filesMap: Map<ExtensionPackageFinder, Set<File>>): MutableSet<File> {
|
private fun allFoundedPackageNumber(filesMap: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>): Int {
|
||||||
val result: MutableSet<File> = mutableSetOf()
|
val result = mutableSetOf<URL>()
|
||||||
for (files in filesMap.values) {
|
for (files in filesMap.values) {
|
||||||
result.addAll(files)
|
for (file in files) {
|
||||||
|
result.add(file.getRawUrl())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result.size
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findExtensionPackageFile(
|
private fun findExtensionPackageFile(
|
||||||
extensionArtifact: Artifact,
|
extensionArtifact: Artifact,
|
||||||
extensionsPath: File = AppPaths.EXTENSIONS.file
|
): Map<ExtensionPackageFinder, Set<FoundExtensionPackage>> {
|
||||||
): Map<ExtensionPackageFinder, Set<File>> {
|
val result = mutableMapOf<ExtensionPackageFinder, Set<FoundExtensionPackage>>()
|
||||||
val result = mutableMapOf<ExtensionPackageFinder, Set<File>>()
|
|
||||||
for (finder in finders) {
|
for (finder in finders) {
|
||||||
val artifacts = finder.findByArtifact(extensionArtifact, extensionsPath)
|
val artifacts = finder.findByArtifact(extensionArtifact, extensionsPath)
|
||||||
if (artifacts.isNotEmpty()) {
|
if (artifacts.isNotEmpty()) {
|
||||||
@ -85,17 +100,19 @@ internal class ExtensionLoader(
|
|||||||
|
|
||||||
private fun printExtensionFileConflictError(
|
private fun printExtensionFileConflictError(
|
||||||
extensionArtifact: Artifact,
|
extensionArtifact: Artifact,
|
||||||
foundResult: Map<ExtensionPackageFinder, Set<File>>
|
foundResult: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>
|
||||||
) {
|
) {
|
||||||
val errMessage = StringBuilder(
|
val errMessage = StringBuilder(
|
||||||
"""
|
"""
|
||||||
[Bot ${bot.botUsername}] 扩展包 $extensionArtifact 存在多个文件, 为防止安全问题, 已禁止加载该扩展包:
|
[Bot ${bot.botUsername}] 扩展包 $extensionArtifact 存在多个文件, 为防止安全问题, 已禁止加载该扩展包:
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
).append('\n')
|
).append('\n')
|
||||||
|
|
||||||
foundResult.forEach { (finder, files) ->
|
foundResult.forEach { (finder, files) ->
|
||||||
errMessage.append("\t- 搜索器 `").append(finder::class.simpleName).append("` 找到了以下扩展包: \n")
|
errMessage.append("\t- 搜索器 `").append(finder::class.simpleName).append("` 找到了以下扩展包: \n")
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
errMessage.append("\t\t* ").append(file.canonicalPath).append('\n')
|
errMessage.append("\t\t* ")
|
||||||
|
.append(URLDecoder.decode(file.getRawUrl().toString(), StandardCharsets.UTF_8)).append('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.error { errMessage }
|
log.error { errMessage }
|
||||||
@ -110,8 +127,7 @@ internal class ExtensionLoader(
|
|||||||
return dataFolder
|
return dataFolder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class LoadedExtensionEntry(
|
||||||
data class ExtensionEntry(
|
|
||||||
val extensionArtifact: Artifact,
|
val extensionArtifact: Artifact,
|
||||||
val factoryClass: Class<out BotExtensionFactory>,
|
val factoryClass: Class<out BotExtensionFactory>,
|
||||||
val extension: AbilityExtension
|
val extension: AbilityExtension
|
||||||
@ -180,22 +196,78 @@ internal interface ExtensionPackageFinder {
|
|||||||
* @param extensionsPath 建议的搜索路径, 如搜索器希望通过网络来获取也可以.
|
* @param extensionsPath 建议的搜索路径, 如搜索器希望通过网络来获取也可以.
|
||||||
* @return 返回按搜索器的方式可以找到的所有与构件坐标有关的扩展包路径.
|
* @return 返回按搜索器的方式可以找到的所有与构件坐标有关的扩展包路径.
|
||||||
*/
|
*/
|
||||||
fun findByArtifact(extensionArtifact: Artifact, extensionsPath: File): Set<File>
|
fun findByArtifact(extensionArtifact: Artifact, extensionsPath: File): Set<FoundExtensionPackage>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已找到的扩展包信息.
|
||||||
|
* 通过实现该接口, 以寻找远端文件的 [ExtensionPackageFinder]
|
||||||
|
* 可以在适当的时候将扩展包下载到本地, 而无需在搜索阶段下载扩展包.
|
||||||
|
*/
|
||||||
|
internal interface FoundExtensionPackage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取扩展包的构件坐标.
|
||||||
|
* @return 返回扩展包的构件坐标.
|
||||||
|
*/
|
||||||
|
fun getExtensionArtifact(): Artifact
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取原始的扩展 Url.
|
||||||
|
* @return 返回扩展包所在的 Url.
|
||||||
|
*/
|
||||||
|
fun getRawUrl(): URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取扩展包并返回扩展包在本地的 File 对象.
|
||||||
|
*
|
||||||
|
* 当调用本方法时, Finder 可以将扩展包下载到本地(如果扩展包在远端服务器的话).
|
||||||
|
* @return 返回扩展包在本地存储时指向扩展包文件的 File 对象.
|
||||||
|
*/
|
||||||
|
fun loadExtension(): File
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已找到的扩展包文件.
|
||||||
|
* @param artifact 扩展包构件坐标.
|
||||||
|
* @param file 已找到的扩展包文件.
|
||||||
|
*/
|
||||||
|
internal class FileFoundExtensionPackage(private val artifact: Artifact, private val file: File) :
|
||||||
|
FoundExtensionPackage {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw FileNotFoundException(file.canonicalPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getExtensionArtifact(): Artifact = artifact
|
||||||
|
|
||||||
|
override fun getRawUrl(): URL = file.canonicalFile.toURI().toURL()
|
||||||
|
|
||||||
|
override fun loadExtension(): File = file
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于文件名的搜索器.
|
* 基于文件名的搜索器.
|
||||||
*
|
*
|
||||||
* 将搜索文件名(不带扩展包名)结尾为 `${groupId}-${artifactId}-${version}` 的文件.
|
* 将搜索文件名(不带扩展包名)结尾为 `${groupId}_${artifactId}_${version}` 的文件.
|
||||||
|
* 比如说 `(Example Extension) org.example_scalabot-example_v1.0.0-SNAPSHOT.jar` 是可以的
|
||||||
*/
|
*/
|
||||||
internal object FileNameFinder : ExtensionPackageFinder {
|
internal object FileNameFinder : ExtensionPackageFinder {
|
||||||
|
|
||||||
override fun findByArtifact(extensionArtifact: Artifact, extensionsPath: File): Set<File> {
|
override fun findByArtifact(extensionArtifact: Artifact, extensionsPath: File): Set<FoundExtensionPackage> {
|
||||||
val focusName = getExtensionFilename(extensionArtifact)
|
val focusName = getExtensionFilename(extensionArtifact)
|
||||||
val files = extensionsPath.listFiles { file ->
|
val files = extensionsPath.listFiles { file ->
|
||||||
file.nameWithoutExtension.endsWith(focusName)
|
file.nameWithoutExtension.endsWith(focusName)
|
||||||
|
} ?: return emptySet()
|
||||||
|
|
||||||
|
val extensionPackage = mutableSetOf<FoundExtensionPackage>()
|
||||||
|
for (file in files) {
|
||||||
|
extensionPackage.add(FileFoundExtensionPackage(extensionArtifact, file))
|
||||||
}
|
}
|
||||||
return files?.toSet() ?: emptySet()
|
return if (extensionPackage.isEmpty()) emptySet() else extensionPackage
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExtensionFilename(extensionArtifact: Artifact) =
|
private fun getExtensionFilename(extensionArtifact: Artifact) =
|
||||||
@ -203,14 +275,17 @@ internal object FileNameFinder : ExtensionPackageFinder {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过检查 Maven 在发布构件时打包进去的元信息(包括 POM 文件)来获取构件坐标.
|
||||||
|
*/
|
||||||
internal object MavenMetaInformationFinder : ExtensionPackageFinder {
|
internal object MavenMetaInformationFinder : ExtensionPackageFinder {
|
||||||
|
|
||||||
private const val MAVEN_META_XML = "pom.xml"
|
private const val MAVEN_META_XML = "pom.xml"
|
||||||
private const val MAVEN_META_PROPERTIES = "pom.properties"
|
private const val MAVEN_META_PROPERTIES = "pom.properties"
|
||||||
|
|
||||||
override fun findByArtifact(extensionArtifact: Artifact, extensionsPath: File): Set<File> {
|
override fun findByArtifact(extensionArtifact: Artifact, extensionsPath: File): Set<FoundExtensionPackage> {
|
||||||
val files = extensionsPath.listFiles() ?: return emptySet()
|
val files = extensionsPath.listFiles() ?: return emptySet()
|
||||||
val result = mutableSetOf<File>()
|
val result = mutableSetOf<FoundExtensionPackage>()
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
if (file.isFile) {
|
if (file.isFile) {
|
||||||
val foundArtifact = when (file.extension) {
|
val foundArtifact = when (file.extension) {
|
||||||
@ -222,12 +297,12 @@ internal object MavenMetaInformationFinder : ExtensionPackageFinder {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
if (foundArtifact != null && extensionArtifact.equalsArtifact(foundArtifact)) {
|
if (foundArtifact != null && extensionArtifact.equalsArtifact(foundArtifact)) {
|
||||||
result.add(file)
|
result.add(FileFoundExtensionPackage(extensionArtifact, file))
|
||||||
}
|
}
|
||||||
} else if (file.isDirectory) {
|
} else if (file.isDirectory) {
|
||||||
val foundArtifact = getArtifactCoordinateFromArtifactDirectory(file)
|
val foundArtifact = getArtifactCoordinateFromArtifactDirectory(file)
|
||||||
if (foundArtifact != null && extensionArtifact.equalsArtifact(foundArtifact)) {
|
if (foundArtifact != null && extensionArtifact.equalsArtifact(foundArtifact)) {
|
||||||
result.add(file)
|
result.add(FileFoundExtensionPackage(extensionArtifact, file))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user