Skip to content

Commit

Permalink
feat: add plugin system and improve infrastructure for it
Browse files Browse the repository at this point in the history
  • Loading branch information
duruer committed Mar 3, 2024
1 parent 8b26b0b commit 520bbe9
Show file tree
Hide file tree
Showing 335 changed files with 597 additions and 8,600 deletions.
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,11 @@ config.conf

.docker

file-uploads
file-uploads

# Ignore everything under plugins/
plugins/*

# Except the following files
!plugins/build.gradle.kts
!plugins/*.jar
151 changes: 151 additions & 0 deletions Pano/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
val vertxVersion: String by project
val gsonVersion: String by project
val handlebarsVersion: String by project
val log4jVersion = "2.21.1"
val appMainClass = "com.panomc.platform.Main"
val pf4jVersion: String by project
val pluginsDir: File? by rootProject.extra

plugins {
kotlin("jvm") version "1.9.20"
kotlin("kapt") version "1.9.20"
id("com.github.johnrengelman.shadow") version "8.1.1"
application
`maven-publish`
}

group = "com.panomc"
version =
(if (project.hasProperty("version") && project.findProperty("version") != "unspecified") project.findProperty("version") else "local-build")!!

val buildType = project.findProperty("buildType") as String? ?: "alpha"
val timeStamp: String by project
val buildDir by extra { file("${rootProject.layout.buildDirectory.get()}/libs") }

repositories {
mavenCentral()
maven("https://oss.sonatype.org/content/repositories/iovertx-3720/")
maven("https://jitpack.io")
}

dependencies {
implementation(kotlin("stdlib-jdk8"))
implementation(kotlin("reflect"))

testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.0")
testImplementation("io.vertx:vertx-unit:$vertxVersion")

implementation("io.vertx:vertx-web:$vertxVersion")
implementation("io.vertx:vertx-web-client:$vertxVersion")
implementation("io.vertx:vertx-mysql-client:$vertxVersion")
implementation("io.vertx:vertx-mail-client:$vertxVersion")
implementation("io.vertx:vertx-lang-kotlin:$vertxVersion")
implementation("io.vertx:vertx-web-templ-handlebars:$vertxVersion")
implementation("io.vertx:vertx-config:$vertxVersion")
implementation("io.vertx:vertx-config-hocon:$vertxVersion")
implementation("io.vertx:vertx-lang-kotlin-coroutines:$vertxVersion")
implementation("io.vertx:vertx-web-validation:$vertxVersion")
implementation("io.vertx:vertx-json-schema:$vertxVersion")
implementation("io.vertx:vertx-web-proxy:$vertxVersion")

// https://mvnrepository.com/artifact/com.auth0/java-jwt
implementation("com.auth0:java-jwt:4.4.0")

implementation(group = "org.apache.logging.log4j", name = "log4j-api", version = log4jVersion)
implementation(group = "org.apache.logging.log4j", name = "log4j-core", version = log4jVersion)
implementation(group = "org.apache.logging.log4j", name = "log4j-slf4j2-impl", version = log4jVersion)

// recaptcha v2 1.0.4
implementation("com.github.triologygmbh:reCAPTCHA-V2-java:1.0.4")

// https://mvnrepository.com/artifact/commons-codec/commons-codec
implementation(group = "commons-codec", name = "commons-codec", version = "1.16.1")

// https://mvnrepository.com/artifact/org.springframework/spring-context
implementation("org.springframework:spring-context:5.3.32")

// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation("com.google.code.gson:gson:2.10.1")

implementation("org.pf4j:pf4j:${pf4jVersion}")
}

tasks {
register("copyJar") {
if (shadowJar.get().archiveFile.get().asFile.parentFile.absolutePath != buildDir.absolutePath) {
doLast {
copy {
from(shadowJar.get().archiveFile.get().asFile.absolutePath)
into(buildDir)
}
}
}

outputs.upToDateWhen { false }
mustRunAfter(shadowJar)
}

register("buildDev") {
dependsOn("build")
}

shadowJar {
manifest {
val attrMap = mutableMapOf<String, String>()

if (project.gradle.startParameter.taskNames.contains("buildDev"))
attrMap["MODE"] = "DEVELOPMENT"

attrMap["VERSION"] = version.toString()
attrMap["BUILD_TYPE"] = buildType

attributes(attrMap)
}

archiveFileName.set("${rootProject.name}-${version}.jar")

if (project.gradle.startParameter.taskNames.contains("publish")) {
archiveFileName.set(archiveFileName.get().lowercase())
}
}

jar {
enabled = false
dependsOn(shadowJar)
dependsOn("copyJar")
}
}

tasks.named<JavaExec>("run") {
environment("EnvironmentType", "DEVELOPMENT")
environment("PanoVersion", version)
environment("PanoBuildType", buildType)
pluginsDir?.let { systemProperty("pf4j.pluginsDir", it.absolutePath) }
}

application {
mainClass.set(appMainClass)
}

publishing {
repositories {
maven {
name = "Pano"
url = uri("https://maven.pkg.github.com/panocms/pano")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME_GITHUB")
password = project.findProperty("gpr.token") as String? ?: System.getenv("TOKEN_GITHUB")
}
}
}

publications {
create<MavenPublication>("shadow") {
project.extensions.configure<com.github.jengelman.gradle.plugins.shadow.ShadowExtension> {
artifactId = "pano"
component(this@create)
}
}
}
}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class Main : CoroutineVerticle() {
} catch (e: Exception) {
System.getenv("PanoBuildType").toString()
}
)
)!!
}

@JvmStatic
Expand All @@ -82,15 +82,17 @@ class Main : CoroutineVerticle() {
enum class EnvironmentType {
DEVELOPMENT, RELEASE
}

lateinit var applicationContext: AnnotationConfigApplicationContext
}

private val logger by lazy {
LoggerFactory.getLogger("Pano")
}

private lateinit var router: Router
private lateinit var applicationContext: AnnotationConfigApplicationContext
private lateinit var configManager: ConfigManager
private lateinit var pluginManager: PluginManager

override suspend fun start() {
println(
Expand All @@ -112,6 +114,8 @@ class Main : CoroutineVerticle() {
private suspend fun init() {
initDependencyInjection()

initPlugins()

initConfigManager()

clearTempFiles()
Expand All @@ -127,6 +131,47 @@ class Main : CoroutineVerticle() {
initRoutes()
}

private fun initPlugins() {
logger.info("Initializing plugin manager")

pluginManager = applicationContext.getBean(PluginManager::class.java)

logger.info("Loading plugins")

pluginManager.loadPlugins()


// pluginManager.plugins

logger.info("Enabling plugins")

pluginManager.startPlugins()
// try {
//
// // Iterate through each plugin
// for (plugin in pluginManager.plugins) {
// // Get the main class of the plugin
// val mainClass = plugin.pluginClassLoader.loadClass(plugin.descriptor.pluginClass)
//
// // Get the package of the main class
// val mainPackage = mainClass.`package`
//
// // Print the base package of the main class
// if (mainPackage != null) {
// println("Base package of main class of plugin ${plugin.descriptor.pluginId}: ${mainPackage.name}")
//// applicationContext.scan(mainPackage.name)
// } else {
// println("Main class package not found for plugin ${plugin.descriptor.pluginId}")
// }
// }
//// pluginManager.plugins.map {(it.plugin as PanoPlugin).context.pluginBeanContext.getBeansWithAnnotation(Endpoint::class.java)}
// } catch (e: Exception) {
// logger.error(e.toString())
// }


}

private fun clearTempFiles() {
val tempFolder = File(configManager.getConfig().getString("file-uploads-folder") + "/temp")

Expand Down
17 changes: 17 additions & 0 deletions Pano/src/main/kotlin/com/panomc/platform/PanoPluginLoader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.panomc.platform

import org.pf4j.JarPluginLoader
import org.pf4j.PluginClassLoader
import org.pf4j.PluginDescriptor
import org.pf4j.PluginManager
import java.nio.file.Path

class PanoPluginLoader(pluginManager: PluginManager) : JarPluginLoader(pluginManager) {
override fun loadPlugin(pluginPath: Path, pluginDescriptor: PluginDescriptor): ClassLoader {
val pluginClassLoader = PluginClassLoader(pluginManager, pluginDescriptor, javaClass.classLoader)

pluginClassLoader.addFile(pluginPath.toFile())

return pluginClassLoader
}
}
45 changes: 45 additions & 0 deletions Pano/src/main/kotlin/com/panomc/platform/PluginEventManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.panomc.platform

import com.panomc.platform.api.EventListener
import com.panomc.platform.api.PanoEventListener
import com.panomc.platform.api.PanoPlugin
import com.panomc.platform.api.PluginEventListener
import org.springframework.context.annotation.AnnotationConfigApplicationContext

class PluginEventManager {
companion object {
private val eventListeners = mutableMapOf<PanoPlugin, MutableList<EventListener>>()

fun getEventListeners() = eventListeners.toMap()

internal inline fun <reified T : PanoEventListener> getPanoEventListeners() =
eventListeners.flatMap { it.value }.filterIsInstance<T>()


inline fun <reified T : PluginEventListener> getEventListeners() =
getEventListeners().flatMap { it.value }.filter { it !is PanoEventListener }.filterIsInstance<T>()
}

internal fun initializePlugin(plugin: PanoPlugin, pluginBeanContext: AnnotationConfigApplicationContext) {
if (eventListeners[plugin] == null) {
eventListeners[plugin] = pluginBeanContext
.getBeansWithAnnotation(com.panomc.platform.api.annotation.EventListener::class.java)
.map { it.value as EventListener }
.toMutableList()
}
}

internal fun unregisterPlugin(plugin: PanoPlugin) {
eventListeners.remove(plugin)
}

fun register(plugin: PanoPlugin, eventListener: EventListener) {
if (eventListeners[plugin]!!.none { it::class == eventListener::class }) {
eventListeners[plugin]!!.add(eventListener)
}
}

fun unRegister(plugin: PanoPlugin, eventListener: EventListener) {
eventListeners[plugin]?.remove(eventListener)
}
}
56 changes: 56 additions & 0 deletions Pano/src/main/kotlin/com/panomc/platform/PluginFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.panomc.platform

import com.panomc.platform.PluginManager.Companion.pluginEventManager
import com.panomc.platform.SpringConfig.Companion.vertx
import com.panomc.platform.api.PanoPlugin
import kotlinx.coroutines.runBlocking
import org.pf4j.DefaultPluginFactory
import org.pf4j.Plugin
import org.pf4j.PluginWrapper
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.AnnotationConfigApplicationContext

class PluginFactory : DefaultPluginFactory() {
companion object {
private val logger = LoggerFactory.getLogger(PluginFactory::class.java)
}

override fun createInstance(pluginClass: Class<*>, pluginWrapper: PluginWrapper): Plugin? {
val pluginBeanContext by lazy {
val pluginBeanContext = AnnotationConfigApplicationContext()

pluginBeanContext.parent = Main.applicationContext
pluginBeanContext.classLoader = pluginClass.classLoader
pluginBeanContext.scan(pluginClass.`package`.name)
pluginBeanContext.refresh()

pluginBeanContext
}

try {
val constructor = pluginClass.getConstructor()

val plugin = constructor.newInstance() as PanoPlugin

pluginEventManager.initializePlugin(plugin, pluginBeanContext)

plugin.pluginId = pluginWrapper.pluginId
plugin.vertx = vertx
plugin.pluginEventManager = pluginEventManager
plugin.environmentType = Main.ENVIRONMENT
plugin.releaseStage = Main.STAGE
plugin.pluginBeanContext = pluginBeanContext
plugin.applicationContext = Main.applicationContext

runBlocking {
plugin.onLoad()
}

return plugin
} catch (e: Exception) {
logger.error(e.message, e)
}

return null
}
}
Loading

0 comments on commit 520bbe9

Please sign in to comment.