sbt run Main和play会出错

时间:2014-03-11 15:37:43

标签: scala playframework-2.2

我已经构建了一个litte对象,可以动态解释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也使用了修改过的 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
  }
}

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

2 个答案:

答案 0 :(得分: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. 

答案 1 :(得分:0)

这是因为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