sbt.ClassLoaderLayeringStrategy.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of main_2.12 Show documentation
Show all versions of main_2.12 Show documentation
sbt is an interactive build tool
The newest version!
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
/**
* Represents a ClassLoader layering strategy. By providing an instance of [[ClassLoaderLayeringStrategy]],
* users can configure the strategy that they want to use in various sbt tasks, most importantly
* [[Keys.run]] and [[Keys.test]]. This setting is only relevant if fork := false in the task for
* which we obtain a ClassLoaderLayeringStrategy.
*
* ClassLoaders can be composed of multiple ClassLoaders
* to form a graph for loading a class. The different portions of the graph may be cached and
* reused to minimize both the memory taken up by ClassLoaders (and the classes that they load) and
* the startup time for tasks like test and run. For example, the scala library is large and takes
* a while just to load the classes in predef. The [[Keys.scalaInstance]] task provides access to
* a classloader that can load all of the java bootstrap classes and scala.*. Supposing that we want
* to run code in a jar containing scala code called "foo_2.12.jar" in the base directory and that
* we have a scala instance in scope and suppose further that "foo_2.12.jar" contains a main method
* in the class foo.Main, then we can invoke foo.Main.main like so
*
* {{{
* val fooJarFile = new File("foo_2.12.jar")
* val classLoader = new URLClassLoader(
* Array(fooJarFile.toURI.toURL), scalaInstance.loaderLibraryOnly)
* val main = classLoader.loadClass("foo.Main").getDeclaredMethod("main", classOf[Array[String]])
* main.invoke(null, Array.empty[String])
* }}}
*
* Now suppose that we have an alternative jar "foo_alt_2.12.jar" that also provides foo.Main, then
* we can run that main method:
*
* {{{
* val fooJarFile = new File("foo_alt_2.12.jar")
* val altClassLoader = new URLClassLoader(
* Array(fooAltJarFile.toURI.toURL), scalaInstance.loaderLibraryOnly)
* val altMain = classLoader.loadClass("foo.Main").getDeclaredMethod("main", classOf[Array[String]])
* altMain.invoke(null, Array.empty[String])
* }}}
*
* In the second invocation, the scala library will have already been loaded by the
* scalaInstance.loaderLibraryOnly ClassLoader. This can reduce the startup time by O(500ms) and
* prevents an accumulation of scala related Class objects. Note that these ClassLoaders should
* only be used at a code boundary such that their loaded classes do not leak outside of the
* defining scope. This is because the layered class loaders can create mutually incompatible
* classes. For example, in the example above, suppose that there is a class foo.Bar provided
* by both "foo_2.12.jar" and "foo_2.12.jar" and that both also provide a static method
* "foo.Foo$.bar" that returns an instance of foo.Bar, then the following code will not work:
*
* {{{
* Thread.currentThread.setContextClassLoader(altClassLoader)
* val bar: Object = classLoader.loadClass("foo.Foo$").getDeclaredMethod("bar").invoke(null)
* val barTyped: foo.Bar = bar.asInstanceOf[foo.Bar]
* // throws ClassCastException because the thread context class loader is altClassLoader, but
* // but bar was loaded by classLoader.
* }}}
*
* In general, this should only happen if the user explicitly overrides the thread context
* ClassLoader or uses reflection to manipulate classes loaded by different loaders.
*/
sealed trait ClassLoaderLayeringStrategy
/**
* Provides instances of [[ClassLoaderLayeringStrategy]] that can be used to define the ClassLoader used by
* [[Keys.run]], [[Keys.test]] or any other task that runs java code inside of the sbt jvm.
*/
object ClassLoaderLayeringStrategy {
/**
* Include all of the dependencies in the loader. The base loader will be the Application
* ClassLoader. All classes apart from system classes will be reloaded with each run.
*/
case object Flat extends ClassLoaderLayeringStrategy
/**
* Add a layer for the scala library class loader.
*/
sealed trait ScalaLibrary extends ClassLoaderLayeringStrategy
/**
* This should indicate that we use a two layer ClassLoader where the top layer is the scala
* instance and all of the dependencies and project class paths are included in the search path
* of the second layer.
*/
case object ScalaLibrary extends ScalaLibrary
/**
* Add a layer on top of the ScalaLibrary layer for all of the task jar dependencies.
*/
sealed trait AllLibraryJars extends ScalaLibrary
/**
* Add a layer on top of the ScalaLibrary layer for all of the jar dependencies.
*/
case object AllLibraryJars extends AllLibraryJars
}