@@ -2,12 +2,52 @@ package org.codeoverflow.chatoverflow.framework.helper
2
2
3
3
import java .net .{URL , URLClassLoader }
4
4
5
+ import org .codeoverflow .chatoverflow .WithLogger
6
+
5
7
/**
6
- * This plugin class loader does only exist for plugin security policy checks and
7
- * to expose the addURL method to the package inorder to add all required dependencies after dependency resolution.
8
+ * This plugin class loader is used for plugin security policy checks,
9
+ * to expose the addURL method to the package inorder to add all required dependencies after dependency resolution
10
+ * and most importantly to isolate the plugin from the normal classpath and only access the classpath if it needs to load the ChatOverflow api.
11
+ * Also if this PluginClassLoader had access to the classpath the same classes of the classpath would have
12
+ * higher priority over the classes in this classloader which could be a problem if a plugin uses a newer version
13
+ * of a dependency that the framework.
8
14
*
9
15
* @param urls Takes an array of urls an creates a simple URLClassLoader with it
10
16
*/
11
- class PluginClassLoader (urls : Array [URL ]) extends URLClassLoader (urls) {
17
+ class PluginClassLoader (urls : Array [URL ]) extends URLClassLoader (urls, PluginClassLoader .platformClassloader) {
18
+ // Note the platform classloader in the constructor of the URLClassLoader as the parent.
19
+ // That way the classloader skips the app classloader with the classpath when it is asks it's parents for classes.
20
+
12
21
protected [helper] override def addURL (url : URL ): Unit = super .addURL(url) // just exposes this method to be package-private instead of class internal protected
22
+
23
+ override def loadClass (name : String , resolve : Boolean ): Class [_] = {
24
+ if (name.startsWith(" org.codeoverflow.chatoverflow.api" )) {
25
+ PluginClassLoader .appClassloader.loadClass(name) // Api needs to be loaded from the classpath
26
+ } else {
27
+ super .loadClass(name, resolve) // non api class. load it as normal
28
+ }
29
+ }
30
+ }
31
+
32
+ /**
33
+ * This companion object holds references to the app classloader (normal classloader, includes java and classpath)
34
+ * and to the extension/platform classloader depending on the java version that excludes the classpath,
35
+ * but still includes everything from java.
36
+ */
37
+ private object PluginClassLoader extends WithLogger {
38
+ val appClassloader : ClassLoader = this .getClass.getClassLoader
39
+ val platformClassloader : ClassLoader = {
40
+ var current = appClassloader
41
+ while (current != null && ! current.getClass.getName.contains(" ExtClassLoader" ) && // ExtClassLoader is java < 9
42
+ ! current.getClass.getName.contains(" PlatformClassLoader" )) { // PlatformClassLoader is java >= 9
43
+ current = current.getParent
44
+ }
45
+
46
+ if (current != null ) {
47
+ current
48
+ } else {
49
+ logger error " Platform classloader couldn't be found. Falling back to normal app classloader."
50
+ appClassloader
51
+ }
52
+ }
13
53
}
0 commit comments