Affects Version/s: Scala 2.11.7
Fix Version/s: None
Component/s: Repl / Interpreter
- Windows 7; Oracle JDK build 1.8.0_31-b13; sbt 0.13.8
- project\build.properties -> sbt.version=0.13.1
ScalaClassLoader.asContext highjacks the thread context classloader and causes classloader branching that destroy proper parent relationship
The thing I tried solving was running some runtime compilation that would allow some parts of my configuration be written in scala code (essentially the config is a function that maps stuff).
To get that working this is what I did.
This code worked in an isolated test program, but once I started combining the thing with real code everything broke down. Example issue:
Those two classes come from the same jar, so typing between them should not ever be a problem. To me it looked like a Classloader issue, and it was.
Turns out the org.apache.velocity.runtime.log.LogChute interface was loaded by:
sun.misc.Launcher$AppClassLoader (1 instance in heap)
org.apache.velocity.runtime.log.NullLogChute and second copy of LogChute where loaded by:
scala.reflect.internal.util.ScalaClassLoader$URLClassLoader (1 instance in heap)
Both classloaders had a common parent:
which makes them classloader siblings explaining the incompatible types.
I thought it was going wrong in the compile part, but as it turns out, it breaks in the eval() part.
At some point this sequence of things is called (not full stack listed, top down order).
These are the instresting bits
Now what happens here is:
- When the context class loader happens to be a java URLClassloader, it gets stripped and repackaged in a scala URLClassloader, with reuse of the parent of the original loader
- This destroys a meaningful parent relationship that should have been there, allowing commons stuff to be loaded by a common Classloader
- It also replaces what is 'saved' in the asContext utility
- Hence it is what is restored as thread context class loader once asContext is done
Which led to the breakage described above.
With this insight I was able to build this workarround (it prepackages the context class loader as the fallback case does in the above code):
Though I'm glad I could work around it, I can't help at feel this smells like a bug.