April 22, 2020

Enhance your Java Spring application with R data science

In this post we look at GraalVM running a sample Spring application written in Java, that is using ggplot2, an R library, to plot the data.

GraalVM is a polyglot virtual machine capable of running various languages efficiently such as Java, Scala, JavaScript, Ruby, R and native languages compiled to llvm bitcode..

If you want to Gain In-depth Knowledge on Data Science, please go through this link Data Science Online Training

What is even more interesting is that GraalVM allows you to mix languages in a single program. There’s no performance hit for having Java or JavaScript, or Ruby, or R call each other or operate on the same data together. Let’s examine how to implement this interoperability between Java and R in a Spring boot application.

Why can’t we stick to just 1 language?

Every programming language has its own ecosystem and is great for certain tasks. R, for example, is great for data processing, data visualisation and machine learning. Having an option to choose the language freely makes choosing the best tool for the job an easier task.

For example, say we want an application with a simple visual output. Plotting data is very easy in R, so we chose R. ggplot2 is a very popular R package for plotting all kinds of data, which sounds exactly like the right tool for the job!

Now, we don’t expect that every application out there would require a data plotting ability, or should become a polyglot application immediately. But we strongly believe that every programming language has something unique to offer. And it is more efficient to re-use existing solutions than recreate them from scratch.

Java + R Spring application

The demo application which we use throughout this article is available from the graalvm/graalvm-demos repository. It is a Spring boot application which uses the R ggplot2library to render a plot of the CPU load on the current machine.

In order to run it, you will need a runtime capable of running both Java code (JVM bytecode) and R.

To get in-depth knowledge on Data Science, enroll for a live free demo on Data Science Course

Download either the community or enterprise edition of GraalVM (both will work). We’re using the enterprise edition here on macOS.

Unpack the downloaded binary and set GRAALVM_HOME to point to the directory in which it was unpacked.

Add $GRAALVM_HOME/bin to $PATH.

Also export JAVA_HOME=$GRAALVM_HOME to be make sure Maven will find the graal-sdk.jar file with the GraalVM polyglot API classes.

export GRAALVM_HOME=/path/to/GraalVMexport JAVA_HOME=$GRAALVM_HOMEexport PATH=$GRAALVM_HOME/bin:$PATH

Clone the demo app repository and navigate to the spring-r directory:

git clone https://github.com/graalvm/graalvm-demoscd spring-r

Since this Java application uses the ggplot2 R library, you need to install R language support:

gu -c install org.graalvm.R

Install R dependencies for the demo app. As mentioned, it uses ggplot2, a very popular package for visualizing data. The following command will download the sources for the ggplot2dependencies, and place them under the GraalVM distribution. (Note: it can take a couple of minutes.)

Rscript -e "install.packages(\"ggplot2\")"

Now we can finally run the demo application:

mvn spring-boot:run

Wait a couple of seconds for Spring to initialize and then open http://localhost:8080/load in your browser.

The page will load an svg image and the HTTP headers tell the browser to refresh the page every second.

After a couple of seconds, you should see a picture like this:

Java — R interop in GraalVM

Let’s look at the code that does this. The plotting function, written in R, resides in the plot.Rfile. It defines a global data variable and a function that appends a given number to data, and plots data as an svg image. The svg image is then returned as a String since it is the value produced by the svg.off() statement.

library(ggplot2)
data <<- numeric(100)
function(v) {
  svg()
  data <<- c(data[2:100], v)
  plot <- ggplot(data = data.frame(systemLoad = data, time = -99:0),
  aes(x=time, y=systemLoad, group=1)) +
  geom_line(color="orange") +
  expand_limits(x=0, y=0)
  print(plot)
  svg.off()
}

So far, so good. Now how can we use this function from Java? Since we’re dealing with a Spring app, we can define the components necessary for the GraalVM’s interop as Spring beans.

To use the GraalVM polyglot functionality, we need to define a context for the dynamic languages used from Java (it actually works in a similar way for all language interop in GraalVM).

@Bean
public Context getGraalVMContext() {
  return Context.newBuilder().allowAllAccess(true).build();
}

Next, we use this context to evaluate our R file. Since it only declares a single function, it is the result of the ctx.eval() call, so we can convert the polyglot value to an instance of the java.util.Function class.

@Value(value = "classpath:plot.R")
private Resource rSource;

@Bean
Function<Double, String> getPlotFunction(@Autowired Context ctx)
throws IOException {
  Source source = Source.newBuilder("R", rSource.getURL()).build();
  return ctx.eval(source).as(Function.class);
}

Note, that to enable our app the access to the R file, we place it into the resources directory which will be on the class path. Then we configure access to it by annotating the Resource field with @Value.

Note, if you’re going to modify this code, Spring has a class called Value, the annotation used above, and GraalVM polyglot API also has a Value class for representing values in the polyglot context. Be careful not to mix or confuse these.

Take your career to new heights of success with an Spring Boot Online Training

Now comes the best part, the R function defined in a separate R file is exposed as a normal Spring bean, so we can declare a field of the correct type and wire it to be provided for us by the Spring dependency injection mechanism.

@Autowiredprivate Function<Double, String> plotFunction;

When the app is being initialized, the plotFunction field will be set to the result of evaluating our R source file. The rest of the app is straighforward. We define a request mapping in a controller that serves the svg file generated by our plotFunction function to the browser:

@RequestMapping(value = "/load", produces = "image/svg+xml")
public ResponseEntity<String> load() {
  HttpHeaders responseHeaders = new HttpHeaders();
  responseHeaders.set("Refresh", "1");
  return new ResponseEntity<String>(
     plotFunction.apply(
       ManagementFactory.getOperatingSystemMXBean()
                        .getSystemLoadAverage()),
     responseHeaders,
     HttpStatus.OK);
}

There’s a couple of very interesting things happening in this code. During execution we cross the language barrier from Java to R and back to Java. Note that the plotFunction function takes a normal Java double as the argument, and it returns a normal Java String. The R code operates on these values automatically, without a need for conversion.

For example, we pass a DataHolder object to R:

public static class DataHolder {  public double value;  public DataHolder(double v) {    value = v;  }}

In the R code we access the DataHolder.value field using the normal R field access pattern:

function(dataHolder) {  svg()  data <<- c(data[2:100], dataHolder$value)  ...}

The interop works the other way too. For example it makes sense to log all the things through the Java logger for consistency.

For example, we have the following LogHolder class:

public class LogHolder {
private static final Log LOG =   
                   LogFactory.getLog(SpringRApplication.class);
public static void log(double value, Object... args) {
  LOG.info(String.format("Logging (value = %s): %s", value,        
                                        Arrays.toString(args)));
  }
}

The following short snippet allows R code to access the Java LogHolder type and call its static log method:

logHolder <- java.type("org.graalvm.demos.springr.LogHolder")
logHolder$log(dataHolder$value, data[90:100])

Impressive, isn’t it?

Conclusion

In this article we looked at enhancing a normal Java application with code in another language, R. Every programming language has its own ecosystem and is great for certain problems. We took a normal Spring Boot application and added a small R script to generate a plot of the CPU load. Binding it all together is fairly straightforward as GraalVM allows you to export members of dynamic languages as Spring beans.