You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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).
This code worked in an isolated test program, but once I started combining the thing with real code everything broke down. Example issue:
Caused by: org.apache.velocity.exception.VelocityException: Failed to initialize an instance of org.apache.velocity.runtime.log.NullLogChute with the current runtime configuration.
at org.apache.velocity.runtime.log.LogManager.createLogChute(LogManager.java:220)
at org.apache.velocity.runtime.log.LogManager.updateLog(LogManager.java:269)
at org.apache.velocity.runtime.RuntimeInstance.initializeLog(RuntimeInstance.java:871)
... 18 more
Caused by: org.apache.velocity.exception.VelocityException: The specified logger class org.apache.velocity.runtime.log.NullLogChute does not implement the org.apache.velocity.runtime.log.LogChute interface.
at org.apache.velocity.runtime.log.LogManager.createLogChute(LogManager.java:181)
... 20 more
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:
sun.misc.Launcher$ExtClassLoader
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).
scala.tools.nsc.interpreter.IMain.WrappedRequest.loadAndRunReq
scala.reflect.internal.util.ScalaClassLoader.asContext
scala.reflect.internal.util.ScalaClassLoader.apply(cl: ClassLoader)
These are the instresting bits
scala.reflect.internal.util.ScalaClassLoader.asContext
implicitdefapply(cl: JClassLoader):ScalaClassLoader= cl match {
casecl: ScalaClassLoader=> cl
casecl: JURLClassLoader=>newURLClassLoader(cl.getURLs.toSeq, cl.getParent)
case _ =>newJClassLoader(cl) withScalaClassLoader
}
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):
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:
sun.misc.Launcher$ExtClassLoader
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).
scala.tools.nsc.interpreter.IMain.WrappedRequest.loadAndRunReq
scala.reflect.internal.util.ScalaClassLoader.asContext
scala.reflect.internal.util.ScalaClassLoader.apply(cl: ClassLoader)
These are the instresting bits
scala.reflect.internal.util.ScalaClassLoader.asContext
scala.reflect.internal.util.ScalaClassLoader.contextLoader
scala.reflect.internal.util.ScalaClassLoader.apply(cl: ClassLoader)
Now what happens here is:
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.
The text was updated successfully, but these errors were encountered: