I have a method of performing some operation using some external libraries.
To do this, I defined an interface IMethod
and an abstract class AMethod
:
public interface IMethod {
/**
* Get a level by a certain method.
*
* @return The level obtained using this method.
* @throws MethodUndefinedResultException If an error occurred in the method while obtaining the level.
*/
Level getLevel() throws MethodUndefinedResultException;
/**
* Get method type.
*
* @return Enum method value.
*/
Method getMethod();
}
abstract class ALibrary implements IMethod {
private final Logger log = LoggerWrapper.getLogger();
private final Library library;
public ALibrary(Library library) {
this.library = library;
}
@Override
public Level getLevel() throws MethodUndefinedResultException {
try {
linkWithLibrary();
String nativeCallResult = makeNativeCall();
return parseNativeCallResult(nativeCallResult);
} catch (LibraryResolveException | LibraryNativeCallException | LibraryParsingException e) {
log.error(e.getMessage());
throw new MethodUndefinedResultException(library.getMethod(), e);
}
}
@Override
public Method getMethod() {
return library.getMethod();
}
/**
* Link with library for the native method.
*
* @throws LibraryResolveException If library is not available.
*/
private void linkWithLibrary() throws LibraryResolveException {
try {
System.loadLibrary(library.getLibraryName());
} catch (Throwable e) {
log.error(e.getMessage());
throw new LibraryResolveException(library, e);
}
}
/**
* Make native call for the level.
*
* @return Result of the native call.
* @throws LibraryNativeCallException If the result does not contain a level.
*/
protected abstract String makeNativeCall() throws LibraryNativeCallException;
/**
* Parse library native call result to the level.
*
* @param nativeCallResult Result of the native call.
* @return Parsed level value.
* @throws LibraryParsingException If the result of the native method call could not be parsed.
*/
protected abstract Level parseNativeCallResult(String nativeCallResult)
throws LibraryParsingException;
}
After this I implemented test class TestMethod
that extends AMethod
and implements IMethod
:
public class TestLibrary extends ALibrary implements IMethod {
private static final Library LIBRARY = Library.Test;
private final Logger log = LoggerWrapper.getLogger();
public TestLibrary() {
super(LIBRARY);
}
/**
* A native method of the Test library that returns level.
*
* @return Level in numeric format.
*/
private native String getlvl();
@Override
protected String makeNativeCall() throws LibraryNativeCallException {
try {
return new TestLibrary().getlvl();
} catch (Throwable e) {
log.error(e.getMessage());
throw new LibraryNativeCallException(LIBRARY);
}
}
@Override
protected Level parseNativeCallResult(String nativeCallResult) throws LibraryParsingException {
log.info(nativeCallResult);
// TODO: Test with lib and remove stub
throw new LibraryParsingException(LIBRARY, null);
}
}
And use it as:
IMethod testMethod = new TestLibrary();
testMethod.getLevel();
But I got UnsatisfiedLinkError
from the getlvl()
in the makeNativeCall()
method of the TestMethod
class that cause LibraryNativeCallException
.
But when I'm doing something like:
public class TestMethod {
public native String getlvl();
static {
System.loadLibrary(Library.Test.getLibraryName());
}
public String getLevel() throws UnsatisfiedLinkError {
return new TestMethod().getlvl();
}
}
Everything works well. It seems to me that there is some kind of problem with an abstract class, but I can't figure out what it is. I would like to fix the new code and remove the old one.
Can you help please?
As I understood, if I change the java code, I need to change and recompile the C/C++ code.
In the testlib.cpp
I have this contract:
JNIEXPORT jstring JNICALL Java_lvl_method_library_impl_TestMethod_getlvl(JNIEnv * env, jobject jobj)
And when I use TestMethod
everything works well, but with TestLibrary
my code does not call getlvl()
.
On the advice of @aled, I recompiled the library by changing the name of the function in it to:
JNIEXPORT jstring JNICALL Java_lvl_method_library_impl_TestLibrary_getlvl(JNIEnv * env, jobject jobj)
And the new code started working.
Yaaaaas!