Why can't the SBT stream script class loader load JDK class files as resources?

lihaoyi test$ tree . └── Foo.scala 0 directories, 1 file lihaoyi test$ cat Foo.scala object Main{ def main(args: Array[String]): Unit = { println(getClass.getClassLoader.getResourceAsStream("java/lang/String.class")) println(getClass.getClassLoader.getClass) println(Thread.currentThread().getContextClassLoader.getResourceAsStream("java/lang/String.class")) println(Thread.currentThread().getContextClassLoader.getClass) } } lihaoyi test$ sbt run [info] Loading global plugins from /Users/lihaoyi/.sbt/0.13/plugins [info] Set current project to test (in build file:/Users/lihaoyi/Dropbox/Workspace/test/) [info] Updating {file:/Users/lihaoyi/Dropbox/Workspace/test/}test... [info] Resolving org.fusesource.jansi#jansi;1.4 ... [info] Done updating. [info] Compiling 1 Scala source to /Users/lihaoyi/Dropbox/Workspace/test/target/scala-2.10/classes... [info] Running Main sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@18e3 8ff2 class sbt.classpath.ClasspathUtilities$$anon$1 null class sbt.classpath.ClasspathFilter [success] Total time: 2 s, completed 29 May, 2017 4:14:11 PM lihaoyi test$ 

Here we see that getClass.getClassLoader and Thread.currentThread.getContextClassLoader return different values. Moreover, Thread.currentThread.getContextClassLoader seems to refuse to load java/lang/String.class , and the other can.

Remarkably, when I run the jar file with an external tool like scalac / scala or java , both class loaders can load the class file as a resource

 lihaoyi test$ scalac Foo.scala lihaoyi test$ scala Main sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@1b28 cdfa class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@7229 724f class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader 

I would expect the SBT to behave similarly: so that Main.getClass.getClassLoader and Thread.currentThread().getContextClassLoader could load java/lang/String.class as a resource. What gives?

+5
source share
2 answers

Some tips provided by Jason Zaugg (retronym) sbt launcher notes .

sbt / launcher is a small Scala application that downloads an arbitrary Scala program (usually SBT), describes a configuration file and receives it through the resolution of the Ivy dependency.

Creates a child class loader containing Scala 2.10.6. The child from this contains the SBT itself and xsbti / interface-0.13.11.jar.

SBT needs to use custom classloader delegation to selectively hide classes when creating child classloaders for plugin code, for Scala compiler, or for user code.

Some more tips in sbt 0.13 sources:

 def makeLoader(classpath: Seq[File], instance: ScalaInstance, nativeTemp: File): ClassLoader = filterByClasspath(classpath, makeLoader(classpath, instance.loader, instance, nativeTemp)) def makeLoader(classpath: Seq[File], parent: ClassLoader, instance: ScalaInstance, nativeTemp: File): ClassLoader = toLoader(classpath, parent, createClasspathResources(classpath, instance), nativeTemp) 

Basically, sbt is the kitchen sink of a Java application that has arbitrary Scala versions and your code, as well as your test libraries along with the Java / OpenJDK Java library. To build a class path that makes sense without loading them over and over, it creates a hierarchy of class loaders, each of which is filtered by some criteria. (I think)

+3
source

It is noteworthy that one way to solve this problem is to install

 (fork in run) := true, (connectInput in run) := true, (outputStrategy in run) := Some(StdoutOutput), 

This seems to solve this problem, ( Thread.currentThread().getContextClassLoader.getResourceAsStream("java/lang/String.class") now works. Thread.currentThread().getContextClassLoader.getResourceAsStream("java/lang/String.class") ), but introduces other unrelated problems (the forked JVM takes a while to load, it loads cold and takes time to warm up ...)

+1
source

Source: https://habr.com/ru/post/1268314/


All Articles