Skip to content

Calling Java code from native functions – with JNI and Maven, too!

by Matthias Fraass on Februar 4th, 2011

few hours ago I explained in a blog posting how to set up a build system with Maven for JNI projects. This included an example on how to call native (C) code from Java.
Now I like to do the other way around: calling Java methods from native code.

I won’t explain the Maven project structure again, as this is basically the same as in the first article. The only difference is in the Java code, native code and the native pom.xml.

Here’s the structure:

But let’s start Top-down again.

The Java part

Our Java class is a simple Hello World class. It doesn’t contain any native declarations.

package net.tricoder.jnitest;

public class PureJava
{
public void hello()
{
System.out.println("Hello from Java");
}
}

The Java module’s pom.xml is roughly the same as before – I only added a “R” (for reverse) suffix to all identifieres

4.0.0

net.tricoder.jnitest
parentProjectR
1.0-SNAPSHOTnet.tricoder.jnitest
jniExampleJavaR
JNI reverse example - Java

maven-compiler-plugin

1.6
1.6

This one is called from the main pom.xml:

4.0.0
net.tricoder.jnitest
parentProjectR
1.0-SNAPSHOT
JNI reverse example parent project

pom
java
native
org.codehaus.mojo
native-maven-plugin

So we’re at the top again, so let’s go top-down to the native parts:

The C part

The “native” module is the same in our first example:

4.0.0

net.tricoder.jnitest
parentProjectR
1.0-SNAPSHOTnativeParentR

JNI reverse example - native parent

pom         win32
platform
win32

win32
hpux
platform
hpux

hpux

The native code as a bit more complex, as we have to

  1. create a JVM this time, then
  2. find our class and the method
  3. construct our class
  4. call the method

So this is the code:

#include
#include
#include

// stolen from http://www.inonit.com/cygwin/jni/invocationApi/c.html
// and http://coding.derkeiler.com/Archive/Java/comp.lang.java/2004-02/0570.html ;)

JNIEnv* create_vm() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];

args.version = JNI_VERSION_1_2;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=jniExampleJavaR.jar";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;

JNI_CreateJavaVM(&jvm, (void **)&env, &args);
return env;
}

void invoke_class(JNIEnv* env) {
jclass myClass;
// find class
myClass = (*env)->FindClass(env, "net/tricoder/jnitest/PureJava");
if (myClass == 0)
{
puts("class not found");
}
// find constructor
jmethodID ctID = (*env)->GetMethodID(env, myClass, "", "()V");
if (ctID == 0)
{
puts("constructor not found");
}
// find "hello" method
jmethodID methodID = (*env)->GetMethodID(env, myClass, "hello", "()V");
if (methodID == 0)
{
puts("method not found");
}

// call constructor
jobject jInstance = (*env)->NewObject(env, myClass, ctID);
// call method
(*env)->CallVoidMethod(env, jInstance, methodID);
}

int main(void) {
puts("Native impl starting...");

JNIEnv* env = create_vm();
puts("JVM created");

invoke_class( env );
puts("finish");

return EXIT_SUCCESS;
}

The build instructions for the native code are a bit simpler than in our first example, as we don’t have to call javah this time.

win32

4.0.0

net.tricoder.jnitest
nativeParentR
1.0-SNAPSHOTnet.tricoder.jnitest
jniExampleNative
0.0.1-SNAPSHOT
JNI reverse example native win32
http://maven.apache.org

exe

net.tricoder.jnitest
jniExampleJavaR
1.0-SNAPSHOT
jar
compile

org.codehaus.mojo
native-maven-plugin
true

win32

generic-classic
gcc

gcc

../src/main/native

jni_exampleR.c


-Lc:\Programme\Java\jdk1.6.0_07\lib\             -static -ljvm

HP-UX

4.0.0

net.tricoder.jnitest
nativeParentR
1.0-SNAPSHOTnet.tricoder.jnitest
jniExampleNativeR
0.0.1-SNAPSHOT
JNI reverse example native HP-UX
http://maven.apache.org

uexe

net.tricoder.jnitest
jniExampleJavaR
1.0-SNAPSHOT
jar
compile

maven-compiler-plugin

1.6
1.6
org.codehaus.mojo
native-maven-plugin
true

<!--  trigger javah -->
hp-ux

generic-classic
cc

cc

../src/main/native

jni_exampleR.c


-L/opt/java6/jre/lib/IA64N/server             -lcl -lpthread -ljvm

[cc]
<h2>Building</h2>
So let's build it!

[cc width="900"]
":/home/tricoder/jniRMavenExample&gt;"mvn clean install -P hpux
[INFO] Scanning for projects...
[INFO] Reactor build order:
[INFO]   JNI reverse example parent project
[INFO]   JNI reverse example - Java
[INFO]   JNI reverse example - native parent
[INFO]   JNI reverse example native HP-UX
[INFO] ------------------------------------------------------------------------
[INFO] Building JNI reverse example parent project
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] [site:attach-descriptor {execution: default-attach-descriptor}]
[INFO] [install:install {execution: default-install}]
[INFO] Installing /home/tricoder/jniRMavenExample/pom.xml to /opt/projekte/halden/tools/apache-maven-2.2.1/repository/net/tricoder/jnitest/parentProjectR/1.0-SNAPSHOT/parentProjectR-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Building JNI reverse example - Java
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] Deleting directory /home/tricoder/jniRMavenExample/java/target
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (8859_1 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/tricoder/jniRMavenExample/java/src/main/resources
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Compiling 1 source file to /home/tricoder/jniRMavenExample/java/target/classes
[INFO] [resources:testResources {execution: default-testResources}]
[WARNING] Using platform encoding (8859_1 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/tricoder/jniRMavenExample/java/src/test/resources
[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] No sources to compile
[INFO] [surefire:test {execution: default-test}]
[INFO] No tests to run.
[INFO] [jar:jar {execution: default-jar}]
[INFO] Building jar: /home/tricoder/jniRMavenExample/java/target/jniExampleJavaR-1.0-SNAPSHOT.jar
[INFO] [install:install {execution: default-install}]
[INFO] Installing /home/tricoder/jniRMavenExample/java/target/jniExampleJavaR-1.0-SNAPSHOT.jar to /opt/projekte/halden/tools/apache-maven-2.2.1/repository/net/tricoder/jnitest/jniExampleJavaR/1.0-SNAPSHOT/jniExampleJavaR-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] Building JNI reverse example - native parent
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] [site:attach-descriptor {execution: default-attach-descriptor}]
[INFO] [install:install {execution: default-install}]
[INFO] Installing /home/tricoder/jniRMavenExample/native/pom.xml to /opt/projekte/halden/tools/apache-maven-2.2.1/repository/net/tricoder/jnitest/nativeParentR/1.0-SNAPSHOT/nativeParentR-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Building JNI reverse example native HP-UX
[INFO]    task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] Deleting directory /home/tricoder/jniRMavenExample/native/hpux/target
[INFO] [native:initialize {execution: default-initialize}]
[INFO] [native:unzipinc {execution: default-unzipinc}]
[INFO] [native:compile {execution: default-compile}]
[INFO] /bin/sh -c cd /home/tricoder/jniRMavenExample/native/hpux &amp;&amp; cc -I/home/tricoder/jniRMavenExample/native/src/main/native -I/opt/java6/jre/../include -I/opt/java6/jre/../include/hp-ux -o/home/tricoder/jniRMavenExample/native/hpux/target/objs/jni_exampleR.o -c /home/tricoder/jniRMavenExample/native/src/main/native/jni_exampleR.c
[INFO] [native:link {execution: default-link}]
[INFO] /bin/sh -c cd /home/tricoder/jniRMavenExample/native/hpux &amp;&amp; cc -L/opt/java6/jre/lib/IA64N/server -o/home/tricoder/jniRMavenExample/native/hpux/target/jniExampleNativeR.uexe target/objs/jni_exampleR.o -lcl -lpthread -ljvm
[INFO] [resources:testResources {execution: default-testResources}]
[WARNING] Using platform encoding (8859_1 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/tricoder/jniRMavenExample/native/hpux/src/test/resources
[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] No sources to compile
[INFO] [surefire:test {execution: default-test}]
[INFO] No tests to run.
[INFO] [install:install {execution: default-install}]
[INFO] Installing /home/tricoder/jniRMavenExample/native/hpux/target/jniExampleNativeR.uexe to /opt/projekte/halden/tools/apache-maven-2.2.1/repository/net/tricoder/jnitest/jniExampleNativeR/0.0.1-SNAPSHOT/jniExampleNativeR-0.0.1-SNAPSHOT.uexe
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] JNI reverse example parent project .................... SUCCESS [1.863s]
[INFO] JNI reverse example - Java ............................ SUCCESS [1.928s]
[INFO] JNI reverse example - native parent ................... SUCCESS [0.011s]
[INFO] JNI reverse example native HP-UX ...................... SUCCESS [10.257s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16 seconds
[INFO] Finished at: Thu Feb 03 23:59:35 CET 2011
[INFO] Final Memory: 23M/291M
[INFO] ------------------------------------------------------------------------
":/home/tricoder/jniRMavenExample&gt;"

Executing

And finally – this time under HP-UX:

":/home/tricoder&gt;"cd jniRMavenExample/native/hpux/target/
":/home/tricoder/jniRMavenExample/native/hpux/target&gt;"ll
total 144
-rwxrwxrwx   1 tricoder     users        70828 Feb  3 23:59 jniExampleNativeR.uexe
drwxrwxrwx   2 tricoder     users           96 Feb  3 23:59 lib
drwxrwxrwx   2 tricoder     users           96 Feb  3 23:59 objs
":/home/tricoder/jniRMavenExample/native/hpux/target&gt;"cp lib/jniExampleJavaR.jar .
":/home/tricoder/jniRMavenExample/native/hpux/target&gt;"jniExampleNativeR.uexe
Native impl starting...
JVM created
Hello from Java
finish
":/home/tricoder/jniRMavenExample/native/hpux/target&gt;"

Wonderful, isn’t it? ;)

You can grab the code here. Have fun and let me know if that was useful for anyone!

From → Coding

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS