I am trying some things out with JNI and by that I found the following problem:
If I want to use a native function in Java I load the needed lib, in which the needed function is stored, via
static{
System.loadLibrary("lib");
}
and use
native private static int calculate(byte[] numberArray);
to declare the native method in the java file. During the program itself I can use this function to calculate something with:
int result = calculate(array);
This works only if I compiled the shared object with the header-file created by javah so that each function is named on c side as:
static void Java_com_packagename_File_calculate(const void* array, void* result){
code[...]
}
If I delete the reference in the java code ("native [...] calculate[...]")to this c function; is there any possibility to access / execute the still existing c-code via java (of course without editing the exisiting file ;-)) for example via reflections or inheritance? Or is there something possible like:
public class NewClass{
public int nativeCheater(){
System.loadLibrary("lib");
native private static int Java_com_packagename_File_calculate;
}
}
It is important that I want to use a whole new class without any relations to the prior used package com.packagename.(File).
Thanks in advance :-)
No, but you can create a new class with same package and class name and access the same native method. The new class can declare this method public.
An alternative is to use dynamic binding via Jni_OnLoad() and RegisterNatives(). This way, your native implementations may bind to any Java class, or even more than one.
But if you have access neither to the Java class nor to the native source, you can always create your own native method, in your own class, and inside your C explicitly call the original:
static void Java_com_mypackagename_File_calculate(const void* array, void* result) {
Java_com_packagename_File_calculate(array, result);
}
Related
i am trying to integrate a legacy system via a provided c-library "libext.so".
To test JNA/JNI i want to call the "setProperty" function.
objdump libext.so -t | grep setProperty
0000000000104d50 g F .text 000000000000000e Java_ExtClass_setProperty
0000000000104be0 g F .text 000000000000016a Java_com_company_ExtClass_setProperty
this is my code, using java 8, jna 4.5.1 and/or native jni, both ways fail with an UnsatisfiedLinkError when calling the function - loading the library works without an Exception.
public class TestClass
{
static {
try {
System.load("/path/to/libext.so");
} catch (Throwable e) {
e.printStackTrace();
}
}
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary(("/path/to/libext.so"), CLibrary.class);
void setProperty(String key, String value);
}
public static native synchronized void setProperty(String key, String value);
public static void main(String[] args) {
// setProperty("a", "b");
CLibrary.INSTANCE.setProperty("a", "b");
}
}
what am i missing?
#Update:
I now moved both, the JNI class and the JNA INterface to the correct package and renamed the jna interface
package com.company;
public interface LibExtLibrary extends Library {
LibExtLibrary INSTANCE = (LibExtLibrary)
Native.loadLibrary(("/path/to/libext.so"),
LibExtLibrary.class);
void Java_com_company_ExtClass_setProperty(String key, String value);
void Java_ExtClass_setProperty(String key, String value);
void ExtClass_setProperty(String key, String value);
void setProperty(String key, String value);
}
calling the first two methods on INSTANCE gives an InvalidMemoryAccess, the second two an UnsatisfiedLinkError.
The JNI Method worked, as soon as the fully qualified classname was equal to the one defined in the native library
If the question is "why can't I call JNI methods with JNA?":
To be able to call external libraries from Java you have to use JNI. You declare some methods as native and javah will generate special functions for you that you can fill in with your code. Every function will have two additional arguments: an env pointer that is bound to the calling Java thread and the object or class that is the method receiver. If you pass a Java object as one of the other arguments you will get a jobject in your function.
If the implementation of the JNI functions would only consist of forwarding the calls to another external library you can also use JNA. JNA uses JNI to wrap libffi so you can call an existing external library directly. This way you don't have to create a wrapper library yourself. JNA will not create an env pointer or pass the method receiver as argument. And it will try to marshal all the other arguments into something the external library understands. That means no jobjects here.
Summary: JNI calls functions explicitly created for it and the arguments are Java specific. JNA uses JNI internally to call functions in non-Java external libraries.
I have been given a DLL and a JNI wrapper which are used to access a business card scanner.
So far I have only been able to call the API when all classes are in the default package. When I try to move classes to other packages I get an UnsatisfiedLinkError.
I have had no experience of JNI up until now and wonder if I need to rewrite the wrapper of if I can organise the project in a different way. What I cannot do is to use the default package as this would mean putting all my classes there.
The wrapper looks something like this:
public final class Wrapper {
private native int CRTK_Init(int[] lphRTK);
private int m_hRTK;
private int m_hRTKDB;
static
{
System.loadLibrary("crtk_jni");
}
public Wrapper() {
m_hRTKDB = 0;
int[] pRTK = new int[1];
CRTK_Init(pRTK); // UnsatisfiedLinkError here
m_hRTK = pRTK[0];
}
}
The thing with JNI is that the fully qualified class name of the class containing the native methods is tightly coupled to the method-signature of the native (C-)functions.
The C-signature must be something like
JNIEXPORT jobject JNICALL Java_packageName_className_methodName(JNIEnv * env, jclass parameter)
Renaming the class or moving it to another package would change the expected function-name and result in an UnsatisfiedLinkError.
So what can you do?
Unless you have access to the native sources to change the function-names all classes that come as a bundle together with the dll must remain in the default-package, all your own classes can go where you want to have them.
Getting a JNI-package that has its native methods in the default-package is considered poor style and does not bode well for the quality of the received software. And be prepared for further trouble than can come from using the default package, AFAIR e.g. tomcat had (has?) problems with those.
I'm aware that it is possible to use Java defined static methods in Lua, due to the section "Libraries of Java Functions" on http://luaj.org/luaj/README.html.
However I am struggling to find out how I can use the same for instance methods, I have a shortened example here:
private static class CallbackStore {
public void test(final String test) {
}
}
(I am aware that I can use a static method here as well, but it is not possible with the real life scenario)
I am using the following Lua code:
-- Always name this function "initCallbacks"
function initCallbacks(callbackStore)
callbackStore.test("test")
end
Which does not work as it is expecting userdata back, but I give it a string.
And I call the Lua code like this:
globals.load(new StringReader(codeTextArea.getText()), "interopTest").call();
CallbackStore callbackStore = new CallbackStore();
LuaValue initCallbacks = globals.get("initCallbacks");
initCallbacks.invoke(CoerceJavaToLua.coerce(callbackStore));
where the Lua code is returned by codeTextArea.getText()
Bottom line of my question is, how do I make my code running with test as an instance method?
When accessing member functions (in Lua objects in general, not just luaj) you have to provide the this argument manually as the first argument like so:
callbackStore.test(callbackStore,"test")
Or, you can use the shorthand notation for the same thing:
callbackStore:test("test")
I have an Ability.h file that is dependent on an Effect.h file.
I need to use javah to generate my header, but I'm unable to define an Effect dependency in my Ability.java class from which I'd like the c++ header to be generated.
Example:
public class Ability {
static {
System.loadLibrary("com_test_Effect");
System.loadLibrary("com_test_Ability");
}
public native Effect foo(Effect x);
}
This code generates an *.h file without the foo() function, as if it couldn't recognize it. It does generate a proper file if I swap the return type to int and don't include the com_test_Effect.
I do have both of the modules defined in the Android.mk file (com_test_Effect and com_test_Ability).
How to include an another c++ file directly in the Xyz.java class from which the *.h is generated by javah ?
Edit: The question can also be asked like this: Is there a way to pass C++-type arguments or return a C++-type value from a function that is an interface between C++ and Java ? (The interfacing medium being JNI.) For example, you can do so with basic types like int which then gets converted to jint and so on.
What about returning an Object:
private native Object fooNative(Object x);
Then convert it so that it has the same signature:
public Effect foo(Effect x) {
return (Effect)fooNative(x);
}
I have a C++ function:
struct Result {
//...
};
bool doSomething(Result[]);
If I use the following JNA binding, the function call works fine:
public class Result extends Structure {
//...
}
public interface CPPLibrary extends Library {
public static final CPPLibrary INSTANCE = (CPPLibrary)Native.loadLibrary("dllname");
boolean doSomething(Result[]);
}
But with direct call, I hit an IllegalArgumentException saying class [Lcom.usta.Result; is not a supported argument type (in method calcPV01 in class com.usta.CPPLibrary). My JNA code for the direct call-mapping:
public class CPPLibrary implements Library {
Native.register("dllname");
public static native boolean doSomething(Result[]);
}
I can see in com.sun.jna.Function#convertArgument() explicitly handles Structure[] but com.sun.jna.Native#getConversion(), which is used by direct call-mapping, does not handle Structure[].
The conversion is trivial, just call Structure.getPointer() on the first element of your structure array (assuming that you got the array from Structure.toArray in the first place).
You're actually better off with that when using direct mapping; when passing non-primitive, non-pointer types the JNI layer has to call back into the VM to derive the appropriate native data.
Feel free to file an issue for support of Structure[] arguments in direct mappings. That should be supported (JNA documentation notes that arrays of Pointer/String/WString/NativeMapped are not supported).
If I use a different method signature:
boolean doSomething(Pointer results);
it does work. But then I have to convert from Result[] to a Pointer my self.