Question

How can I access *.so object and it's methods in Scala? Here is a Python example: https://github.com/soulseekah/py-libgfshare/blob/master/gfshare.py where ctypes library is used to interact with libgfshare.so. What tools do I need to use to achieve the same in Scala?

Was it helpful?

Solution 2

The previous answer is quite correct that JNI is the way to go but getting it all to work requires a little perseverance. An example of a multi-platform Scala interface to a real world native library can be found here. As a summary, the steps you need to take are detailed below.

First, define the Scala interface that you want to use to access your native library with. An example of a native interface declaration in Scala is:

package foo.bar

object NativeAPI {
  @native def test(data: Array[Byte]): Long
}

Compile this file using scalac and then use javah to output a suitable C/C++ header file for the JNI native stub. The javah header generator (part of Java SDK) needs to be invoked as

javah -classpath <path-to-scala-libs> foo.bar.NativeAPI$

Note that the $ is added to Scala objects by the Scala compiler. The functions generated in this header will be called when you call the API from the JVM. For this example, the javah generated C header's declaration for this function would look like this:

JNIEXPORT jlong JNICALL Java_foo_bar_NativeAPI_00024_test(JNIEnv *, jobject, jbyteArray);

At this point, you need to create a C file which then maps this function from your JVM api to the native library you intend to use. The resulting C file needs to be compiled to a shared library (.so in Linux) which references the native library you want to call. The C interface into the JVM is described here.

For this example, lets call this library libjni-native.so and assume it references a 3rd party library called libfoo.so.0. If both these libraries are available in the dynamic library search path of your OS, then you need to instruct the JVM to load the library and call the function as follows:

System.loadLibrary("libjni-native.so")
val data = new Array[Byte](100)
// Populate 'data'
NativeAPI.test(data)

On Linux and OSX machines, the dynamic linker will know that libfoo.so.0 (.dylib for OSX) is a dependency of libjni-native.so and will load it automatically. You can now call the test function from Scala. You should now be able to make a call to foo.bar.Native.test() and have the native function executed.

In the real world, you probably need to bundle the .so libraries into a JAR file for distribution. To do this, you can place the shared libraries in a suitable directory in the resources directory of your project. These libraries need to be copied from the JAR file to a temporary directory at run time and then loaded using System.load("/tmppath/libjni-native.so"). LibLoader from the example shows one way how this can be achieved.

OTHER TIPS

If you would like to interact with a native library which doesn't support JNI (Java Native Interface) (that is, not designed especially for interacting with Java VM), try JNA (Java Native Access). There's also Scala Native Access project on Google Code, which seems to provide more "scala-friendly" API, but it seems inactive (last commit was in 2010).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top