Call a User-Defined Java Method from Ballerina

Dulmina Renuke
Ballerina Swan Lake Tech Blog
4 min readNov 15, 2023

This article was written using Ballerina Swan Lake Update 8 (2201.8.0) and Java JDK 17.

Due to Java’s widespread adoption as a programming language, it owns a richer set of libraries compared to those available for Ballerina in Ballerina Central. To leverage the functionality of these Java libraries within Ballerina, Ballerina provides a mechanism to execute Java code and retrieve the output via external functions. Essentially, an external function in Ballerina is a function where its body is associated with a corresponding Java method.

Let’s take a simple scenario where Ballerina consumes some utility functions from a user-defined Java library.

Step 1: Create a Java Library

Let’s create a Java library called Utilities. This library will contain a package named com.utils, which contains two classes named IntUtils.java. and StringUtils.java.

└── src
└── com
└── utils
├── IntUtils.java
└── StringUtils.java

The IntUtils.java class contains a simple add function, which adds two numbers and returns the result.

package com.utils;

public class IntUtils {
public static int add(int a, int b) {
return a + b;
}
}

The StringUtils.java class contains a replace function, which replaces oldChar with newChar in the given source text.

package com.utils;

public class StringUtils {
public static String replace(String source, char oldChar, char newChar) {
return source.replace(oldChar, newChar);
}
}

Step 2: Create the Ballerina Project

Create a new Ballerina project by executing bal new FFI. This will create a new Ballerina project with the name FFI.

├── Ballerina.toml
├── Dependencies.toml
└── main.bal

Step 3: Add the Utilities Library as a Dependency

You can do this in two ways.

  1. Add as a dependency JAR file
  2. Add as a Maven dependency

We are going to follow the first way, therefore, let’s create the JAR file for the Utilities library.

Note: For instructions on creating a JAR file, refer to this short video.

Next, create a folder called libs/java and put the Utilities.jar there.

├── Ballerina.toml
├── Dependencies.toml
├── libs
│ └── java
│ └── Utilities.jar
└── main.bal

Now, you need to tell Ballerina to use this JAR as a dependency. To do that, let’s add the following to the Ballerina.toml file.

[[platform.java17.dependency]]
path = "./libs/java/Utilities.jar"

Note: You can place the JAR files anywhere in the project and provide the path from the source root.

Step 4: Implement the External Functions

4.1. Implement the External Function for the add Method

Let’s implement the Ballerina external function to invoke the add method in IntUtil.java.

function add(int a, int b) returns int = @java:Method {
'class: "com.utils.IntUtils"
} external;

Here, to mark that this function maps to a Java method, we use the @java:Method{} annotation with the external keyword. The ‘class field is a required field that specifies the class in which the method exists. This will now look for a static Java method named add with two int parameters in IntUtils.java.

Note: If the Java method name that we intend to call is different from the Ballerina method name, we can use the name field in the annotation to specify the Java method name.

We can now call the Ballerina function and print the result.

4.2. Implement the External Function for the replace Method

The first parameter of the Java replace function is String, which should be mapped to the handle type in Ballerina. The second and third parameters are of the Java primitive type char, which will be mapped to the Ballerina type int. Therefore, the Ballerina external function signature would be, replace(handle, int, int) returns handle.

The above program is written to replace the underscore with white spaces in a given text. We can create the handle value, which refers to the Java String value using the java:fromString() method. Next, pass the corresponding ASCII values to the underscore and white space, which are 95 and 32, respectively. Executing this program will print cats and dogs.

That’s it. Now, you have successfully called Java functions from the Ballerina side. However, when it comes to large libraries, it is not practical to write all of these manually. This is where the Ballerina Bindgen tool comes in handy. This will auto-generate all the necessary Ballerina classes and functions for you.

Also, take a look at the Ballerina FFI learn page, where you can find out how to call instance methods, how to deal with overloaded constructors and methods, map Java classes into Ballerina classes, handle Java exceptions, etc.

That’s all for now. Stay tuned for the next article!.

--

--