-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Francisco Solis <imfran@duck.com>
- Loading branch information
Showing
1 changed file
with
105 additions
and
0 deletions.
There are no files selected for viewing
105 changes: 105 additions & 0 deletions
105
src/main/kotlin/xyz/theprogramsrc/simplecoreapi/global/module/ModuleClassLoader.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package xyz.theprogramsrc.simplecoreapi.global.module | ||
|
||
import com.google.common.io.ByteStreams | ||
import java.io.File | ||
import java.net.URLClassLoader | ||
import java.security.CodeSource | ||
import java.util.concurrent.CopyOnWriteArraySet | ||
import java.util.jar.JarFile | ||
|
||
class ModuleClassLoader( | ||
private val moduleDescription: ModuleDescription, | ||
private val file: File | ||
): URLClassLoader(arrayOf(file.toURI().toURL())) { | ||
|
||
companion object { | ||
private val loaders = CopyOnWriteArraySet<ModuleClassLoader>() | ||
|
||
init { | ||
ClassLoader.registerAsParallelCapable() | ||
} | ||
} | ||
|
||
private val jarFile = JarFile(file) | ||
private val url = file.toURI().toURL() | ||
private val manifest = jarFile.manifest | ||
private var module: Module? = null | ||
|
||
init { | ||
loaders.add(this) | ||
} | ||
|
||
override fun loadClass(name: String, resolve: Boolean): Class<*> = loadClassInternally(name, resolve) | ||
|
||
private fun loadClassInternally(name: String, resolve: Boolean, checkOther: Boolean = true): Class<*> { | ||
try { | ||
val result = super.loadClass(name, resolve) | ||
if(checkOther || result.classLoader == this) { | ||
return result | ||
} | ||
} catch (_: ClassNotFoundException) {} | ||
|
||
if (checkOther) { | ||
for (loader in loaders) { | ||
if (loader != this) { | ||
try { | ||
return loader.loadClassInternally(name, resolve, false) | ||
} catch (_: ClassNotFoundException) {} | ||
} | ||
} | ||
} | ||
|
||
throw ClassNotFoundException(name) | ||
} | ||
|
||
override fun findClass(name: String): Class<*> { | ||
val path = name.replace('.', '/').plus(".class") | ||
val entry = jarFile.getJarEntry(path) | ||
|
||
val classBytes = jarFile.getInputStream(entry).use { | ||
ByteStreams.toByteArray(it) | ||
} | ||
|
||
val dot = name.lastIndexOf('.') | ||
if(dot != -1) { | ||
val pkgName = name.substring(0, dot) | ||
if(getPackage(pkgName) == null) { | ||
try { | ||
if(manifest != null) { | ||
definePackage(pkgName, manifest, url) | ||
} else { | ||
definePackage(pkgName, null, null, null, null, null, null, null) | ||
} | ||
}catch (_: IllegalArgumentException) { | ||
if(getPackage(pkgName) == null) { | ||
throw IllegalStateException("Cannot find package $pkgName") | ||
} | ||
} | ||
} | ||
|
||
val signers = entry.codeSigners | ||
return defineClass(name, classBytes, 0, classBytes.size, CodeSource(url, signers)) | ||
} | ||
|
||
return super.findClass(name) | ||
} | ||
|
||
override fun close() { | ||
jarFile.use { | ||
super.close() | ||
} | ||
} | ||
|
||
fun init(module: Module) { | ||
check(module.javaClass.classLoader == this) { | ||
"Module class loader mismatch" | ||
} | ||
|
||
check(this.module == null) { | ||
"Module already initialized" | ||
} | ||
|
||
this.module = module | ||
module.init(this.file, this.moduleDescription) | ||
} | ||
} |