mirror of
https://github.com/LamGC/ScalaBot.git
synced 2025-04-29 22:27:31 +00:00
feat(extension): 支持 ExtensionPackageFinder 设置优先级.
添加 FinderRules 注解, 发现器标记注解后, 将根据优先级选择加载扩展包; 该改动有助于支持远端扩展包加载的开发, 例如可以直接从 maven 中央仓库获取支持加载的构件, 支持该功能将大大增加便捷性. Closed #1
This commit is contained in:
parent
2e49a1ec12
commit
16135e3cde
@ -4,6 +4,7 @@ import mu.KotlinLogging
|
|||||||
import net.lamgc.scalabot.extension.BotExtensionFactory
|
import net.lamgc.scalabot.extension.BotExtensionFactory
|
||||||
import net.lamgc.scalabot.util.deepListFiles
|
import net.lamgc.scalabot.util.deepListFiles
|
||||||
import net.lamgc.scalabot.util.equalsArtifact
|
import net.lamgc.scalabot.util.equalsArtifact
|
||||||
|
import net.lamgc.scalabot.util.getPriority
|
||||||
import org.eclipse.aether.artifact.Artifact
|
import org.eclipse.aether.artifact.Artifact
|
||||||
import org.eclipse.aether.artifact.DefaultArtifact
|
import org.eclipse.aether.artifact.DefaultArtifact
|
||||||
import org.jdom2.Document
|
import org.jdom2.Document
|
||||||
@ -40,20 +41,49 @@ internal class ExtensionLoader(
|
|||||||
for (extensionArtifact in bot.extensions) {
|
for (extensionArtifact in bot.extensions) {
|
||||||
val extensionFilesMap = findExtensionPackageFile(extensionArtifact)
|
val extensionFilesMap = findExtensionPackageFile(extensionArtifact)
|
||||||
val foundedNumber = allFoundedPackageNumber(extensionFilesMap)
|
val foundedNumber = allFoundedPackageNumber(extensionFilesMap)
|
||||||
if (foundedNumber > 1) {
|
if (checkConflict(extensionFilesMap)) {
|
||||||
printExtensionFileConflictError(extensionArtifact, extensionFilesMap)
|
printExtensionFileConflictError(extensionArtifact, extensionFilesMap)
|
||||||
continue
|
continue
|
||||||
} else if (foundedNumber == 0) {
|
} else if (foundedNumber == 0) {
|
||||||
log.warn { "[Bot ${bot.botUsername}] 找不到符合的扩展包文件: $extensionArtifact" }
|
log.warn { "[Bot ${bot.botUsername}] 找不到符合的扩展包文件: $extensionArtifact" }
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val files = loadFoundExtensionPackage(extensionFilesMap)
|
|
||||||
|
val files = getExtensionFiles(filterHighPriorityResult(extensionFilesMap))
|
||||||
extensionEntries.addAll(getExtensionFactories(extensionArtifact, files.first()))
|
extensionEntries.addAll(getExtensionFactories(extensionArtifact, files.first()))
|
||||||
}
|
}
|
||||||
return extensionEntries.toSet()
|
return extensionEntries.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFoundExtensionPackage(packageMap: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>): Set<File> {
|
/**
|
||||||
|
* 检查是否发生冲突.
|
||||||
|
* @return 如果出现冲突, 返回 `true`.
|
||||||
|
*/
|
||||||
|
private fun checkConflict(foundResult: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>): Boolean {
|
||||||
|
if (foundResult.isEmpty()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val highPriorityFinders = filterHighPriorityResult(foundResult).keys
|
||||||
|
if (highPriorityFinders.size > 1) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
val finder = highPriorityFinders.firstOrNull() ?: return false
|
||||||
|
return foundResult[finder]!!.size > 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterHighPriorityResult(foundResult: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>)
|
||||||
|
: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>> {
|
||||||
|
val finders: List<ExtensionPackageFinder> = foundResult.keys
|
||||||
|
.filter { checkExtensionPackageFinder(it) && (foundResult[it]?.size ?: 0) != 0 }
|
||||||
|
.sortedBy { it.getPriority() }
|
||||||
|
|
||||||
|
val highPriority = finders.first().getPriority()
|
||||||
|
return foundResult.filterKeys { it.getPriority() == highPriority }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExtensionFiles(packageMap: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>): Set<File> {
|
||||||
val files = mutableSetOf<File>()
|
val files = mutableSetOf<File>()
|
||||||
for (set in packageMap.values) {
|
for (set in packageMap.values) {
|
||||||
for (foundedExtensionPackage in set) {
|
for (foundedExtensionPackage in set) {
|
||||||
@ -90,6 +120,9 @@ internal class ExtensionLoader(
|
|||||||
): Map<ExtensionPackageFinder, Set<FoundExtensionPackage>> {
|
): Map<ExtensionPackageFinder, Set<FoundExtensionPackage>> {
|
||||||
val result = mutableMapOf<ExtensionPackageFinder, Set<FoundExtensionPackage>>()
|
val result = mutableMapOf<ExtensionPackageFinder, Set<FoundExtensionPackage>>()
|
||||||
for (finder in finders) {
|
for (finder in finders) {
|
||||||
|
if (!checkExtensionPackageFinder(finder)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
val artifacts = finder.findByArtifact(extensionArtifact, extensionsPath)
|
val artifacts = finder.findByArtifact(extensionArtifact, extensionsPath)
|
||||||
if (artifacts.isNotEmpty()) {
|
if (artifacts.isNotEmpty()) {
|
||||||
result[finder] = artifacts
|
result[finder] = artifacts
|
||||||
@ -98,6 +131,9 @@ internal class ExtensionLoader(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkExtensionPackageFinder(finder: ExtensionPackageFinder): Boolean =
|
||||||
|
finder::class.java.getDeclaredAnnotation(FinderRules::class.java) != null
|
||||||
|
|
||||||
private fun printExtensionFileConflictError(
|
private fun printExtensionFileConflictError(
|
||||||
extensionArtifact: Artifact,
|
extensionArtifact: Artifact,
|
||||||
foundResult: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>
|
foundResult: Map<ExtensionPackageFinder, Set<FoundExtensionPackage>>
|
||||||
@ -109,7 +145,9 @@ internal class ExtensionLoader(
|
|||||||
).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("`")
|
||||||
|
.append("(Priority: ${finder.getPriority()})")
|
||||||
|
.append(" 找到了以下扩展包: \n")
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
errMessage.append("\t\t* ")
|
errMessage.append("\t\t* ")
|
||||||
.append(URLDecoder.decode(file.getRawUrl().toString(), StandardCharsets.UTF_8)).append('\n')
|
.append(URLDecoder.decode(file.getRawUrl().toString(), StandardCharsets.UTF_8)).append('\n')
|
||||||
@ -226,6 +264,11 @@ internal interface FoundExtensionPackage {
|
|||||||
*/
|
*/
|
||||||
fun loadExtension(): File
|
fun loadExtension(): File
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun getExtensionPackageFinder(): ExtensionPackageFinder
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -233,8 +276,11 @@ internal interface FoundExtensionPackage {
|
|||||||
* @param artifact 扩展包构件坐标.
|
* @param artifact 扩展包构件坐标.
|
||||||
* @param file 已找到的扩展包文件.
|
* @param file 已找到的扩展包文件.
|
||||||
*/
|
*/
|
||||||
internal class FileFoundExtensionPackage(private val artifact: Artifact, private val file: File) :
|
internal class FileFoundExtensionPackage(
|
||||||
FoundExtensionPackage {
|
private val artifact: Artifact,
|
||||||
|
private val file: File,
|
||||||
|
private val finder: ExtensionPackageFinder
|
||||||
|
) : FoundExtensionPackage {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
@ -247,6 +293,8 @@ internal class FileFoundExtensionPackage(private val artifact: Artifact, private
|
|||||||
override fun getRawUrl(): URL = file.canonicalFile.toURI().toURL()
|
override fun getRawUrl(): URL = file.canonicalFile.toURI().toURL()
|
||||||
|
|
||||||
override fun loadExtension(): File = file
|
override fun loadExtension(): File = file
|
||||||
|
|
||||||
|
override fun getExtensionPackageFinder(): ExtensionPackageFinder = finder
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -255,6 +303,7 @@ internal class FileFoundExtensionPackage(private val artifact: Artifact, private
|
|||||||
* 将搜索文件名(不带扩展包名)结尾为 `${groupId}_${artifactId}_${version}` 的文件.
|
* 将搜索文件名(不带扩展包名)结尾为 `${groupId}_${artifactId}_${version}` 的文件.
|
||||||
* 比如说 `(Example Extension) org.example_scalabot-example_v1.0.0-SNAPSHOT.jar` 是可以的
|
* 比如说 `(Example Extension) org.example_scalabot-example_v1.0.0-SNAPSHOT.jar` 是可以的
|
||||||
*/
|
*/
|
||||||
|
@FinderRules(priority = FinderPriority.LOCAL)
|
||||||
internal object FileNameFinder : ExtensionPackageFinder {
|
internal object FileNameFinder : ExtensionPackageFinder {
|
||||||
|
|
||||||
override fun findByArtifact(extensionArtifact: Artifact, extensionsPath: File): Set<FoundExtensionPackage> {
|
override fun findByArtifact(extensionArtifact: Artifact, extensionsPath: File): Set<FoundExtensionPackage> {
|
||||||
@ -265,7 +314,7 @@ internal object FileNameFinder : ExtensionPackageFinder {
|
|||||||
|
|
||||||
val extensionPackage = mutableSetOf<FoundExtensionPackage>()
|
val extensionPackage = mutableSetOf<FoundExtensionPackage>()
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
extensionPackage.add(FileFoundExtensionPackage(extensionArtifact, file))
|
extensionPackage.add(FileFoundExtensionPackage(extensionArtifact, file, this))
|
||||||
}
|
}
|
||||||
return if (extensionPackage.isEmpty()) emptySet() else extensionPackage
|
return if (extensionPackage.isEmpty()) emptySet() else extensionPackage
|
||||||
}
|
}
|
||||||
@ -278,6 +327,7 @@ internal object FileNameFinder : ExtensionPackageFinder {
|
|||||||
/**
|
/**
|
||||||
* 通过检查 Maven 在发布构件时打包进去的元信息(包括 POM 文件)来获取构件坐标.
|
* 通过检查 Maven 在发布构件时打包进去的元信息(包括 POM 文件)来获取构件坐标.
|
||||||
*/
|
*/
|
||||||
|
@FinderRules(priority = FinderPriority.LOCAL)
|
||||||
internal object MavenMetaInformationFinder : ExtensionPackageFinder {
|
internal object MavenMetaInformationFinder : ExtensionPackageFinder {
|
||||||
|
|
||||||
private const val MAVEN_META_XML = "pom.xml"
|
private const val MAVEN_META_XML = "pom.xml"
|
||||||
@ -297,12 +347,12 @@ internal object MavenMetaInformationFinder : ExtensionPackageFinder {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
if (foundArtifact != null && extensionArtifact.equalsArtifact(foundArtifact)) {
|
if (foundArtifact != null && extensionArtifact.equalsArtifact(foundArtifact)) {
|
||||||
result.add(FileFoundExtensionPackage(extensionArtifact, file))
|
result.add(FileFoundExtensionPackage(extensionArtifact, file, this))
|
||||||
}
|
}
|
||||||
} 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(FileFoundExtensionPackage(extensionArtifact, file))
|
result.add(FileFoundExtensionPackage(extensionArtifact, file, this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,3 +505,35 @@ internal class ExtensionClassLoader(vararg urls: URL) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索器规则.
|
||||||
|
* @property priority 搜索器优先级. 优先级从 0 (最高)开始, 相同构件坐标下将使用优先级最高的搜索器所找到的文件.
|
||||||
|
*/
|
||||||
|
annotation class FinderRules(
|
||||||
|
val priority: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **建议**的搜索器优先级常量.
|
||||||
|
*/
|
||||||
|
class FinderPriority private constructor() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* 本地扩展包.
|
||||||
|
*/
|
||||||
|
const val LOCAL = 100
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 远端扩展包.
|
||||||
|
*/
|
||||||
|
const val REMOTE = 200
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替补的扩展包.
|
||||||
|
*/
|
||||||
|
const val ALTERNATE = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.lamgc.scalabot.util
|
package net.lamgc.scalabot.util
|
||||||
|
|
||||||
|
import net.lamgc.scalabot.ExtensionPackageFinder
|
||||||
|
import net.lamgc.scalabot.FinderRules
|
||||||
import org.eclipse.aether.artifact.Artifact
|
import org.eclipse.aether.artifact.Artifact
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileFilter
|
import java.io.FileFilter
|
||||||
@ -53,3 +55,11 @@ internal fun File.deepListFiles(
|
|||||||
return result.toTypedArray()
|
return result.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Finder 的 [FinderRules] 注解中获取优先级.
|
||||||
|
* @return 获取 Finder 的优先级.
|
||||||
|
* @throws NoSuchFieldException 如果 Finder 没有添加 [FinderRules] 注解时抛出该异常.
|
||||||
|
*/
|
||||||
|
internal fun ExtensionPackageFinder.getPriority() =
|
||||||
|
this::class.java.getDeclaredAnnotation(FinderRules::class.java)?.priority
|
||||||
|
?: throw NoSuchFieldException("Finder did not add `FinderRules` annotation")
|
||||||
|
Loading…
Reference in New Issue
Block a user