I'm reading about Java Native Access and so far i have been able to successfully call C functions from Java.
Is there a way to do the opposite? Googling didnt help much.
I'm reading about Java Native Access and so far i have been able to successfully call C functions from Java.
Is there a way to do the opposite? Googling didnt help much.
Off course you can! Let's create simple example.
let's create header file header.h. For callback we will use callbackTriger method. getDeviceRandomStatus and randNum is just helper methods to generate random data responses.
#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
typedef void(*NotificationListener)(char *, int);
void callbackTriger(const NotificationListener l);
void getDeviceRandomStatus(char *answer, int sizeOfChars);
int randNum( int min, int max);
#endif // HEADER_H_INCLUDED
header.c
#include<stdio.h>
#include "header.h"
#include <stdlib.h>
#include <time.h>
void callbackTriger(const NotificationListener l){
int size=randNum(1,20);
char answer[size];
getDeviceRandomStatus(answer, size);
(*l)(answer, sizeof(answer));
}
void getDeviceRandomStatus(char *answer, int sizeOfChars){
int i;
for(i=0; i<sizeOfChars; i++){
int i=randNum(0,255);
answer[i]=i+'0';
}
}
int randNum( int min, int max){
srand ( time(NULL) );
double scaled = (double)rand()/RAND_MAX;
int val=(max - min +1)*scaled + min;
return val;
}
main.c for testing library methods:
#include<stdio.h>
#include <limits.h>
#include <stdlib.h>
int main(void)
{
int sizeOfChars=randNum(1,10);
char answer[sizeOfChars];
getDeviceRandomStatus(answer, sizeOfChars);
int i;
for (i = 0; i < sizeof(answer); ++i){
printf("%d ", answer[i]);
}
return 0;
}
Now lets create Shared lib and test it:
cd <path>
gcc -c -Wall -Werror -fpic header.c
gcc -shared -o libHeader.so header.o
gcc main.c -o main -lHeader -L<path> -Wl,-rpath=/home/vq/Desktop
./main
Now we need JAVA classes! Let's go:
import java.util.Arrays;
import java.util.logging.Logger;
import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public class CallBack {
public static Logger log = Logger.getLogger(CallBack.class.getSimpleName());
public interface CLibrary extends Library {
public interface NotificationListener extends Callback {
void invoke(Pointer val, int lenth);
}
public static class NotificationListenerImpl implements NotificationListener {
@Override
public void invoke(Pointer val, int lenth) {
log.info("returned byte array, size: "+lenth);
log.info("java mehtod, callback: " + Arrays.toString(val.getByteArray(0, lenth)));
}
}
public void callbackTriger(NotificationListener callback);
}
static public void main(String argv[]) {
CLibrary clib = (CLibrary) Native.loadLibrary("<path>/libHeader.so", CLibrary.class);
// instantiate a callback wrapper instance
CLibrary.NotificationListenerImpl callbackImpl = new CLibrary.NotificationListenerImpl();
// pass the callback wrapper to the C library
clib.callbackTriger(callbackImpl);
}
}
Looks like you can start the JVM and call functions in Java from C, using the JNI library. Is this what you are after?
Here's a simple sample. The java code, called ssm/SSMFuse.java calls a C function called littleWorkC, in fuseSSM.c (libfuseSSM.so), which calls a java method called Times10J -- twice and returns the sum of the two calls to the java Times10J method (which just returns 10 times its int argument. (The 'fuse' stuff is because I'm working on a File system in User Space project on Linux)
Here is the output, indented in the code to show the nesting of java calling C calling java:
java main code, FuseLib = <libfuseSSM.so@134315239349360>
java main code, callbackImpl = ssm.SSMFuse$Times10J@2c13da15
java main code, calling littleWorkC in fuseLib, pass it times10J CB
C code libfuseSSM.so, in littleWorkC 0x7a28bce15fe0
java code, Times10J.invoke method, arg: 230
C code, after calling *pT10J callback, sum: 2300.
java code, Times10J.invoke method, arg: 230
C code, after calling *pT10J callback, sum: 4600.
java main code, after littleWorkC, sum = 4600
The first C code line shows that java called C. The deeply indented java code lines are showing that the number 230 was passed to Times10J in java which returns 2300. The second call to Times10J with 230 again caused 2300+2300, or the sum of 4600 to be returned to the java main code.
Here is the complete java code, called ssm/SSMFuse.java:
package ssm;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Callback;
public class SSMFuse {
public interface FuseLib extends Library {
// This interface describes the functions in the (libFuseSSM.so) C library
public int littleWorkC(Times10J callback);
} // end of FuseLib interface
// Define the method that C will call here in java
public static class Times10J implements Callback {
public int callback(int val) { // Returns 10 times the value given
prt(" java code, Times10J.invoke method, arg: " + val);
return val*10;
}
} // end of Times10J class
public static void main(String[] args) {
@SuppressWarnings("deprecation")
final FuseLib fuseLib = (FuseLib)Native.loadLibrary("libfuseSSM.so",
FuseLib.class);
prt(" java main code, FuseLib = "+fuseLib);
// Create a callback object
Times10J times10JCB = new Times10J();
prt(" java main code, callbackImpl = "+times10JCB);
// Call littleWorkC in C, pass the callback 'function pointer' to Times10J
prt(" java main code, calling littleWorkC in fuseLib, pass it times10J CB");
int sum = fuseLib.littleWorkC(times10JCB);
prt(" java main code, after littleWorkC, sum = "+sum+"\n\n");
} // end of main
public static void prt(String ln) { System.out.println(ln); }
} // end of class SSMFuse
Here is the relevant part of the C code in fuseSSM.c:
// Test of calling C from Java and Java from C, using ssm.fuseSSM.java
typedef int(*Times10J)(int);
int littleWorkC(const Times10J pT10J) {
printf(" C code libfuseSSM.so, in littleWorkC %p\n",
(void*)pT10J);
// Call the Times10J method in java, twice, return the sum
int sum = 0;
for (int ii=0; ii<2; ii++) {
sum += (*pT10J)(230); // Call 'callback' method in Java Times10J class
printf(" C code, after calling *pT10J callback, sum: %d.\n", sum);
}
return sum;
} // end of littleWorkC
Here is the make output in Linux to produce libfuseSSM.so which is copied to /usr/local/lib:
gcc -fPIC -D_FILE_OFFSET_BITS=64 -Wall -g -c fuseSSM.c -lfuse -o fuseSSM.o
gcc -shared fuseSSM.o -o libfuseSSM.so -lfuse
And how the java code is invoked:
java -cp /ssm/class:/ssm/lib/jna-5.12.1.jar ssm.SSMFuse
Note: The whole point of the interface FuseLib extends Library in java is to list the signatures of the functions in the C .so (or .dll)
The class Times10J implements Callback is the implementation of the callback routine, note that only one method/function can be in each Callback class, You can create multiple different Callback classes if you need to have C call several Java functions.
This doesn't work with JNA, use JNI instead http://en.wikipedia.org/wiki/Java_Native_Interface