ZaydH
ZaydH

Reputation: 679

Compile and Run Scala File within a Java Program

I am working on a Java program that takes a Scala program in a text file, compiles it (inside Java), then runs it (also inside Java). I could not figure out a clean way to implement this. To compile, I tried to use code as shown below:

import scala.collection.JavaConversions;
import scala.tools.nsc.Global;
import scala.tools.nsc.Settings;

Global g = new Global(new Settings());
Global.Run run = g.new Run();
List<String> fileNames = new ArrayList<String>(Arrays.asList("Hello.scala"));

run.compile(JavaConversions.asScalaBuffer(fileNames).toList());

However, I get an error (reduced for clarity):

error: error while loading Object, Missing dependency 'object scala in compiler mirror', required by C:\Program Files\Java\jdk1.7.0_79\jre\lib\rt.jar(java/lang/Object.class)

I did not understand what was causing this or how to fix. As a temporary workaround, I tried compiling the Scala code externally and calling that in Java via:

Runtime rt = Runtime.getRuntime();
Process pr = rt.exec("java -cp scala-library.jar;. Hello");

While I think the code runs, it is not interactive with the Java program as I would like. For example, when running the Scala program (inside Java) if the Scala program wanted the user to type a string into the console, the Java program should do the same essentially.

Any guidance is appreciated.

Upvotes: 2

Views: 1675

Answers (2)

Suki
Suki

Reputation: 11

Looks like twitter eval is no more supported. Following https://github.com/open-src-code/btlang/blob/main/README.md supports what you are looking for

Example scala function to be executed from java

() => {
  scala.io.Source.fromFile("src/test/resources/para.txt")
    .getLines
    .flatMap(_.split("\\W+"))
    .foldLeft(Map.empty[String, Int]){
      (count, word) => count + (word -> (count.getOrElse(word, 0) + 1))
    }
}

Java code to execute the above function

String lambdaFunction = getSource("LambdaWordCountWithMap.scala"); // Lambda source
scala.collection.immutable.HashMap map = lambdaRunner.executeLambda(lambdaFunction, scala.collection.immutable.HashMap.class);
Assert.assertTrue(map.size() == 55);

disclaimer : I'm one of the author of this library.

Upvotes: 1

0__
0__

Reputation: 67280

The easiest way is probably just to use a tiny wrapper library that takes care of things like class-paths etc. For example, there is a small library Twitter Util-Eval (source).

Here is an example:

package foo;

import com.twitter.util.Eval;

public class Test {
  public static void main(String[] args) {
    final Eval eval = new Eval();
    final int result = eval.apply("3 + 4", true);
    System.out.println("Result: " + result);
  }
}

If you want to bake your own compiler and runner wrapper, this involves a bit more, you will need to compile to virtual files and then load them into a class loader (see for example here and here).

Upvotes: 2

Related Questions