Skip to content

Commit

Permalink
大量修改,更改插件加载方式,阿里云OSS插件
Browse files Browse the repository at this point in the history
- 修改构建脚本
- 更改插件加载方式,从 JarClassLoader 到 ClassLoaderUtils,现在直接读到和主线程一样的 ClassLoader,不会在造成无法访问到类的情况
- 添加资源上传方法
- 添加阿里云OSS资源插件(支持资源上传)
- 添加客户端请求工具(未完成)
- 添加以前没上传的文档资源
  • Loading branch information
Taskeren committed Jan 12, 2023
1 parent 9f4063c commit 9e7a299
Show file tree
Hide file tree
Showing 14 changed files with 456 additions and 16 deletions.
7 changes: 7 additions & 0 deletions booster-plugins/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Booster Plugins

Plugin projects running on Booster loader.

## Plugins

- [Aliyun OSS Resource Provider](aliyun-oss-resource)
17 changes: 17 additions & 0 deletions booster-plugins/aliyun-oss-resource/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
plugins {
kotlin("jvm")
id("com.github.johnrengelman.shadow") version "7.1.2"
}

group = "explode"
version = "1.0.0"

repositories {
mavenCentral()
}

dependencies {
compileOnly(project(":booster-resource"))

implementation("com.aliyun.oss:aliyun-sdk-oss:3.16.0")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package explode2.booster.resource.oss.aliyun

import com.aliyun.oss.OSS
import com.aliyun.oss.OSSClientBuilder
import com.github.taskeren.config.Configuration
import explode2.booster.resource.RedirectResourceProvider
import explode2.booster.resource.ResourceReceiver
import explode2.labyrinth.LabyrinthPlugin.Companion.labyrinth
import explode2.logging.Colors
import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream
import java.io.File
import java.util.*

private const val MUSIC_TYPE = "music"
private const val PREVIEW_MUSIC_TYPE = "preview-music"
private const val COVER_PICTURE_TYPE = "cover-picture"
private const val STORE_COVER_PICTURE_TYPE = "store-cover"
private const val CHART_MAP_TYPE = "chart-map"
private const val USER_AVATAR_TYPE = "user-avatar"

class AliyunOSSResourceProvider : RedirectResourceProvider, ResourceReceiver {

companion object {

private val logger = LoggerFactory.getLogger(AliyunOSSResourceProvider::class.java)

private val config = Configuration(File("aliyun-oss.resource.cfg"))

private val endpoint: String =
config.getString("endpoint", "oss", "http://oss-cn-hangzhou.aliyuncs.com", "地域节点地址")

private val accessKeyId: String =
config.getString("access-key-id", "user-access", "", "用户 AccessKeyId")
private val accessKeySecret: String =
config.getString("access-key-secret", "user-access", "", "用户 AccessKeySecret")

private val bucketName: String =
config.getString("bucket-name", "bucket", "explode-oss-resource", "Bucket 名称")
private val resourceKeyPattern: String =
config.getString(
"resource-key-pattern",
"bucket",
"{type}-{id}",
"资源 Key 模板({type} 为资源类型,{id} 为资源ID)"
)

init {
config.save()
}

private val oss: OSS = OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret)

init {
if(oss.doesBucketExist(bucketName)) {
logger.info("${Colors.Light.Green}Aliyun OSS bucket $bucketName is available")
} else {
logger.error("${Colors.Light.Red}Aliyun OSS bucket $bucketName was not ready!")
throw IllegalStateException("Aliyun OSS bucket $bucketName was not ready!")
}
}
}

private fun getResourceKey(type: String, id: String): String {
return resourceKeyPattern.replace("{type}", type).replace("{id}", id)
}

private fun getResourceUrl(type: String, id: String): String {
val resourceKey = getResourceKey(type, id)

if(oss.doesObjectExist(bucketName, resourceKey)) {
// TODO: 调整过期时间
val expiration = Date(Date().time + 3600 * 1000)
return oss.generatePresignedUrl(bucketName, resourceKey, expiration).toString()
} else {
throw IllegalStateException("requested resource $resourceKey does not exist in bucket $bucketName")
}
}

/**
* 检查 Token 不为空,切用户存在
*/
private fun validateToken(token: String?) {
labyrinth.gameUserFactory.getGameUserById(token ?: error("soudayo is undefined")) ?: error("invalid token")
}

private fun validateTokenUpload(token: String?) {
labyrinth.gameUserFactory.getGameUserById(token ?: error("soudayo is undefined")) ?: error("invalid token")
}

override fun getMusic(id: String, token: String?): String {
validateToken(token)
return getResourceUrl(MUSIC_TYPE, id)
}

override fun getPreviewMusic(id: String, token: String?): String {
validateToken(token)
return getResourceUrl(PREVIEW_MUSIC_TYPE, id)
}

override fun getCoverPicture(id: String, token: String?): String {
validateToken(token)
return getResourceUrl(COVER_PICTURE_TYPE, id)
}

override fun getStoreCoverPicture(id: String, token: String?): String {
validateToken(token)
return getResourceUrl(STORE_COVER_PICTURE_TYPE, id)
}

override fun getChartXML(cid: String, sid: String, token: String?): String {
validateToken(token)
return getResourceUrl(CHART_MAP_TYPE, cid)
}

override fun getUserAvatar(id: String, token: String?): String {
validateToken(token)
return getResourceUrl(USER_AVATAR_TYPE, id)
}

override fun uploadMusic(id: String, token: String?, content: ByteArray) {
validateTokenUpload(token)
oss.putObject(bucketName, getResourceKey(MUSIC_TYPE, id), ByteArrayInputStream(content))
}

override fun uploadPreviewMusic(id: String, token: String?, content: ByteArray) {
validateTokenUpload(token)
oss.putObject(bucketName, getResourceKey(PREVIEW_MUSIC_TYPE, id), ByteArrayInputStream(content))
}

override fun uploadCoverPicture(id: String, token: String?, content: ByteArray) {
validateTokenUpload(token)
oss.putObject(bucketName, getResourceKey(COVER_PICTURE_TYPE, id), ByteArrayInputStream(content))
}

override fun uploadStoreCoverPicture(id: String, token: String?, content: ByteArray) {
validateTokenUpload(token)
oss.putObject(bucketName, getResourceKey(STORE_COVER_PICTURE_TYPE, id), ByteArrayInputStream(content))
}

override fun uploadChartXML(cid: String, sid: String, token: String?, content: ByteArray) {
validateTokenUpload(token)
oss.putObject(bucketName, getResourceKey(CHART_MAP_TYPE, cid), ByteArrayInputStream(content))
}

override fun uploadUserAvatar(id: String, token: String?, content: ByteArray) {
validateTokenUpload(token)
oss.putObject(bucketName, getResourceKey(USER_AVATAR_TYPE, id), ByteArrayInputStream(content))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
explode2.booster.resource.oss.aliyun.AliyunOSSResourceProvider
3 changes: 2 additions & 1 deletion booster/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ dependencies {
api("io.ktor:ktor-server-cors:2.1.2")

// providing classloading
api("org.xeustechnologies:jcl-core:2.8")
api("io.github.lxgaming:classloaderutils:1.0.1")


testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0")
Expand Down
46 changes: 31 additions & 15 deletions booster/src/main/kotlin/explode2/booster/BoosterMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import explode2.booster.Booster.dispatchEvent
import explode2.booster.event.KtorInitEvent
import explode2.booster.event.KtorModuleEvent
import explode2.booster.util.forEachExceptional
import io.github.lxgaming.classloader.ClassLoaderUtils
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import org.slf4j.LoggerFactory
import org.slf4j.MarkerFactory
import org.xeustechnologies.jcl.JarClassLoader
import java.io.File
import java.net.BindException
import java.net.URI
import java.util.*
import kotlin.concurrent.thread
import kotlin.system.exitProcess
Expand All @@ -27,6 +28,7 @@ fun main() {
}

// 加载插件
logger.info("Constructing plugins")
ExplodeService.loadBooster()

// 检查插件数量,为零直接退出
Expand Down Expand Up @@ -75,11 +77,13 @@ fun main() {
}

logger.info("Boosted!")
// 主线程退出
}

object ExplodeService {

private val servMarker = MarkerFactory.getMarker("ExplodeService")
// 加载插件阶段使用的标记
private val preludeMarker = MarkerFactory.getMarker("Prelude")

private val services: MutableMap<String, BoosterPlugin> = mutableMapOf()
private val classToService: MutableMap<Class<out BoosterPlugin>, BoosterPlugin> = mutableMapOf()
Expand All @@ -89,60 +93,72 @@ object ExplodeService {

private val PluginFolder = File("./plugins/").also { it.mkdirs() }

val ExternalClassLoader = loadExternalJars()

fun loadBooster() {
loadPluginJars()
loadPlugins(load())
}

inline fun <reified T> load(): ServiceLoader<T> {
return ServiceLoader.load(T::class.java, ExternalClassLoader)
return ServiceLoader.load(T::class.java)
}

private fun loadPlugins(serviceLoader: ServiceLoader<BoosterPlugin>) {
serviceLoader.stream().forEach { provider ->
runCatching {
loadSinglePlugin(provider)
}.onFailure {
logger.error(servMarker, "An exception occurred when loading plugin: ${provider.type().canonicalName}", it)
logger.error(preludeMarker, "An exception occurred when loading plugin: ${provider.type().canonicalName}", it)
}
}
}

private fun loadSinglePlugin(provider: ServiceLoader.Provider<BoosterPlugin>) {
val plugin = provider.get()
if(services.putIfAbsent(plugin.id, plugin) == null) {
logger.info("Scanned plugin: ${plugin.id}(${plugin.version}) <${plugin.javaClass.simpleName}>")
logger.info(preludeMarker, "Scanned plugin: ${plugin.id}(${plugin.version}) <${plugin.javaClass.simpleName}>")
classToService[plugin.javaClass] = plugin
} else {
logger.warn(servMarker, "Failed to instantiate plugin ${plugin.id} because of id collision")
logger.warn(preludeMarker, "Failed to instantiate plugin ${plugin.id} because of id collision")
}
}

fun validateStatus() {
if(services.isEmpty()) {
logger.error(servMarker, "No plugin is found!")
logger.error(servMarker, "You need to add at least one plugin to continue")
logger.error(preludeMarker, "No plugin is found!")
logger.error(preludeMarker, "You need to add at least one plugin to continue")
exitProcess(0)
}
}

fun dispatchPreInit() = services.values.forEachExceptional(BoosterPlugin::onPreInit) { plugin, exception ->
logger.error(servMarker, "Exception occurred when pre-initializing plugin ${plugin.id}", exception)
logger.error(preludeMarker, "Exception occurred when pre-initializing plugin ${plugin.id}", exception)
}

fun dispatchInit() = services.values.forEachExceptional(BoosterPlugin::onInit) { plugin, exception ->
logger.error(servMarker, "Exception occurred when initializing plugin ${plugin.id}", exception)
logger.error(preludeMarker, "Exception occurred when initializing plugin ${plugin.id}", exception)
}

fun dispatchPostInit() = services.values.forEachExceptional(BoosterPlugin::onPostInit) { plugin, exception ->
logger.error(servMarker, "Exception occurred when post-initializing plugin ${plugin.id}", exception)
logger.error(preludeMarker, "Exception occurred when post-initializing plugin ${plugin.id}", exception)
}

/// LOAD EXTERNAL JARS

private fun loadExternalJars(): JarClassLoader {
return JarClassLoader().apply { add(PluginFolder.path) }
private fun loadPluginJars() {
logger.info(preludeMarker, "Working directory: ${System.getProperty("user.dir")}")
val urls = PluginFolder.listFiles { _, name -> name.endsWith(".jar") }?.onEach {
logger.info(preludeMarker, "Scanned external jar: ${it.name}")
}?.map(File::toURI)?.map(URI::toURL)
if(urls == null) {
logger.warn(preludeMarker, "Plugins folder is not existent or something else happened")
} else {
urls.forEachExceptional({ url ->
ClassLoaderUtils.appendToClassPath(url)
}) { url, ex ->
logger.error(preludeMarker, "Exception occurred when loading external jar: $url", ex)
// no-op
}
}
}

}
8 changes: 8 additions & 0 deletions booster/src/main/kotlin/explode2/logging/Colors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ object Colors {
val Miku = 0x66FFCC.toColor()
val TianYi = 0x66CCFF.toColor()
val TaskPurple = 0xFF66CC.toColor()

object Light {
val Red = 0xF2705E.toColor()
val Purple = 0xB55EF7.toColor()
val Blue = 0x60B0E0.toColor()
val Green = 0x5EF777.toColor()
val Yellow = 0xEDDF5A.toColor()
}
}

private val ColorChar = Char(167)
Expand Down
21 changes: 21 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

plugins {
java
kotlin("jvm") version "1.7.20"
application
id("com.github.johnrengelman.shadow") version "7.1.2" apply false
}

group = "explode"
version = "1.0-SNAPSHOT"

allprojects {
if(!this.name.startsWith("explode")) {
tasks.withType<ShadowJar> {
dependencies {
exclude(project(":booster"))
exclude(project(":booster-graphql"))
exclude(project(":booster-resource"))
exclude(project(":labyrinth"))
exclude(project(":labyrinth-mongodb"))
exclude(project(":booster-maintain"))
exclude(project(":gatekeeper"))

exclude(dependency("org.jetbrains.kotlin:.*"))
}
}
}
}

repositories {
mavenCentral()
}
Expand Down
22 changes: 22 additions & 0 deletions devtools/dynamite-cli/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
plugins {
kotlin("jvm") version "1.7.20"
}

group = "explode"
version = "1.0-SNAPSHOT"

repositories {
mavenCentral()
}

dependencies {
val ktorVer = "2.2.1"
fun ktor(moduleName: String) = implementation("io.ktor:${moduleName}:${ktorVer}")
ktor("ktor-client-core")
ktor("ktor-client-okhttp")
ktor("ktor-client-content-negotiation")
ktor("ktor-serialization-gson")
implementation("com.google.code.gson:gson:2.10")

testImplementation(kotlin("test"))
}
Loading

0 comments on commit 9e7a299

Please sign in to comment.