{{announcement.body}}
{{announcement.title}}

Ballerina Interop: All You Need to Know

DZone 's Guide to

Ballerina Interop: All You Need to Know

Everything you need to get started with Java integration in Ballerina.

· Web Dev Zone ·
Free Resource

ballerina-in-alley

The elegant language of integration

First of all, I'll give a brief introduction to Ballerina, as this is my first post about the language on DZone.

Ballerina is a brand new, statically-typed programming language that was developed specifically to target the integration space. There are some previous posts I have written in Medium about Ballerina.

Let's come to the topic at hand; very often people ask me about the interrelationship between Java and Ballerina and how to use existing Java libraries in Ballerina. I am not going to talk about the interrelationship in this post, as that itself is a separate topic. This post is about how to properly use interop functionality to interact with Java in Ballerina.

Earlier, we used a different mechanism called native binding to interact with Java. However, since the introduction of interop, we strongly recommend the usage of interop instead of the old mechanism. In fact, it is a bit hard to do the old style binding anyway now (even though we have both of the mechanisms still there and most of the standard libraries are still using the old style).

You may also like: The What, Why, and How of Unified Endpoint Management.

If anyone is familiar with the old style of binding, a major difference between the two is how we keep binding details. Wherein the old-style we kept them at the Java side as an annotation and in interop, those details are kept on the Ballerina side as annotations.

First, I'll explain the concept. Say you need to invoke a Java method inside Ballerina, what you have to do is define a ballerina method that matches the Java signature and specifies the fully qualified Java class that has the Java method in the annotation.

So, let's dig into the howto with a simple example.

Here, we are going to invoke a Java method from the Ballerina-side as an interop function.

package org.wso2.ballerina.math;

public class AddInt {

    public static long addInt(long a, long b) {
        return a + b;
    }
}


So, first of all, you'll have to write above Java code and get that compiled into a jar. As you can see, the above example is a simple static Java method that accepts two long parameters. (I am using long here because Ballerina's int is a long on the Java side, which makes the sample a simple one.)

The next step is to create a Ballerina function definition and provide the necessary linking information.

Below is the example Ballerina function definition

public function addInt(int a, int b) returns int = @java:Method {
    class:"org/wso2/ballerina/math/AddInt"
} external;


That's simply it; you can use addInit as a normal function on the Ballerina-side. Still, as I have said earlier, for the Ballerina module to build properly, you'll have to provide the jar file, which contains the above AddInit Java class. That, you can specify in the Ballerina.toml file itself. Below is how it's done.

[platform]
target = "java8"

    [[platform.libraries]]
    artifactId = "ballerina-math"
    version = "@project.version@"
    path = "../native-math/target/ballerina-math-1.0.0.jar"
    groupId = "org.wso2.ballerinalang"
    modules = ["math"]


You can find the complete example in the GitHub repo ballerina-math. (Check the tag relevant to the ballerina version.)

The concept is pretty simple. Even though we are invoking a Java method, there should always be a Ballerina-side definition, so from the Ballerina perspective, it's very easy to do all the type checking and validations. The only other validation we are doing with interop is validating the Ballerina definition against the actual Java implementation.

When it comes to invoking Java, there comes the problem of how to map Ballerina types to Java types vice versa. And also what to do about actual Java types that cannot be represented in the Ballerina side. For that, we have introduced a new type called handle. Handle is an opaque type as far as Ballerina is concerned, so you can keep any kind of Java value under it.

Other than that, we tried to map all the possible types with Java because with handles we cannot properly do type checking. So for example, int in ballerina is a long value in java.

Okay, now, since we have the basics down, let's dig into a bit of a complex example.

Java Method Overloads

For overloads, we can use annotations (java:Method annotation) to provide more details. Since Ballerina doesn't allow overloading, you'll have to define separate Ballerina functions with distinct names; however, using annotations, you can specify the actual Java method name you need to invoke. (Basically, you can use annotations to override the default behavior.)

Java Constructors

For the constructor, you can use java:Constructor annotation. As you may have guessed, you can override parameter mapping in the annotation as well. And you can keep the created object reference in ballerina side as a handle then when invoking an instance method, you will have to pass that handle as the first argument of the method invocation. Okay, let's take an example for more clarity

package org.wso2.ballerina.math;

public class Substract {
    private long initialVal;

    public Substract(long initial) {
    this.initialVal = initial;
    }

    public long substractInt(long a) {
    return initialVal - a;
    }
}


Below is the Ballerina-side constructor mapping:

public function newSubstract(int initial) returns handle = @java:Constructor {
    class:"org/wso2/ballerina/math/Substract"
} external;


As you can see, we have used the java:Constructor annotation here, and we are returning a handle type value, which is the object instance. Here, the name of the Ballerina method doesn't matter (no need to match it with the Java constructor's name), we use parameters to distinguish between overloaded constructors.

Below is the Ballerina method which matches the instance method.

public function substract(handle receiver, int b) returns int = @java:Method{
    class:"org/wso2/ballerina/math/Substract",
    name:"substractInt"
} external;


Note that I have used the name field to specify the actual Java method name (when the ballerina method name is different from the actual Java method name).

Constructors and Instance Methods

Below is sample code that creates an instance of Java's Substract class and invoke its substractInt instance method.

    // Creating instance
    handle sub = newSubstract(10);

    // Invoking a instance method
    int subRes = substract(sub, 5);
    io:println(subRes);


I have touched most topics related to Java interop. However, there are more complex scenarios than this, like how to handle errors in interop, how to do async invocations, how to access fields, how to use defaultable parameters and list goes on :) there are so many cool features in Ballerina :)

Yet I'm going to conclude the post here as it is already a bit long. I will try to either extend this post or create a new one with the rest of the details. (Maybe I have to update the post heading "..Part of what you need to know" instead of the current heading :)).


Further Reading

Topics:
ballerina ,programing language ,interoperability ,java ,tutorial ,web dev

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}