11package com.lambda.plugin
22
33import com.lambda.Lambda.LOG
4+ import com.lambda.core.Loadable
5+ import com.lambda.util.FolderRegister.listRecursive
6+ import com.lambda.util.FolderRegister.mods
47import java.io.File
58import java.lang.reflect.InvocationTargetException
69import java.lang.reflect.Method
710import java.net.URL
811import java.util.jar.JarFile
912
10- object PluginRegistry {
11- private val knotClassLoader: ClassLoader = Thread .currentThread().contextClassLoader
12- private val addUrlMethod: Method
13+ object PluginRegistry : Loadable {
14+ private val errorMessageFMT = """
15+ A serious error occurred while loading a plugin.
16+ This is likely a bug in the plugin itself but it could also be a bug in Lambda.
17+ If you are a developer, please check the plugin's main class and load method.
18+ If you are a regular user, please report this issue to Lambda team and the plugin developer.
19+
20+ Plugin: {}
21+ Stacktrace: {}
22+ """ .trimIndent()
1323
14- private var loadClass: Class <* >? = null
15- private var loadMethod: Method ? = null
16- private var loadInstance: Any? = null // TODO: This won't work with constructors
24+ private val threadClassLoader: ClassLoader = Thread .currentThread().contextClassLoader
25+ private val addUrlMethod: Method
1726
1827 init {
1928 try {
20- addUrlMethod = knotClassLoader.javaClass.getMethod(" addUrlFwd" , URL ::class .java)
29+ addUrlMethod =
30+ threadClassLoader.javaClass.getMethod(" addUrlFwd" , URL ::class .java) // TODO: Check for other methods if not found
31+
2132 addUrlMethod.isAccessible = true
2233 } catch (e: NoSuchMethodException ) {
2334 throw RuntimeException (" Failed to get the addURL method from the KnotClassLoader." )
@@ -28,64 +39,54 @@ object PluginRegistry {
2839 IllegalAccessException ::class ,
2940 IllegalArgumentException ::class ,
3041 InvocationTargetException ::class )
31- fun feedJarToKnot (jar : File ) {
32- addUrlMethod.invoke(knotClassLoader , jar.toURI().toURL())
42+ fun forceFeedJar (jar : File ) {
43+ addUrlMethod.invoke(threadClassLoader , jar.toURI().toURL())
3344 }
3445
35- private fun preLoadPlugin (file : File ): PluginClassLoader ? {
36- runCatching { feedJarToKnot(file) }
37- .onFailure {
38- LOG .error(" Failed to add the URL of the plugin $file " , it)
39- return null
40- }
46+ private fun loadPlugin (file : File ) {
47+ runCatching {
48+ // Someone got a better idea?
49+ // Don't like nested try-catch blocks
50+ runCatching { forceFeedJar(file) }
51+ .onFailure {
52+ LOG .error(" Failed to feed the plugin {} to the thread class loader" , file)
53+ }
4154
42- LOG .debug(" Added the URL of the plugin {} to the Knot class loader" , file)
55+ LOG .debug(" Added the URL of the plugin {} to the thread class loader" , file)
4356
44- val jar = JarFile (file)
45- val loader = PluginClassLoader (jar, knotClassLoader)
57+ val jar = JarFile (file)
4658
47- val mainClass = jar.manifest.mainAttributes.getValue(" Main-Class" )
48- ? : return null . also { LOG .error(" The plugin $jar does not have a main class" ) }
59+ val mainClass = jar.manifest.mainAttributes.getValue(" Main-Class" )
60+ ? : return LOG .error(" The plugin $jar does not have a main class" )
4961
50- loadClass = loader .loadClass(mainClass)
62+ val loadClass = threadClassLoader .loadClass(mainClass)
5163
52- loadInstance =
53- loadClass? .declaredFields? .firstOrNull { it.name == " INSTANCE" }?.get(null )
54- ? : loadClass? .constructors? .firstOrNull()?.newInstance()
55- ? : return null . also { LOG .error(" The plugin $jar does not have an object instance or a public constructor" ) }
64+ val loadInstance =
65+ loadClass.declaredFields.firstOrNull { it.name == " INSTANCE" }?.get(null )
66+ ? : loadClass.constructors.firstOrNull()?.newInstance()
67+ ? : return LOG .error(" The plugin $jar does not have an object instance or a public constructor" )
5668
57- loadMethod =
58- loadClass?.methods?.find { it.name == " load" } ? : return null
59- . also { LOG .warn(" The plugin $jar does not have a load method" ) }
69+ val loadMethod =
70+ loadClass?.methods?.find { it.name == " load" }
71+ ? : return LOG .warn(" The plugin $jar does not have a load method" )
6072
61- return loader
73+ loadMethod.invoke(loadInstance)
74+ }.onFailure { LOG .error(errorMessageFMT, file, Thread .currentThread().stackTrace.joinToString(" \n " )) }
6275 }
6376
64- private fun loadPlugin (file : File ) {
65- runCatching {
66- loadMethod?.invoke(loadInstance)
67- }
68- .onFailure {
69- return LOG .error("""
70- A serious error occurred while loading a plugin.
71- This is likely a bug in the plugin itself but it could also be a bug in Lambda.
72- If you are a developer, please check the plugin's main class and load method.
73- If you are a regular user, please report this issue to Lambda team and the plugin developer.
74-
75- Plugin: $file
76- Stacktrace: ${Thread .currentThread().stackTrace.joinToString(" \n " )}
77- """ .trimIndent())
77+ override fun load (): String {
78+ val plugins = mods
79+ .listRecursive {
80+ it.isFile && it.extension == " jar"
81+ // Not a fan of creating a new JarFile instance every iteration
82+ && JarFile (it).manifest.mainAttributes.getValue(" Lambda-Plugin" ) == " true"
7883 }
79- }
84+ val size = plugins.count()
8085
81- fun preLoad (path : File ): List <PluginClassLoader > {
82- val plugins = path.walk().filter { it.isFile && it.extension == " jar" }.toList()
83- return plugins.mapNotNull { preLoadPlugin(it) }
84- }
86+ plugins.forEach(::loadPlugin)
87+
88+ val plural = if (size == 1 ) " " else " s"
8589
86- fun load (path : File ): String {
87- val plugins = path.walk().filter { it.isFile && it.extension == " jar" }.toList()
88- plugins.forEach { loadPlugin(it) }
89- return " Loaded ${plugins.size} plugins"
90+ return " Loaded $size plugin$plural "
9091 }
9192}
0 commit comments