Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit 2b9d6ab

Browse files
committed
Isolate plugins from the classpath so that the framework doesn't override plugin dependencies
1 parent 5e49491 commit 2b9d6ab

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

src/main/scala/org/codeoverflow/chatoverflow/framework/helper/PluginClassLoader.scala

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,52 @@ package org.codeoverflow.chatoverflow.framework.helper
22

33
import java.net.{URL, URLClassLoader}
44

5+
import org.codeoverflow.chatoverflow.WithLogger
6+
57
/**
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.
814
*
915
* @param urls Takes an array of urls an creates a simple URLClassLoader with it
1016
*/
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+
1221
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+
}
1353
}

0 commit comments

Comments
 (0)