Skip to content

Commit

Permalink
Add metadata reader to allow Spring Boot can read another plugin jar'…
Browse files Browse the repository at this point in the history
…s classpath resources
  • Loading branch information
vjh0107 committed Feb 1, 2024
1 parent 6318bb0 commit a1e0860
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package kr.summitsystems.springbukkit.core

import org.springframework.beans.factory.FactoryBean
import org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory
import org.springframework.context.ApplicationListener
import org.springframework.context.ResourceLoaderAware
import org.springframework.context.event.ContextRefreshedEvent
import org.springframework.core.io.ResourceLoader
import org.springframework.core.type.classreading.CachingMetadataReaderFactory

/**
* https://github.com/spring-projects/spring-boot/pull/39321
*/
internal class SpringBukkitMetadataReaderFactoryBean : FactoryBean<ConcurrentReferenceCachingMetadataReaderFactory>, ApplicationListener<ContextRefreshedEvent>, ResourceLoaderAware {
private var metadataReaderFactory: ConcurrentReferenceCachingMetadataReaderFactory? = null

override fun setResourceLoader(resourceLoader: ResourceLoader) {
metadataReaderFactory = ConcurrentReferenceCachingMetadataReaderFactory(resourceLoader)
}

override fun getObject(): ConcurrentReferenceCachingMetadataReaderFactory? {
return metadataReaderFactory
}

override fun getObjectType(): Class<*> {
return CachingMetadataReaderFactory::class.java
}

override fun isSingleton(): Boolean {
return true
}

override fun onApplicationEvent(event: ContextRefreshedEvent) {
metadataReaderFactory?.clearCache()
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
package kr.summitsystems.springbukkit.core

import org.bukkit.plugin.Plugin
import org.bukkit.plugin.java.JavaPlugin
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.support.BeanDefinitionBuilder
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.boot.Banner
import org.springframework.boot.WebApplicationType
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.env.YamlPropertySourceLoader
import org.springframework.context.ApplicationContextInitializer
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.support.GenericApplicationContext
import org.springframework.core.annotation.AnnotationUtils
import org.springframework.core.env.PropertiesPropertySource
import org.springframework.core.io.DefaultResourceLoader
import org.springframework.core.io.FileSystemResource
import java.io.File
import java.util.*

abstract class SpringBukkitPlugin : JavaPlugin(), ApplicationContextInitializer<ConfigurableApplicationContext>, DisposableContainer, Disposable {
abstract class SpringBukkitPlugin : JavaPlugin(), ApplicationContextInitializer<GenericApplicationContext>,
DisposableContainer, Disposable {
private var applicationContext: ConfigurableApplicationContext? = null
private val disposables: MutableList<Disposable> = mutableListOf()

Expand Down Expand Up @@ -55,19 +60,23 @@ abstract class SpringBukkitPlugin : JavaPlugin(), ApplicationContextInitializer<

private fun runApplication(applicationSource: Class<*>): ConfigurableApplicationContext {
Thread.currentThread().contextClassLoader = this.classLoader
return SpringApplicationBuilder(DefaultResourceLoader(this.classLoader), applicationSource)
return SpringApplicationBuilder(
SpringBukkitResourceLoader(this, this.classLoader, server.pluginManager),
applicationSource
)
.web(WebApplicationType.NONE)
.bannerMode(Banner.Mode.OFF)
.logStartupInfo(false)
.initializers(this)
.run()
}

override fun initialize(applicationContext: ConfigurableApplicationContext) {
override fun initialize(applicationContext: GenericApplicationContext) {
applicationContext.setClassLoader(this.classLoader)
registerYamlPropertySource(applicationContext, "application.yml")
registerYamlPropertySource(applicationContext, "config.yml")
registerPropertiesPropertySource(applicationContext, "application.properties")
registerMetadataReaderFactory(applicationContext)
registerPluginBean(applicationContext)
}

Expand All @@ -92,7 +101,22 @@ abstract class SpringBukkitPlugin : JavaPlugin(), ApplicationContextInitializer<
}
}

private fun registerPluginBean(applicationContext: ConfigurableApplicationContext) {
applicationContext.beanFactory.registerSingleton("plugin", this)
private fun registerPluginBean(beanDefinitionRegistry: BeanDefinitionRegistry) {
val definition: BeanDefinition = BeanDefinitionBuilder
.rootBeanDefinition(
Plugin::class.java
) { this }
.getBeanDefinition()
beanDefinitionRegistry.registerBeanDefinition(this::class.qualifiedName!!, definition)
}

private fun registerMetadataReaderFactory(beanDefinitionRegistry: BeanDefinitionRegistry) {
val beanName = "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"
val definition: BeanDefinition = BeanDefinitionBuilder
.rootBeanDefinition(
SpringBukkitMetadataReaderFactoryBean::class.java
) { SpringBukkitMetadataReaderFactoryBean() }
.getBeanDefinition()
beanDefinitionRegistry.registerBeanDefinition(beanName, definition)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package kr.summitsystems.springbukkit.core

import org.bukkit.plugin.PluginManager
import org.bukkit.plugin.java.JavaPlugin
import org.springframework.core.io.DefaultResourceLoader
import org.springframework.core.io.Resource

class SpringBukkitResourceLoader(
plugin: JavaPlugin,
classLoader: ClassLoader,
private val pluginManager: PluginManager
): DefaultResourceLoader(classLoader) {
private val dependResourceLoaders: List<InternalDefaultResourceLoader> = plugin.description.depend.map { pluginId ->
val javaPlugin = pluginManager.getPlugin(pluginId) as? JavaPlugin
?: throw IllegalStateException("The plugin with id $pluginId is not found.")
val getClassLoaderMethodAccessor = JavaPlugin::class.java.getDeclaredMethod("getClassLoader")
getClassLoaderMethodAccessor.isAccessible = true
val pluginClassLoader = getClassLoaderMethodAccessor.invoke(javaPlugin) as ClassLoader
InternalDefaultResourceLoader(pluginClassLoader)
}

override fun getResource(location: String): Resource {
val superResource = super.getResource(location)
if (superResource.exists()) {
return superResource
} else {
dependResourceLoaders.forEach { dependResourceLoader ->
val dependResource = dependResourceLoader.getResource(location)
if (dependResource.exists()) {
return dependResource
}
}
}
return superResource
}

override fun getResourceByPath(path: String): Resource {
val superResource = super.getResourceByPath(path)
if (superResource.exists()) {
return superResource
} else {
dependResourceLoaders.forEach { dependResourceLoader ->
val dependResource = dependResourceLoader.getResourceByPath(path)
if (dependResource.exists()) {
return dependResource
}
}
}
return superResource
}

private class InternalDefaultResourceLoader(classLoader: ClassLoader): DefaultResourceLoader(classLoader) {
public override fun getResourceByPath(path: String): Resource {
return super.getResourceByPath(path)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import org.bukkit.plugin.Plugin
import java.util.concurrent.TimeUnit

class FoliaBukkitTaskScheduler(
private val plugin: Plugin
) : BukkitTaskScheduler {
private val globalRegionScheduler: GlobalRegionScheduler = Bukkit.getGlobalRegionScheduler()
private val plugin: Plugin,
private val globalRegionScheduler: GlobalRegionScheduler = Bukkit.getGlobalRegionScheduler(),
private val asyncScheduler: AsyncScheduler = Bukkit.getAsyncScheduler()
) : BukkitTaskScheduler {


override fun schedule(task: (BukkitScheduledTask) -> Unit): BukkitScheduledTask {
val scheduledTask = globalRegionScheduler.run(plugin) {
Expand Down

0 comments on commit a1e0860

Please sign in to comment.