Plug custom native functions to Ballerina

Rajith Vitharana
Ballerina Swan Lake Tech Blog
3 min readJul 2, 2017

I’m going to explain how ballerina native functions work and how we can plug custom native functions to Ballerina.

Ballerina native functions have two parts,

  1. Ballerina file called “natives.bal”(file name is just convention) which has relevant method declarations.
  2. Required functions written in native language(java)

How ballerina works with native?

function declaration mentioned in “natives.bal” file should have word “native” as the prefix of the function, so that ballerina can identify there is a native implementation for that and bind them together in runtime. Ballerina will read the “natives.bal” file at compile time and binding with native implementation happens only at runtime. So in order to define a native function, you must define it in the “natives.bal” file first.

Implementation

So example function declaration would look like follows

package wso2.sample.math;native function nextInt (int bound) (int);

After that, what you have to do is provide the native implementation, Here native implementations can be either non blocking or blocking,

Non blocking means, function invocation will return the invoking thread immediately and return the results later using a callback. Where as in blocking mode, function invocation happens in the same thread and returns the values.

For non blocking native functions, you have to implement “org.ballerinalang.model.NativeCallableUnit” class and for blocking native functions you can extend “org.ballerinalang.bre.bvm.BlockingNativeCallableUnit” class

Following sample implementation for Random.nextInt(int bound) in java is using blocking implementation.

@BallerinaFunction(
packageName = "wso2.sample.math",
functionName = "nextInt",
args = {@Argument(name = "a", type = TypeKind.INT)},
returnType = {@ReturnType(type = TypeKind.INT)},
isPublic = true
)
public class NextInt implements BlockingNativeCallableUnit {
public void execute(Context ctx) {
int bound = (int) getIntArgument(ctx, 0);
int res = new java.util.Random().nextInt(bound);
ctx.setReturnValues(new BInteger(res));
}
}

As you can see in the above example, You have to use “BallerinaFunction” annotation to specify the details about the function, ex — package name, function name, param types etc. So what ballerina does is, it reads those annotations and then create a native element provider using those annotations. So at the end ballerina will have the matching function signature and a matching native function so that it can combine them and use in runtime.

To generate native element provider for given native implementations. You have to use “org.ballerinalang.codegen.BallerinaAnnotationProcessor” class with maven processor plugin.

Sample configuration will look like follows.

<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<version>2.2.4</version>
<configuration>
<processors>
<processor>org.ballerinalang.codegen.BallerinaAnnotationProcessor</processor>
</processors>
<options>
<nativeEntityProviderPackage>org.wso2.sample.generated.providers</nativeEntityProviderPackage>
<nativeEntityProviderClass>StandardNativeElementProvider</nativeEntityProviderClass>
</options>
</configuration>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
</plugin>

If I explain the bold color tags mentioned above,

“nativeEntityProviderPackage” property and “nativeEntityProviderClass” property is to be combined to generate the native element provider class, which will be generated by the annotation processor and will contain all the necessary native functions defined within.

Next step is to provide a package repository provider, this repository provider is the one which reads native bal files and load them at compile time.

For this, you have to do two steps,

  1. First you need to copy and paste below class into your source code.
import org.ballerinalang.annotation.JavaSPIService;
import org.ballerinalang.repository.PackageRepository;
import org.ballerinalang.repository.fs.ClasspathPackageRepository;
import org.ballerinalang.spi.ExtensionPackageRepositoryProvider;
@JavaSPIService("org.ballerinalang.spi.ExtensionPackageRepositoryProvider")
public class StandardExtensionPackageRepositoryProvider implements ExtensionPackageRepositoryProvider {
private static final String JAR_SYSTEM_LIB_LOCATION = "/META-INF/natives/";@Override
public PackageRepository loadRepository() {
return new ClasspathPackageRepository(this.getClass(), JAR_SYSTEM_LIB_LOCATION);
}
}

2. Then you need to copy relevant bal files to the generated jar library. For that, you can use maven resource plugin, configuration will looks like follows

<resources>
<!-- copy built-in ballerina sources to the jar -->
<resource>
<directory>${project.build.directory}/../src/main/ballerina</directory>
<targetPath>META-INF/natives</targetPath>
</resource>
</resources>

Note that, what we are doing is copying the native ballerina files to “META-INF/natives” folder inside the jar file and providing that location as “JAR_SYSTEM_LIB_LOCATION” in the above created repository provider class.

Both above package repository provider and native element provider classes will get registered to ballerina as java SPIs. As explained before, package repository provider is the one providing function declarations(which comes from “natives.bal” files), while native element provider provides necessary native implementations for those native functions. In runtime, Ballerina binds them together and executes.

You can find the source code for this in github repository — https://github.com/lankavitharana/ballerina-math/tree/0.95v

(branch latest contains a basic sample for this for the latest ballerina versions(0.95 versions) )

Hope this helps.

--

--