如何在托管环境中为Scala解释器设置类路径?

时间:2011-07-27 05:32:56

标签: scala classpath

我正在为Apache Wicket Web框架extension工作,该框架允许用户在运行时从浏览器执行多种编程语言的代码。其中一种语言是Scala,但是当它作为WAR文件捆绑并部署到Tomcat等容器时遇到了麻烦。

当调用Scala解释器时,它拒绝使用以下消息运行代码:

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

在Scala设置上设置usejavacp后,它仍然无法在托管环境中运行。问题似乎是Scala解释器无法在Java类路径上找到Scala库jar。

在网上搜索时,我找到了一个proposal,它建议使用两个名为'boot.class.path'和'app.class.path'的类路径资源,它们应该包含所需的类路径声明。我试过这个似乎工作。但是,我对这个解决方案的问题是,我的扩展是为了捆绑到WAR文件中并在不同的环境中运行,因此用户必须根据它运行的环境修改这些资源。此外,将每个jar的路径包含在文件中还需要做很多工作。

也许我不完全理解这个提议。有人知道解决方案吗?

3 个答案:

答案 0 :(得分:3)

我已经设法在一个在tomcat中运行的战争中嵌入了scala 2.9.1。

这就是我做的。

val code = """println("Hi");""";

val settings = new Settings
val compilerPath = java.lang.Class.forName("scala.tools.nsc.Interpreter").getProtectionDomain.getCodeSource.getLocation
val libPath = java.lang.Class.forName("scala.Some").getProtectionDomain.getCodeSource.getLocation

println("compilerPath=" + compilerPath);
println("settings.bootclasspath.value=" + settings.bootclasspath.value);

settings.bootclasspath.value = List(settings.bootclasspath.value, compilerPath, libPath) mkString java.io.File.pathSeparator    
settings.usejavacp.value = true
val interpreter = new IMain(settings)

interpreter.interpret(code);

仅适用于搜索引擎。在它起作用之前,这些是我的例外。

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

    Exception in thread "Thread-26" java.lang.Error: typeConstructor inapplicable for <none>
            at scala.tools.nsc.symtab.SymbolTable.abort(SymbolTable.scala:34)
            at scala.tools.nsc.symtab.Symbols$Symbol.typeConstructor(Symbols.scala:877)
            at scala.tools.nsc.symtab.Definitions$definitions$.scala$tools$nsc$symtab$Definitions$definitions$$booltype(Definitions.scala:157)
            at scala.tools.nsc.symtab.Definitions$definitions$.init(Definitions.scala:814)
            at scala.tools.nsc.Global$Run.<init>(Global.scala:697)
            at scala.tools.nsc.interpreter.IMain.scala$tools$nsc$interpreter$IMain$$_initialize(IMain.scala:114)
            at scala.tools.nsc.interpreter.IMain$$anonfun$initialize$1.apply$mcZ$sp(IMain.scala:127)
            at scala.tools.nsc.interpreter.IMain$$anonfun$initialize$2.apply(IMain.scala:126)
            at scala.tools.nsc.interpreter.IMain$$anonfun$initialize$2.apply(IMain.scala:126)
            at scala.concurrent.ThreadRunner$$anon$2$$anonfun$run$2.apply(ThreadRunner.scala:45)
            at scala.concurrent.ThreadRunner.scala$concurrent$ThreadRunner$$tryCatch(ThreadRunner.scala:31)
            at scala.concurrent.ThreadRunner$$anon$2.run(ThreadRunner.scala:45)
            at java.lang.Thread.run(Thread.java:662)

答案 1 :(得分:2)

您可以尝试手动构建和设置类路径:

val setting = new scala.tools.nsc.settings.MutableSettings(println(_))
settings.classpath.append("my/path")

并将此Settings实例传递给Scala编译器。

答案 2 :(得分:1)

我移植到2.10.2并且Peter提供的解决方案不再起作用了。我把网络分开了,每个人都指出斯科特建议的解决方案 - 但它也不适用于我。我正在使用maven将所有需要的依赖项复制到lib文件夹,其中包括scala库和编译器。 Maven还将(相对)类路径设置为Manifest中的所有Jars。

我确信有一种更优雅的方式 - 特别是立即访问正确的Manifest - 并且可能存在一些陷阱,但它对我有用:

    //Get Jarpath
    val jarfile = this.getClass.getProtectionDomain.getCodeSource.getLocation.getPath
    val jarpath = jarfile.take(jarfile.lastIndexOf("/") + 1) 

    //Get classpath from Manifest
    val resources = getClass.getClassLoader.getResources("META-INF/MANIFEST.MF");
    val cpath = Buffer[String]()

    //Get classpath from Manifest 
    while (resources.hasMoreElements()){
        val manifest = new Manifest(resources.nextElement().openStream());
        val attr = manifest.getMainAttributes.getValue("Class-Path")
        //Convert to absolut paths
        if (attr != null) {             
            cpath ++= attr.split(" ").map(p => {"file:" + {if(p(1) == '/') "" else jarpath} +  p })
        }
    }


    val settings = new Settings
    settings.bootclasspath.value = cpath.mkString(java.io.File.pathSeparator)
    settings.usejavacp.value = true