I’m trying to make my Compose application self-upd...
# compose-desktop
s
I’m trying to make my Compose application self-updating. I’ve never messed with `ClassLoader`s before, but thought it might be possible to do with them.
Copy code
val jarFile = File("<Path to downloaded jar>")
val classLoader = URLClassLoader(
    arrayOf(jarFile.toURI().toURL()),
    Thread.currentThread().contextClassLoader
)

val myMainClass = Class.forName("MyClass", true, classLoader)
val mainMethod = myMainClass.getDeclaredMethod("main")
mainMethod.invoke(null)
This works until the AWT loop starts and my class loader hasn’t been set on that thread so it can’t find the right class. I’m sure there is a better way to do this. Can anyone point me in the right direction?
Copy code
Thread.currentThread().contextClassLoader = classLoader
Thread.getAllStackTraces().forEach { (t, _) ->
    if (t.contextClassLoader != null) {
        t.contextClassLoader = classLoader
    }
}
Seems to work, but I imagine new threads that spawn won’t have the class loader. There has to be a better way to do this.
j
ClassLoaders are tricky.  Setting the 
contextClassLoader
 feels very sketchy to me; you shouldn't need to set the 
contextClassLoader
. I'm not sure I understood the issue with the AWT loop.  AWT is always going to get loaded using the parent classloader because it is part of the 
java.
 namespace which is special cased (for security reasons?).  But this shouldn't matter, as long as all your classes are loaded using your custom ClassLoader.  Any class referenced by 
MyClass
 and which is only provided by 
jarFile
 should be loaded by your classloader. If you can elaborate on specifically what problem you were facing with your old approach, perhaps we can help.  Although it is worth noting that we are venturing into purely java territory, and there is nothing compose-specific about these questions, so you might get more visibility in a place like StackOverflow.
c
There’s a library specifically for this, update4j. I haven’t used it myself but it seems pretty solid, and will probably give you a much better result than trying to do it all yourself. As Jim said, classloaders are tricky and there’s a thousand ways you could get it wrong which would lead to subtle “heisenbugs”, so it’s better to leave it up to the folks who actually understand this stuff 😅 https://github.com/update4j/update4j
s
@jim Just saw your responses. Thanks for taking the time.
Setting the 
contextClassLoader
 feels very sketchy to me; you shouldn’t need to set the 
contextClassLoader
This all feels super sketchy to me lol
Any class referenced by 
MyClass
 and which is only provided by 
jarFile
 should be loaded by your classloader.
I actually didn’t see that happening. Based on what I was seeing, I made the assumption that the classloader on the thread that’s running the code is what is used to load the new class.
Copy code
Thread.currentThread().contextClassLoader = classLoader
Thread.getAllStackTraces().forEach { (t, _) ->
    if (t.contextClassLoader != null) {
        t.contextClassLoader = classLoader
    }
}
Actually did get my jar running. I read a blog post about how resolving all memory leaks with an approach like this is almost completely impossible though. I think taking this approach for an auto-updating app seems bad.
@Casey Brooks Thanks - I’ll give update4j a shot