Tobia
Tobia

Reputation: 9526

Java JNA Different implementation according to OS

I have to use Java JNA to link a C library. This library has a Windows implementation and a Linux one. These differ one from other for a single method because this method is implemented only by Windows version.

MyJnaInterface INSTANCE = (MyJnaInterface) Native.loadLibrary("MyLibrary",
                MyJnaInterface.class);

I would like to have just one version of my Java application, this may have a single interface with 2 implementation, one for windows os and one for linux os, obviously the linux implementation will have an empty method.

public interface MyJnaInterface
public class MyJnaWinImpl implements MyJnaInterface
public class MyJnaLinuxImpl implements MyJnaInterface

This works in windows, in linux OS at service startup JNA tries to find its native methods also in windows classes (also if this class is not used in runtime) and so it throws a UnsatifiedLinkError. How to solve this deadlock? I really cannot change the native library (it would be so simple...)

Upvotes: 2

Views: 738

Answers (3)

technomage
technomage

Reputation: 10069

I'm assuming you're using direct mapping, since interface mapping won't look up your function until you invoke it.

Write a base class with the base implementation, then a derived class that includes the additional mapping. Only load the derived class where you know the underlying function exists.

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 */
}

Upvotes: 0

Tobia
Tobia

Reputation: 9526

I solved using static{} block.

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());
        }

Upvotes: 0

user3494614
user3494614

Reputation: 603

I suggest to use compilation toolbox in your project to compile java code runtime depending upon value returned by System.getProperty("os.name") . If it returns windows then you can add source code for MyJnaWinImpl in one string and pass that to JavaSourceCompiler class. Once that is compiled load class and create instance. On linux JavaSourceCompiler will compile MyJnaLinuxImpl. Ensure that before creating this instance libraries are loaded.

Below is a small test code snippet.

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();
    }

}

Upvotes: 1

Related Questions