6

Problem
I'm unable to load and call methods in a compiled c class into a leiningen project. My basic approach is to load a Java class, JavaWrapper.java, that uses JNI to call some native methods in the native code, wrapper.o and then call the methods through this java wrapper class.
I imagine there are classLoader issues with loading a java class which loads the native code from a clojure project, but given that I can't seem to directly get clojure code to find the wrapper.o on the library path, I'm not sure how to handle this.

lein project file

(defproject lein-native-test "0.1.0-SNAPSHOT" ... :java-source-paths ["java-path"] :jvm-opts ["-Djava.library.path=.:./native:/absolute/path/to/native"] ;;not sure what format it wants ) 

clojure file with main method
I've tried it slightly modified with four approaches, all included in code below along with respective error in comments.

(ns lein-native-test.core (:import (com.test JavaWrapper))) (def -main [] ;;four things I've tried and their errors (clojure.lang.RT.load "/abs/path/to/wrapper.o") ;;could not find file /abs/path/wrapper.o_init.class or wrapper.o.clj (clojure.lang.RT.loadLibrary "wrapper.o") ;;UnsatisfiedLinkError no wrapper.o in java library path (JavaWrapper/load "/abs/path/to/wrapper.o") ;;UnsatisfiedLinkError com.test.JavaWrapper.setup() (assembly-load "/abs/path/to/wrapper.o") ;;unable to resolvesymbol: assembly-load ) 

Java code with native methods that uses JNI, JavaWrapper.java

public class JavaWrapper{ public native void setup(); public static void load(String lib){ System.load(lib);} } 

Before trying to get this to work with clojure and lein I did successfully load and use the native methods in wrapper.o via JavaWrapper and JNI.

Possibly related:
I'm also unable to load wrapper.o in JavaWrapper.java via

System.loadLibrary("wrapper.o"); 

I have to use

System.load("/absolute/path/to/wrapper.o"); 

Versions of tools
clojure version: 1.5.1
lein version: 2.3.4
jdk: 1.7
os: debian7

A better understanding of ClassLoaders or especially a working simple example would be very useful, thanks.

1 Answer 1

4

The problem was due to an naming error in my method in the C header and source files per the jni standard. The correct way to use jni with clojure is to create a Java wrapper class as I have done and load the dynamic library with the method clojure.lang.RT.loadLibrary
Since I've had trouble finding good examples for this I've made a demo on github

Errors
1) (clojure.lang.RT.load "/abs/path/to/wrapper.o") ;;could not find file /abs/path/wrapper.o_init.class or wrapper.o.clj
This load method is not meant to be used for native code, its expecting a java class or a clj file

2) (clojure.lang.RT.loadLibrary "wrapper.o") ;;UnsatisfiedLinkError no wrapper.o in java library path
Clojure is unable to find the library at link time, hence UnsatisfiedLinkError --- this is due to a naming error

  • first the library should be compiled as a dynamic shared library, i.e. for the gcc compiler use the -shared flag (I actually did but didn't name the output file with the normal .so extension)
  • java and thus clojure expect the native library to be named in a very specific way: libwrapper.so (or .jnilib for mac or .dll for windows but always with "lib" prefix)

3) (JavaWrapper/load "/abs/path/to/wrapper.o") ;;UnsatisfiedLinkError com.test.JavaWrapper.setup()
This time the error is on a method within JavaWrapper in the file or library, thus you know that at least it found the file. An UnsatisfiedLinkError that specifies a specific method in the Java class, like this one, should always be due to a naming error between what is declared a native method in the Java file and what is actually present in the c source or header file.
Note the namespace "com.test"
When declaring a jni method in c the method name must follow a specific format,
from http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html
"Dynamic linkers resolve entries based on their names. A native method name is concatenated from the following components:"

  • the prefix Java_
  • a mangled fully-qualified class name
  • an underscore (_) separator
  • a mangled method name
  • for overloaded native methods, two underscores (__) followed by the mangled argument signature

In this case the full c source method signature would be

void Java_com_test_setup(JNIEnv *env, jobject obj) 


4) (assembly-load "/abs/path/to/wrapper.o") ;;unable to resolvesymbol: assembly-load
This method too is not meant to load native code

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.