开发者

How to set up classpath for the Scala interpreter in a managed environment?

I am working on an extension for the Apache Wicket web framework, which lets the user execute code in several programming languages at runtime from the browser. One of these languages is Scala, but I am having trouble when it is bundled as a WAR file and deployed to a container such as Tomcat.

When the Scala interpreter is invoked, it refuses to run the code with the following message:

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.

After setting up usejavacp on the Scala settings, it still didn't work in a managed environment. The problem seems to be that the Scala interpreter cannot find the Scala library jars on the Java class path.

Searching the web, I found a proposal, which proposes the use of two classpath resouces named 'boot.class.path' and 'app.class.path', which should include the needed class开发者_如何学Pythonpath declarations. I tried this and it seemed to work. My problem with this solution, though, is that my extension is meant to get bundled into a WAR file and to be run in different environments, so it would be necessary for the user to modifiy these resources with respect to the environment it runs in. Also it would be a lot of work to include every single jar's path into the file.

Maybe I don't understand fully the proposal. Does anybody know of a solution for this?


I have managed to embed scala 2.9.1 within a war running in tomcat.

This is how I did it.

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);

Just for the search engines. These were my exceptions before it worked.

    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.

and

    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)


You could try to build and set the classpath manually:

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

and pass this Settings instance to the Scala compiler.


I ported to 2.10.2 and the solution provided by Peter did not work any more. I serached the web and everyone pointed to the solution Scott suggested - but it did not work for me, either. I'm using maven to copy all needed dependencies to the lib folder, which includes the scala library and compiler. Maven also sets the (relative) classpath to all those Jars in the Manifest.

I'm sure there is a more elegant way of doing this - especially accessing the right Manifest right away -, and there are probably some pitfalls, but it works for me:

    //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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜