【问题标题】:sbt runs IMain and play makes errorssbt 运行 IMain 并且 play 出错
【发布时间】:2014-05-29 11:23:56
【问题描述】:

我已经构建了一个小对象,它可以即时解释 scala 代码并从中获取值。

object Interpreter {
  import scala.tools.nsc._
  import scala.tools.nsc.interpreter._

  class Dummy

  val settings = new Settings
  settings.usejavacp.value = false
  settings.embeddedDefaults[Dummy]  // to make imain useable with sbt.

  val imain = new IMain(settings)

  def run(code: String, returnId: String) = {
    this.imain.beQuietDuring{
      this.imain.interpret(code)
    }
    val ret = this.imain.valueOfTerm(returnId)
    this.imain.reset()
    ret
  }
}

object Main {
  def main(args: Array[String]) {
    println(Interpreter.run("val x = 1", "x"))
  }
}

在纯sbt 环境中或由scala 解释器调用此代码可以正常工作!但是如果我在一个简单的play(2.2.2 版)应用程序中运行它,它会在val ret = this.imain.valueOfTerm(returnId) 处获得一个空指针。

play 也使用 modified sbt,因此它应该可以工作。 play 做了什么使这段代码不再起作用?任何想法如何让此代码在play 中工作?

注意

那是用过的build.sbt

name := "Test"

version := "1.0"

scalaVersion := "2.10.3"

libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value

或者我尝试了这个实现,但它也没有解决问题:

object Interpreter2 {
  import scala.tools.nsc._
  import scala.tools.nsc.interpreter._
  import play.api._
  import play.api.Play.current 

  val settings: Settings = {
    lazy val urls = java.lang.Thread.currentThread.getContextClassLoader match {
      case cl: java.net.URLClassLoader => cl.getURLs.toList
      case _ => sys.error("classloader is not a URLClassLoader")
    }

    lazy val classpath = urls map {_.toString}

    val tmp = new Settings()
    tmp.bootclasspath.value = classpath.distinct mkString java.io.File.pathSeparator
    tmp
  }

  val imain = new IMain(settings)

  def run(code: String, returnId: String) = {
    this.imain.beQuietDuring {
      this.imain.interpret(code)
    }
    val ret = this.imain.valueOfTerm(returnId)
    this.imain.reset()
    ret
  }
}

我发现用于实现第二个实现的有用链接:

【问题讨论】:

    标签: scala playframework-2.2


    【解决方案1】:

    我自己在这个问题上花了几个小时后,我想出了一个解决方案。它在 SBT 内部和外部都有效。它还有望在各种托管环境(如 OSGi)中工作:

    private def getClasspathUrls(classLoader: ClassLoader, acc: List[URL]): List[URL] = {
        classLoader match {
          case null                        => acc
          case cl: java.net.URLClassLoader => getClasspathUrls(classLoader.getParent, acc ++ cl.getURLs.toList)
          case c                           => LOGGER.error("classloader is not a URLClassLoader and will be skipped. ClassLoader type that was skipped is " + c.getClass)
                                              getClasspathUrls(classLoader.getParent, acc)
        }
    }
    val classpathUrls = getClasspathUrls(this.getClass.getClassLoader, List())
    val classpathElements = classpathUrls map {url => url.toURI.getPath}
    val classpath = classpathElements mkString java.io.File.pathSeparator
    val settings = new Settings
    settings.bootclasspath.value = classpath
    val imain = new IMain(settings)
    // use imain to interpret code. It should be able to access all your application classes as well as dependent libraries. 
    

    【讨论】:

      【解决方案2】:

      这是因为play 使用了sbt 的“fork in run”功能。这个特性会启动一个新的JVM,这会导致出现这个故障:

      [info] Failed to initialize compiler: object scala.runtime in compiler mirror not found.
      [info] ** Note that as of 2.8 scala does not assume use of the java classpath.
      [info] ** For the old behavior pass -usejavacp to scala, or if using a Settings
      [info] ** object programatically, settings.usejavacp.value = true.
      

      见:http://www.scala-sbt.org/release/docs/Detailed-Topics/Forking

      【讨论】:

        猜你喜欢
        • 2014-07-16
        • 2018-04-02
        • 2014-04-29
        • 2017-01-10
        • 2011-07-05
        • 2011-12-04
        • 2013-01-02
        • 2015-03-01
        • 2015-10-30
        相关资源
        最近更新 更多