EnvironmentsMethods
Run your own Java model

OpenMOLE makes it simple to include your own Java code in a workflow. Since the Scala compiler can compile Java code, Java code can be directly fed to the ScalaTask as shown in the ScalaTask example. Additionally, a compiled Java program can be encapsulated in a ScalaTask as a jar file through the libraries parameter as below.

Let us consider the simple code Hello.java in a directory named hello (to respect Java's package structure):

package hello;

public class Hello {
  public static void run(int arg) {
    System.out.println("Hello from Java! " + arg);
  }
}

We compile the code and generate the JAR file as follows:
mkdir hello
mv Hello.java hello
cd hello
javac Hello.java
cd ..
jar cvf Hello.jar hello

To call this scala code we will use a ScalaTask which is able to call Java code. In order to do that you should first create a folder in the OpenMOLE we interface and then upload the Hello.jar file in this folder in OpenMOLE. In the same folder you may then write script (script.oms) which looks like that:
val proto1 = Val[Int]

val explo = ExplorationTask(proto1 in (0 until 10))

//Defines the task to perform the hello function
val javaTask = ScalaTask("hello.Hello.run(proto1)") set (
  libraries += workDirectory / "Hello.jar",
  inputs += proto1
)

explo -< javaTask

The output should look like that (the order in which the lines are printed might be different in your case):
Hello from Java! 0
Hello from Java! 1
Hello from Java! 2
Hello from Java! 3
Hello from Java! 4
Hello from Java! 5
Hello from Java! 6
Hello from Java! 7
Hello from Java! 8
Hello from Java! 9
Hello from Java! 10

In the general case a task is used to compute some output values depending on some input values. To illustrate that, let's consider another Java code:
package hello;

public class Hello {
  public static double[] run(double arg1, double arg2) {
    return double[]{arg1 * 10, arg2 * 10};
  }
}

You can compile it and package as "Hello.jar" in the same manner as the previous example. You can then use the task input variables when calling the Java function, and assign the function result to the task output variable like so:

val arg1 = Val[Double]
val arg2 = Val[Double]
val out1 = Val[Double]
val out2 = Val[Double]

val explo = ExplorationTask(
  (arg1 in (0.0 to 10.0 by 1.0)) x
  (arg2 in (0.0 until 10.0 by 1.0))
)

val javaTask = ScalaTask("Array(out1, out2) = hello.Hello.run(arg1, arg2)") set (
  libraries += workDirectory / "Hello.jar",
  inputs += (arg1, arg2),
  outputs += (arg1, arg2, out1, out2)
)

// save the result in a CSV file
val csvHook = AppendToCSVFileHook(workDirectory / "result.csv")

explo -< (javaTask hook csvHook)

This workflow will call the function hello with both arguments varying from 0 to 10 and save the results in a CSV file. Variables arg1 and arg2 are specified in the task output so that they are also written to the CSV file by the hook (see Hooks)

Working with files


When a task needs to access (read from or write to) external files, OpenMOLE needs to know it so it can pass them around to distant computing nodes.

Let's consider another "hello world" code Hello.java. This program reads the content of a file and writes it to another file.
package hello;

import java.io.*;

public class Hello {

  public static void run(int arg, File input, File output) throws IOException {
    //Read the input file
    String content = readFileAsString(input);
    PrintStream myStream = new PrintStream(new FileOutputStream(output));
    try {
      myStream.println(content + "  " + arg);
    } finally {
      myStream.close();
    }
  }

  private static String readFileAsString(File file) throws IOException {
    byte[] buffer = new byte[(int) file.length()];
    BufferedInputStream f = null;
    try {
      f = new BufferedInputStream(new FileInputStream(file));
      f.read(buffer);
    } finally {
      if (f != null) try { f.close(); } catch (IOException ignored) { }
    }
    return new String(buffer);
  }
}

You can compile it and package as "Hello.jar" in the same manner as the previous example. For the program to access external files, give them to the task as inputFile.

val proto1 = Val[Int]
val inputFile = Val[File]
val outputFile = Val[File]

val explo = ExplorationTask(proto1 in (0 to 10))

//Defines the scala task as a launcher of the hello executable
val javaTask =
ScalaTask("val outputFile = newFile(); hello.Hello.run(proto1, inputFile, outputFile)") set (
  libraries += workDirectory / "Hello.jar",
  inputs += (proto1, inputFile),
  outputs += (proto1, outputFile),
  inputFile := workDirectory / "input.txt"
)

  //Save the output file locally
  val copyHook =
    CopyFileHook(
      outputFile,
      workDirectory / "out-${proto1}.txt"
    )

  explo -< (javaTask hook copyHook)

For this example to work you should create a file named "input.txt" in the work directory of your project.

This tutorial works for simple Java programs. For more ambitious developments, you should consider embedding your code in an OpenMOLE plugin.