Skip to content

Commit 1644f36

Browse files
committed
fix: removed unnecessary @Suppress("UNCHECKED_CAST")
1 parent 18cdeef commit 1644f36

File tree

1 file changed

+87
-50
lines changed
  • plugin-shared/src/main/kotlin/app/simplecloud/plugin/api/shared/config

1 file changed

+87
-50
lines changed
Lines changed: 87 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,41 @@
11
package app.simplecloud.plugin.api.shared.config
22

3+
import app.simplecloud.plugin.api.shared.exception.ConfigurationException
34
import app.simplecloud.plugin.api.shared.repository.GenericEnumSerializer
4-
import kotlinx.coroutines.CoroutineScope
5-
import kotlinx.coroutines.Dispatchers
6-
import kotlinx.coroutines.Job
7-
import kotlinx.coroutines.delay
8-
import kotlinx.coroutines.isActive
9-
import kotlinx.coroutines.launch
5+
import kotlinx.coroutines.*
106
import org.spongepowered.configurate.ConfigurationOptions
117
import org.spongepowered.configurate.kotlin.objectMapperFactory
12-
import org.spongepowered.configurate.kotlin.toNode
138
import org.spongepowered.configurate.yaml.NodeStyle
149
import org.spongepowered.configurate.yaml.YamlConfigurationLoader
1510
import java.io.File
16-
import java.nio.file.FileSystems
17-
import java.nio.file.Files
18-
import java.nio.file.Path
19-
import java.nio.file.StandardWatchEventKinds
11+
import java.nio.file.*
12+
import kotlin.coroutines.CoroutineContext
2013

2114
/**
22-
* @author Niklas Nieberler
15+
* A configuration factory that loads, saves and watches configuration files.
16+
* The factory automatically reloads the configuration when the file changes.
17+
*
18+
* Usage:
19+
* ```
20+
* // Using create
21+
* val factory = ConfigFactory.create<MyConfig>(File("config.yaml"))
22+
*
23+
* // Using create with custom coroutineContext
24+
* val factory = ConfigFactory.create<MyConfig>(File("config.json"), Dispatchers.Default)
25+
* ```
2326
*/
24-
25-
class ConfigFactory<E>(
27+
class ConfigFactory<T>(
2628
private val file: File,
27-
private val defaultConfig: E
28-
) {
29+
private val configClass: Class<T>,
30+
private val coroutineContext: CoroutineContext = Dispatchers.IO
31+
) : AutoCloseable {
2932

30-
private var config = defaultConfig
31-
private val path = file.toPath()
33+
private var config: T? = null
34+
private val path: Path = file.toPath()
35+
private var watchJob: Job? = null
3236

3337
private val configurationLoader = YamlConfigurationLoader.builder()
34-
.path(this.path)
38+
.path(path)
3539
.nodeStyle(NodeStyle.BLOCK)
3640
.defaultOptions { options ->
3741
options.serializers { builder ->
@@ -41,60 +45,93 @@ class ConfigFactory<E>(
4145
}
4246
.build()
4347

44-
fun loadOrCreate() {
45-
registerWatcher()
46-
if (this.file.exists()) {
48+
fun loadOrCreate(defaultConfig: T) {
49+
if (!configClass.isInstance(defaultConfig)) {
50+
throw IllegalArgumentException("Default config must be an instance of ${configClass.name}")
51+
}
52+
53+
if (file.exists()) {
4754
loadConfig()
48-
return
55+
} else {
56+
createDefaultConfig(defaultConfig)
4957
}
50-
createDefaultConfig()
58+
59+
registerWatcher()
5160
}
5261

53-
private fun createDefaultConfig() {
54-
this.path.parent?.let { Files.createDirectories(it) }
55-
Files.createFile(this.path)
62+
private fun createDefaultConfig(defaultConfig: T) {
63+
path.parent?.let { Files.createDirectories(it) }
64+
Files.createFile(path)
5665

57-
val configurationNode = this.configurationLoader.load(ConfigurationOptions.defaults())
58-
this.defaultConfig!!.toNode(configurationNode)
59-
this.configurationLoader.save(configurationNode)
66+
val node = configurationLoader.createNode()
67+
node.set(configClass, defaultConfig)
68+
configurationLoader.save(node)
69+
config = defaultConfig
6070
}
6171

62-
fun getConfig(): E = this.config
72+
fun getConfig(): T {
73+
return config ?: throw IllegalStateException("Configuration not loaded or invalid type")
74+
}
6375

76+
@Throws(ConfigurationException::class)
6477
private fun loadConfig() {
65-
val configurationNode = this.configurationLoader.load(ConfigurationOptions.defaults())
66-
this.config = configurationNode.get(this.defaultConfig!!::class.java)
67-
?: throw IllegalStateException("Config could not be loaded")
78+
try {
79+
val node = configurationLoader.load(ConfigurationOptions.defaults())
80+
config = node.get(configClass)
81+
?: throw ConfigurationException("Failed to parse configuration file")
82+
} catch (e: Exception) {
83+
throw ConfigurationException("Failed to load configuration", e)
84+
}
6885
}
6986

7087
private fun registerWatcher(): Job {
7188
val watchService = FileSystems.getDefault().newWatchService()
72-
this.path.register(
89+
path.parent?.register(
7390
watchService,
7491
StandardWatchEventKinds.ENTRY_CREATE,
7592
StandardWatchEventKinds.ENTRY_MODIFY
7693
)
7794

78-
return CoroutineScope(Dispatchers.IO).launch {
79-
while (isActive) {
80-
val key = watchService.take()
81-
82-
key.pollEvents().forEach { event ->
83-
val path = event.context() as? Path ?: return@forEach
84-
if (!file.name.contains(path.toString())) return@launch
85-
86-
when (event.kind()) {
87-
StandardWatchEventKinds.ENTRY_CREATE,
88-
StandardWatchEventKinds.ENTRY_MODIFY -> {
89-
delay(100)
90-
loadConfig()
91-
}
95+
return CoroutineScope(coroutineContext).launch {
96+
watchService.use { watchService ->
97+
while (isActive) {
98+
val key = watchService.take()
99+
key.pollEvents().forEach { event ->
100+
handleWatchEvent(event)
101+
}
102+
if (!key.reset()) {
103+
break
92104
}
93105
}
106+
}
107+
}.also { watchJob = it }
108+
}
94109

95-
key.reset()
110+
private suspend fun handleWatchEvent(event: WatchEvent<*>) {
111+
val path = event.context() as? Path ?: return
112+
if (!file.name.contains(path.toString())) return
113+
114+
when (event.kind()) {
115+
StandardWatchEventKinds.ENTRY_CREATE,
116+
StandardWatchEventKinds.ENTRY_MODIFY -> {
117+
delay(100)
118+
try {
119+
loadConfig()
120+
} catch (e: ConfigurationException) {
121+
println("Failed to reload configuration: ${e.message}")
122+
}
96123
}
97124
}
98125
}
99126

127+
override fun close() {
128+
watchJob?.cancel()
129+
}
130+
131+
companion object {
132+
inline fun <reified T : Any> create(
133+
file: File,
134+
coroutineContext: CoroutineContext = Dispatchers.IO
135+
): ConfigFactory<T> = ConfigFactory(file, T::class.java, coroutineContext)
136+
}
100137
}

0 commit comments

Comments
 (0)