summaryrefslogtreecommitdiff
path: root/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala
blob: 02c229ba2bf8f2e57d535e52700596ffde65958d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package scala.scalajs.sbtplugin.env.phantomjs

import scala.scalajs.tools.io.IO

/** A special [[ClassLoader]] to load the Jetty 8 dependency of [[PhantomJSEnv]]
 *  in a private space.
 *
 *  It loads everything that belongs to [[JettyWebsocketManager]] itself (while
 *  retrieving the requested class file from its parent.
 *  For all other classes, it first tries to load them from [[jettyLoader]],
 *  which should only contain the Jetty 8 classpath.
 *  If this fails, it delegates to its parent.
 *
 *  The rationale is, that [[JettyWebsocketManager]] and its dependees can use
 *  the classes on the Jetty 8 classpath, while they remain hidden from the rest
 *  of the Java world. This allows to load another version of Jetty in the same
 *  JVM for the rest of the project.
 */
private[sbtplugin] class PhantomJettyClassLoader(jettyLoader: ClassLoader,
    parent: ClassLoader) extends ClassLoader(parent) {

  def this(loader: ClassLoader) =
    this(loader, ClassLoader.getSystemClassLoader())

  /** Classes needed to bridge private jetty classpath and public PhantomJS
   *  Basically everything defined in JettyWebsocketManager.
   */
  private val bridgeClasses = Set(
    "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager",
    "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$WSLogger",
    "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$ComWebSocketListener",
    "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$$anon$1",
    "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$$anon$2"
  )

  override protected def loadClass(name: String, resolve: Boolean): Class[_] = {
    if (bridgeClasses.contains(name)) {
      // Load bridgeClasses manually since they must be associated to this
      // class loader, rather than the parent class loader in order to find the
      // jetty classes

      // First check if we have loaded it already
      Option(findLoadedClass(name)) getOrElse {
        val wsManager =
          parent.getResourceAsStream(name.replace('.', '/') + ".class")

        if (wsManager == null) {
          throw new ClassNotFoundException(name)
        } else {
          val buf = IO.readInputStreamToByteArray(wsManager)
          defineClass(name, buf, 0, buf.length)
        }
      }
    } else {
      try {
        jettyLoader.loadClass(name)
      } catch {
        case _: ClassNotFoundException =>
          super.loadClass(name, resolve)
      }
    }
  }
}