我正在为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的路径包含在文件中还需要做很多工作。
也许我不完全理解这个提议。有人知道解决方案吗?
答案 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