ClassLoading multiple binary incompatible scala projects

Hello there, my use case for Scala might be uncommon, but I’d like to share this project with you all.

For some time I have been developing plugins using the Bukkit server-side mod api for the game Minecraft. The projects provides a Java API, which is nice so I can use the api Bukkit provides to write mods in Scala. It’s not a problem at all, I just bundle the Scala runtime in my jar using the sbt assembly plugin. But the problem arises when multiple plugins use different versions of Scala. Plugins can acces eachother classes because their classloaders find classes from their own jars, from their parent classloaders (classes from bukkit), and classes that are shared by the JavaPluginLoader. The usual workaround to prevent binary incompatible plugins is to relocate the scala library classes using shaderules. The result is unnecessarily large jar files, unnecesary consumption of extra RAM by the minecraft server, and slow compile times.

Introducing: ScalaPluginLoader
A Java plugin that downloads scala runtime classes on server startup, loads their classes, and then loads Scala plugins. It’s using a similar classloader hierarcy as the JavaPluginLoader, but the ScalaPluginClassLoaders can only find classes that use binary compatible versions of Scala.

The reason I’m posting this topic is because I’m looking for feedback. I’m still relatively new to Scala and there’s a few issues still in the ScalaLoader plugin.

  • I haven’t found a good way to make classes from scala plugins accessible to JavaPlugins yet. ScalaLoader currently provides an api to force-load all classes in the ScalaPlugin’s jar and inject them into the JavaPlugin’s classloader, but this performs worse than theoretically needed, and can cause memory leaks. The Scala standard library classes are not injected yet because that would consume a lot of RAM. I thought about solving this ussue by analysing the JavaPlugin’s class files and see which types is actually needs, and inject only those classes that it needs.
  • There is no support for shared third-party Scala libraries yet. I’m thinking of implementing by refactoring the ClassLoader hierarchy. There is the ScalaLibraryClassLoader which loads the scala runtime classes (library and reflect) for a specific version of Scala, then ScalaLibraryDependencyClassLoader (probably not the actual name) which can load classes from scala projects that live on a maven repository. These classes will be shared by the ScalaPluginLoader, and have a ScalaLibraryClassLoader as its parent. Then the ScalaPluginClassLoader which will also have a ScalaLibraryClassLoader as its parent, but it able to find classes from other plugins ans scala libraries that are shared by the ScalaPluginLoader. ScalaLibraryDependencyClassLoader and ScalaPluginClassLoader will have the same super type to that these classloaders can be contained into the same collections.

Links: