How to use R scripts with Quarkus | Quarkify

Dmytro Chaban
Quarkify
Published in
4 min readApr 23, 2020

There’s a time when you need to call some R script from Quarkus. One option is to create microservice, but what if it’s something really small and you just want to call it in a single place without overwhelming project architecture. This is where GraalVM comes in. With GraalVm you can execute R scripts directly from Java without any conversion, microservices, or endpoints. We already have seen an example in Python, let’s see how to do it in R.

For better copy-paste experience please open this article on quarkify website

Setting up the environment

We’ll think that you already installed GraalVM, if not, follow this link. We’ll need to install GraalVM R interpreter:

gu install r

Beside’s this command, we’ll need to install some local dependencies. For Ubuntu 18.04 it’s:

sudo apt-get install install libgfortran-8-dev libgomp1 build-essential gfortran-8 libxml2 libc++-dev

For Ubuntu 19.10 it’s:

sudo apt-get install libgfortran-8-dev libgomp1 build-essential gfortran libxml2 libc++-dev

Running R code in Quarkus

Now you’re ready to use the R language. Since our Cohen’s D example in Python can be done with one line in R, we gonna use something more interesting in our case, let’s build logistic regression that will identify when we need to enable CPU cooling. In our case, we have the next simple dataset

Some entries omitted, please have a look R script to see full dataset

If you look at dataset, you can see that we “manually” enabled cooling when CPU was above 60 grad. Let’s build a service that will predict for us when we need to enable it. We gonna start with next R code:

cpu_grads <- c(1.0,20.0, 30.0, 40.0,35.0, 37.5, 60.0, 68, 100, 77, 85, 99)
# We enable cooler around above 60 grad manually
cpu_cooler_enable <- c(0,0,0,0,0,0,1,1,1,1,1,1)
y<-cpu_cooler_enable; x<-cpu_grads;
cpu_model <-glm(y~x, family = 'binomial')
new_data <- data.frame(x=c(40)) # c(40) is our input
predict(cpu_model, new_data, type="response")

This super-simple piece of R code will train logistic regression and after that predict if the CPU should be cooled at 40 grad.

Create Quarkus project

If you’re lazy to write code, clone our project from github Let’s create simple Quarkus app and connect it with our R code.

mvn io.quarkus:quarkus-maven-plugin:1.3.2.Final:create \
-DprojectGroupId=tech.donau.quarkify \
-DprojectArtifactId=quarkus-r-cpu-cooling \
-DclassName="tech.donau.quarkify.CpuCoolingResource" \
-Dpath="/cooling"

Once it’s created, put our R code into file at src/main/resources/cpu_cooling.R that was provided above.

Now, let’s modify our resource, open src/main/java/tech/donau/quarkify/CpuCoolingResource.java and put next code provided in a gist.

What we just did is that we created polyglot Context, and executed our cpu_cooling.R. Since our R code outputs prediction as the last output, we can use return Value. Value is a universal wrapper for any output, with which you can specify what type you want to get, since our R code outputs double, we called Value::asDouble. If the value is more then 0.8(or 80%) we say that we. Just imagine how many lines of code you saved for keeping logistic regression in R code. Let's start our service.

Start and test

./mvnw quarkus:dev

Now, let’s call our endpoint by using curl (or browser)

curl http://localhost:8080/cooling 
# No need to enable cooling

As you can see, in our R code we specified that we want to predict whether we want to enable cooling for 40 grads. Because our network trained on dataset that returns true close to 60 grads, 40 is out of range.

Executing R functions from Java

In previous example, we only used simple script without any input and output. Let’s slightly modify our cpu_cooling.R script so that we'll have a function that we can execute if needed. If you use cloned repo, execute git checkout feature/input

cpu_grads <- c(1.0,20.0, 30.0, 40.0,35.0, 37.5, 60.0, 68, 100, 77, 85, 99)
# We enable cooler around above 60 grad manually
cpu_cooler_enable <- c(0,0,0,0,0,0,1,1,1,1,1,1)
y<-cpu_cooler_enable; x<-cpu_grads;
cpu_model <- glm(y~x, family = 'binomial')
needs_cooling <- function (value) {
new_data <- data.frame(x=c(value))
return(predict(cpu_model, new_data, type="response"))
}

Now, we can use needs_cooling function from Java. Let's rewrite CpuCoolingResource.java a little bit.

In new version, we moved execution of our R code into startup event listener onStart. This will fix our long waiting time to execute first call. We also got R function directly from context and executed it. You can see how easy it is, even if we'll compare with Java's reflection API, it looks a lot better. Let's play around with our new code. If you stopped quarkus server, re-run it again with ./mvnw quarkus:dev.

curl http://localhost:8080/cooling?grads=33
# No need to enable cooling
curl http://localhost:8080/cooling?grads=64
# Enable cooling

As you can see, our trained model in R outputs correct results, and, for data provided, it starts to say that we need to enable cooling starting from 51 grads. You can also see how fast it is.

In conclusion

If for Python you have a limited amount of supported libraries, R can be used almost with no limits. This provides production-grade integration between Quarkus and R. Example that was provided in this lecture was specifically designed to show understandable example, you’re not limited to simple functions and computations.

Note, however, that our example is not ready for production, and there’s more stuff to do, which is a topic of the second article 😉

Have you ever integrated any two languages between each other without using rest or socket communication? How hard was it? I would love to exchange some experience on this topic 🙂

Originally published at https://quarkify.net on April 23, 2020.

--

--

Dmytro Chaban
Quarkify

Software Engineer, addicted to productivity and automatization