Java JNA Различные реализации в зависимости от ОС
Я должен использовать Java JNA, чтобы связать библиотеку C. Эта библиотека имеет реализацию для Windows и Linux. Они отличаются друг от друга для одного метода, потому что этот метод реализован только в версии Windows.
MyJnaInterface INSTANCE = (MyJnaInterface) Native.loadLibrary("MyLibrary",
MyJnaInterface.class);
Я хотел бы иметь только одну версию моего Java-приложения, это может иметь один интерфейс с двумя реализациями, один для Windows OS и один для Linux, очевидно, реализация Linux будет иметь пустой метод.
public interface MyJnaInterface
public class MyJnaWinImpl implements MyJnaInterface
public class MyJnaLinuxImpl implements MyJnaInterface
Это работает в Windows, в ОС Linux при запуске службы JNA пытается найти свои собственные методы также в классах Windows (также, если этот класс не используется во время выполнения) и поэтому выдает UnsatifiedLinkError. Как решить этот тупик? Я действительно не могу изменить нативную библиотеку (это было бы так просто...)
3 ответа
Я решил использовать статический блок {}.
public interface MyJnaInterface;
public interface MyJnaInterfaceWin implements MyJnaInterface; // this has the WinMethod method
...
private static MyJnaInterface INSTANCE;
static{
if(SystemUtils.IS_OS_LINUX){
INSTANCE=(MyJnaInterface) Native.loadLibrary("MyLibrary",MyJnaInterface.class);
}else{
INSTANCE=(MyJnaInterfaceWin) Native.loadLibrary("MyLibrary",MyJnaInterfaceWin.class);
}
}
...
public static void WinMethod(){
if(!SystemUtils.IS_OS_LINUX) ((MyJnaInterfaceWin)INSTANCE).WinMethod());
}
Я предлагаю использовать набор инструментов компиляции в вашем проекте для компиляции времени выполнения кода Java в зависимости от значения, возвращаемого System.getProperty("os.name") . Если он возвращает окна, вы можете добавить исходный код для MyJnaWinImpl в одну строку и передать его в класс JavaSourceCompiler. Как только это будет скомпилировано, загрузите класс и создайте экземпляр. В Linux JavaSourceCompiler будет компилировать MyJnaLinuxImpl. Убедитесь, что перед созданием этого экземпляра библиотеки загружены.
Ниже приведен небольшой фрагмент кода теста.
package test;
import org.abstractmeta.toolbox.compilation.compiler.*;
import org.abstractmeta.toolbox.compilation.compiler.impl.*;
import java.lang.ClassLoader;;
public class test {
public static void main(String[] args) throws ClassNotFoundException,InstantiationException,IllegalAccessException{
JavaSourceCompiler javaSourceCompiler = new JavaSourceCompilerImpl();
JavaSourceCompiler.CompilationUnit compilationUnit = javaSourceCompiler.createCompilationUnit();
String os = System.getProperty("os.name");
String SourceCode;
if ( os.contentEquals("Windows"))
{
SourceCode = "package com.test.foo;\n" +
"import MyJnaInterface.*;" +
"import MyJnaWinImpl " +
"public class Foo implements MyJnaWinImpl {\n" +
" public native void check();\n" +
" }";
}
else
{
SourceCode = "package com.test.foo;\n" +
"import MyJnaInterface.*;" +
"import MyJnaLinuxImpl " +
"public class Foo implements MyJnaLinuxImpl {\n" +
//" public native void check();\n" +
" }";
}
compilationUnit.addJavaSource("com.test.foo.Foo", SourceCode);
ClassLoader classLoader = javaSourceCompiler.compile(compilationUnit);
Class fooClass = classLoader.loadClass("com.test.foo.Foo");
Object foo = fooClass.newInstance();
}
}
Я предполагаю, что вы используете прямое сопоставление, так как сопоставление интерфейса не будет искать вашу функцию, пока вы не вызовете ее.
Напишите базовый класс с базовой реализацией, затем производный класс, который включает в себя дополнительное отображение. Загружайте производный класс только в том случае, если вы знаете, что базовая функция существует.
class BaseInterface {
public native void nativeMethod();
public void extendedMethod() { /* empty stub */ }
}
class ExtendedInterface extends BaseInterface {
public native void extendedMethod();
}
if (needExtendedInterface) {
lib = /* load extended library */
}
else {
lib = /* load base library */
}