Frage Wie wird der Klassenpfad für den Scala-Interpreter in einer verwalteten Umgebung eingerichtet?


Ich arbeite an einem Erweiterung für das Apache Wicket-Webframework, mit dem der Benutzer Code in mehreren Programmiersprachen zur Laufzeit vom Browser aus ausführen kann. Eine dieser Sprachen ist Scala, aber ich habe Probleme, wenn es als WAR-Datei gebündelt und in einem Container wie Tomcat bereitgestellt wird.

Wenn der Scala-Interpreter aufgerufen wird, verweigert er die Ausführung des Codes mit der folgenden Meldung:

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.

Nach dem Einrichten von usejavac in den Scala-Einstellungen funktionierte es in einer verwalteten Umgebung immer noch nicht. Das Problem scheint zu sein, dass der Scala-Interpreter die Scala-Bibliotheks-Jars auf dem Java-Klassenpfad nicht finden kann.

Ich habe im Internet gesucht Vorschlag, die die Verwendung von zwei Klassenpfadressourcen namens 'boot.class.path' und 'app.class.path' vorschlägt, die die erforderlichen Klassenpfaddeklarationen enthalten sollen. Ich habe das versucht und es schien zu funktionieren. Mein Problem mit dieser Lösung ist jedoch, dass meine Erweiterung in eine WAR-Datei gebündelt und in verschiedenen Umgebungen ausgeführt werden soll, so dass der Benutzer diese Ressourcen in Bezug auf die Umgebung, in der er ausgeführt wird, ändern muss. Es wäre auch eine Menge Arbeit, den Weg jedes einzelnen Jars in die Datei einzuschließen.

Vielleicht verstehe ich den Vorschlag nicht vollständig. Weiß jemand eine Lösung dafür?


6
2017-07-27 05:32


Ursprung


Antworten:


Es ist mir gelungen, scala 2.9.1 in einem Krieg mit Tomcat zu integrieren.

So habe ich es gemacht.

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

Nur für die Suchmaschinen. Dies waren meine Ausnahmen, bevor es funktionierte.

    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.

und

    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)

3
2018-02-29 13:57



Sie könnten versuchen, den Klassenpfad manuell zu erstellen und festzulegen:

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

und übergeben Sie diese Settings-Instanz an den Scala-Compiler.


2
2017-07-27 08:42



Ich habe auf 2.10.2 portiert und die Lösung von Peter hat nicht mehr funktioniert. Ich habe das Internet durchsucht und alle haben auf die Lösung hingewiesen, die Scott vorgeschlagen hat - aber es hat auch nicht für mich funktioniert. Ich benutze Maven, um alle benötigten Abhängigkeiten in den lib-Ordner zu kopieren, der die Scala-Bibliothek und den Compiler enthält. Maven setzt auch den (relativen) Klassenpfad zu all diesen Jars im Manifest.

Ich bin mir sicher, dass es eine elegantere Methode gibt, dies zu tun - besonders, wenn man sofort auf das richtige Manifest zugreift - und es gibt wahrscheinlich einige Fallstricke, aber es funktioniert für mich:

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

1
2017-08-14 09:20