Open Source Projects:
Pricing
Chat
Contact
Back to top

GO: Call me maybe, Java!

August 14, 2015

When you need to pass boundaries of managed java environment, you find yourself playing (or fighting 🙂 with C/C++ code (headers, compiler flags, linker flags …). With version 1.5, Go is coming to save us, C/C++ is not your only option anymore…

One of the great features introduced in Go 1.5 is, being able to build a C compatible library. Using -buildmode={c-archive,c-shared} flag, you can now build a C archive go build -buildmode=c-archive or a C shared library go build -buildmode=c-shared. (For more info: Go Execution Modes)

Assume we want to create a native math library… And we need a method to multiply two long numbers.

<span class="kd">public</span> <span class="kd">static</span> <span class="kt">long</span> <span class="nf">multiply</span><span class="o">(</span><span class="kt">long</span> <span class="n">x</span><span class="o">,</span> <span class="kt">long</span> <span class="n">y</span><span class="o">)</span>

JNI with C/C++

To be able to implement a method in C, first we need to define a native java method.

<span class="kn">package</span> <span class="n">io</span><span class="o">.</span><span class="na">dogan</span><span class="o">.</span><span class="na">whiteboard</span><span class="o">.</span><span class="na">jni</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">JniMath</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kd">native</span> <span class="kt">long</span> <span class="nf">multiply</span><span class="o">(</span><span class="kt">long</span> <span class="n">x</span><span class="o">,</span> <span class="kt">long</span> <span class="n">y</span><span class="o">);</span>
<span class="o">}</span>

Then using javah tool, we’ll generate the C header file:

javah io.dogan.whiteboard.jni.JniMath

It will generate the header io_dogan_whiteboard_jni_JniMath.h:

<span class="cp">#include <jni.h></span>
<span class="cp">#ifndef _Included_io_dogan_whiteboard_jni_JniMath</span>
<span class="cp">#define _Included_io_dogan_whiteboard_jni_JniMath</span>
<span class="cp">#ifdef __cplusplus</span>
<span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
<span class="cp">#endif</span>

<span class="n">JNIEXPORT</span> <span class="n">jlong</span> <span class="n">JNICALL</span> <span class="n">Java_io_dogan_whiteboard_jni_JniMath_multiply</span>
  <span class="p">(</span><span class="n">JNIEnv</span> <span class="o">*</span><span class="p">,</span> <span class="n">jclass</span><span class="p">,</span> <span class="n">jlong</span><span class="p">,</span> <span class="n">jlong</span><span class="p">);</span>

<span class="cp">#ifdef __cplusplus</span>
<span class="p">}</span>
<span class="cp">#endif</span>
<span class="cp">#endif</span>

This is our contract between java JNI and native code. Implementation of io_dogan_whiteboard_jni_JniMath.c will be look like this:

<span class="cp">#include "io_dogan_whiteboard_jni_JniMath.h"</span>

<span class="n">JNIEXPORT</span> <span class="n">jlong</span> <span class="n">JNICALL</span> <span class="nf">Java_io_dogan_whiteboard_jni_JniMath_multiply</span>
  <span class="p">(</span><span class="n">JNIEnv</span> <span class="o">*</span> <span class="n">env</span><span class="p">,</span> <span class="n">jclass</span> <span class="n">clazz</span><span class="p">,</span> <span class="n">jlong</span> <span class="n">argX</span><span class="p">,</span> <span class="n">jlong</span> <span class="n">argY</span><span class="p">)</span> <span class="p">{</span>

  <span class="k">return</span> <span class="n">argX</span> <span class="o">*</span> <span class="n">argY</span><span class="p">;</span>
<span class="p">}</span>

And now, we can call our native multiplication method after building the shared library:

gcc -shared -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/darwin io_dogan_whiteboard_jni_NativeActions.c -o libmath.dylib
<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">JniMath</span><span class="o">.</span><span class="na">multiply</span><span class="o">(</span><span class="mi">12345</span><span class="o">,</span> <span class="mi">67890</span><span class="o">));</span>
<span class="c1">// output: 838102050</span>

This is old school JNI, let’s get to the topic…

JNI with Go

We’ll use the same JniMath class and the same contract between java and native code. So, our Go function will use the same signature as C function and we’ll export our Go function as a C function.

Assuming you already know how to export a C function in Go. If you don’t then just take a look at CGO. Basic idea is, it’s one by adding a special export comment over function: //export Java_io_dogan_whiteboard_jni_JniMath_multiply

Here is our Go implementation math.go:

<span class="c1">//math.go</span>

<span class="kn">package</span> <span class="nx">main</span>

<span class="c1">// #cgo CFLAGS: -I/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/include</span>
<span class="c1">// #cgo CFLAGS: -I/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/include/darwin</span>
<span class="c1">// #include <jni.h></span>
<span class="kn">import</span> <span class="s">"C"</span>

<span class="c1">//export Java_io_dogan_whiteboard_jni_JniMath_multiply</span>
<span class="kd">func</span> <span class="nx">Java_io_dogan_whiteboard_jni_JniMath_multiply</span><span class="p">(</span><span class="nx">env</span> <span class="o">*</span><span class="nx">C</span><span class="p">.</span><span class="nx">JNIEnv</span><span class="p">,</span> <span class="nx">clazz</span> <span class="nx">C</span><span class="p">.</span><span class="nx">jclass</span><span class="p">,</span> <span class="nx">x</span> <span class="nx">C</span><span class="p">.</span><span class="nx">jlong</span><span class="p">,</span> <span class="nx">y</span> <span class="nx">C</span><span class="p">.</span><span class="nx">jlong</span><span class="p">)</span> <span class="nx">C</span><span class="p">.</span><span class="nx">jlong</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">x</span> <span class="o">*</span> <span class="nx">y</span>
<span class="p">}</span>

<span class="c1">// main function is required, don't know why!</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{}</span> <span class="c1">// a dummy function</span>

Note that C import statement and #include and #cgo instructions over the import. Alternatively, you can set CFLAGS parameter as an environment variable.

Now, let’s build and run:

go build -buildmode=c-shared -o libmath.dylib math.go
<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">JniMath</span><span class="o">.</span><span class="na">multiply</span><span class="o">(</span><span class="mi">12345</span><span class="o">,</span> <span class="mi">67890</span><span class="o">));</span>
<span class="c1">// output: 838102050</span>

Done? No! Why do we need to import jni.h, use JNIEnv, jlong etc. in our parameter list and call our simple multiply method with a such strange and complex name: Java_io_dogan_whiteboard_jni_JniMath_multiply? Can it be simpler and easier?

Yes! We have a better option: JNR (jnr-ffi).

JNR with Go

Just forget about io_dogan_whiteboard_jni_JniMath.h header, Java_io_dogan_whiteboard_jni_JniMath_multiply function, #cgo, #include <jni.h> directives…

Let’s implement our multiplication as it’s supposed to be math.go:

<span class="c1">// math.go</span>

<span class="kn">package</span> <span class="nx">main</span>

<span class="c1">//export Multiply</span>
<span class="kd">func</span> <span class="nx">Multiply</span><span class="p">(</span><span class="nx">x</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">y</span> <span class="kt">int64</span><span class="p">)</span> <span class="kt">int64</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">x</span> <span class="o">*</span> <span class="nx">y</span>
<span class="p">}</span>

<span class="c1">// main function is required, don't know why!</span>
<span class="kd">func</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{}</span> <span class="c1">// a dummy function</span>

We also need an java interface matching the signature of Go function MathLib:

<span class="kn">package</span> <span class="n">io</span><span class="o">.</span><span class="na">dogan</span><span class="o">.</span><span class="na">whiteboard</span><span class="o">.</span><span class="na">jnr</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">MathLib</span> <span class="o">{</span>
    <span class="kt">long</span> <span class="nf">Multiply</span><span class="o">(</span><span class="kt">long</span> <span class="n">x</span><span class="o">,</span> <span class="kt">long</span> <span class="n">y</span><span class="o">);</span>
<span class="o">}</span>

We have a Go function, we have a java interface… Let’s bind them to each other…

Build go library:

go build -buildmode=c-shared -o libmath.dylib math.go

Load the library using JNR-FFI and call the method:

<span class="kn">package</span> <span class="n">io</span><span class="o">.</span><span class="na">dogan</span><span class="o">.</span><span class="na">whiteboard</span><span class="o">.</span><span class="na">jnr</span><span class="o">;</span>

<span class="kn">import</span> <span class="nn">jnr.ffi.LibraryLoader</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">JnrMath</span> <span class="o">{</span>

    <span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">MathLib</span> <span class="n">MATH_LIB</span><span class="o">;</span>

    <span class="kd">static</span> <span class="o">{</span>
        <span class="n">MATH_LIB</span> <span class="o">=</span> <span class="n">LibraryLoader</span><span class="o">.</span><span class="na">create</span><span class="o">(</span><span class="n">MathLib</span><span class="o">.</span><span class="na">class</span><span class="o">).</span><span class="na">load</span><span class="o">(</span><span class="s">"math"</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">MATH_LIB</span><span class="o">.</span><span class="na">Multiply</span><span class="o">(</span><span class="mi">12345</span><span class="o">,</span> <span class="mi">67890</span><span class="o">));</span>
        <span class="c1">// output: 838102050</span>
    <span class="o">}</span>
<span class="o">}</span>

PS1: Go 1.5 is not released yet by the time this post is written. To be able to test things here, you need to build/install Go 1.5 from source. For more info: Installing Go from source.

PS2: libmath.dylib should be placed in a library directory which is known by JVM; either in a system-wide lib directory or in a path denoted by LD_LIBRARY_PATH.

Caution: Because both Java and Go are garbage collected languages, watch out for possible reference tracking issues between two. For more info: Binding Go and Java

About the Author

Free Hazelcast Online Training Center

Whether you're interested in learning the basics of in-memory systems, or you're looking for advanced, real-world production examples and best practices, we've got you covered.