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...)
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();
}
}
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());
}
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 */
}
Related
I want to wrap a C++ library (PCL) in Java code using JNI, but I am having inconsistent results. I have first created a PointXYZ class for testing and it looks like this:
package pcl;
public class PointXYZ extends NativeObject {
PointXYZ() { }
#Override
public native void alloc(); // creates pointer + handle on the native side
#Override
public native void dispose(); // sets handle to 0 and deletes pointer
public native float getX();
// ...
}
I have generated the C header for this class using javah, compiled everything using CMake, tested it using its getters and setters and everything works perfectly.
static {
System.setProperty("java.library.path", System.getProperty("user.dir") + "/lib");
System.loadLibrary("pcl_java_common");
}
#Test
void attributeAccessTest() {
PointXYZ p = new PointXYZ();
p.alloc();
p.setX(3);
assertEquals(p.getX(), 3);
p.dispose();
// all is good
}
Now I have done the exact same steps for a PointXYZRGB class which inherits from PointXYZ and when I try to test that it throws java.lang.UnsatisfiedLinkError. Here is the class:
package pcl;
public class PointXYZRGB extends PointXYZ {
public PointXYZRGB() { }
#Override
public native void alloc();
#Override
public native void dispose();
public native short getR();
// ...
}
I have checked the generated .dll using Dependency Walker and the PointXYZRGB methods are all present. Anyone knows what the problem could be?
UPDATE: Here are the .dll functions as requested in the comment:
The problem was that System.setProperty("java.library.path", System.getProperty("user.dir") + "/lib"); does not actually make Java look for .dll files in the given path. It essentially does nothing. Then why do the tests work for PointXYZ? This is was my mistake of having put an older .dll into the project root folder, so it was essentially looking for methods in that.
SQLUtils.java:
import org.openide.util.Lookup;
import java.util.ServiceLoader; // This doesn't work either
public class SQLUtils {
public static DBDriver getDriver(String prefix) {
for(DBDriver e : Lookup.getDefault().lookupAll(DBDriver.class)) {
System.out.println(e.getPrefix());
if(e.getPrefix().equalsIgnoreCase(prefix)) {
return e;
}
}
return null;
}
}
MySQLDriver.java:
public class MySQLDriver implements DBDriver {
#Override
public String getPrefix() {
return "mysql";
}
}
DBDriver.java:
import java.io.Serializable;
public interface DBDriver extends Serializable {
public String getPrefix();
}
Main.java:
public class Main {
public static void main(String[] args) {
DBDriver d = SQLUtils.getDriver("mysql");
}
}
This does nothing when running it, it cannot find any classes implementing.
What the program is trying to do is get the driver that is entered as a parameter for SQLUtils.getDriver(String prefix) (in Main.java).
For some reason I cannot get this to work.
I'm not familiar with OpenIDE Lookup mechanism, but I am familiar with the Java ServiceLoader mechanism.
You need to provide a file in the META-INF/services/ folder describing what classes implement specific interfaces. From the Java Docs describing the ServiceLoader class is this example:
If com.example.impl.StandardCodecs is an implementation of the
com.example.CodecSet service then its jar file also contains a file
named
META-INF/services/com.example.CodecSet
This file contains the single line:
com.example.impl.StandardCodecs # Standard codecs implementing com.example.CodecSet
What you are missing is a similar file that needs to be included on your classpath or within your JAR file.
You don't include you package names so I cannot provide a more direct example to help solve your problem.
I dropped the NetBeans API and switched to Reflections. I implemented Maven and ran it with IntelliJ. Works well for me.
I have a project where I defined a JNA wrapper to Windows kernel32 library, upon which I have made several helpers that are not critical for the project but increase the integration to the platform (namely: system debug logging with OutputDebugString + DebugView and Mailslot messaging features).
Here is my JNA defines:
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIFunctionMapper;
import com.sun.jna.win32.W32APITypeMapper;
public interface JnaKernel32 extends StdCallLibrary {
//StdCall is needed for lib kernel32
#SuppressWarnings("unchecked")
Map ASCII_OPTIONS = new HashMap(){
{
put(OPTION_TYPE_MAPPER, W32APITypeMapper.ASCII);
put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.ASCII);
}
};
#SuppressWarnings("unchecked")
Map UNICODE_OPTIONS = new HashMap(){
{
put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
}
};
Map DEFAULT_OPTIONS = Boolean.getBoolean("w32.ascii") ? ASCII_OPTIONS : UNICODE_OPTIONS;
JnaKernel32 INSTANCE = (JnaKernel32) Native.loadLibrary("kernel32", JnaKernel32.class, DEFAULT_OPTIONS);
//some system defines
//...
}
And the Mailslot definition:
public class Mailslot {
static JnaKernel32 kernel32 = JnaKernel32.INSTANCE;
boolean localMailslot = false;
int lastError = 0;
private int hMailslot = JnaKernel32.INVALID_HANDLE_VALUE;
//...
}
And in some places I have also
static JnaKernel32 kernel32 = JnaKernel32.INSTANCE; //to call OutputDebugString
//...
kernel32.OutputDebugString("some debug message");
My concern is that project could be used also on GNU/Linux or MacOS X but obviously the Native.loadLibrary fails at runtime if executed on e.g. OSX.
I am considering about
either porting the native feature with other JNA binding
or simply disable the existing Windows kernel32 binding when running on another platform as it is just convenient but not mandatory helpers.
How can I isolate the platform-specific features and calls that are made? I was thinking about moving the JNA part to a runtime loaded plugin perhaps?
The answer is really a general software development strategy.
Identify the API that your client code needs
In this case, it might be MailsSlot.sendMessage(int destID, String msg)
Abstract the implementation details behind that API
public interface MailSlot {
void sendMessage(int destId, String msg);
}
Provide one or more concrete implementations to meet the API contract
public class Win32MailSlot implements MailSlot {
public void sendMessage(int destId, String msg) {
// Do stuff here that's windows-specific
}
}
public class OSXMailSlot implements MailSlot {
public void sendMessage(int destId, String msg) {
// Do stuff here that's windows-specific
}
}
Choose the appropriate implementation at runtime:
MailSlot mslot = Platform.IS_WINDOWS ? new Win32MailSlot() : new OSXMailSlot();
After a few implementations, you may find some duplicated code, which you might then refactor into an abstract base class shared among the platform-specific implementations.
See the JNA platform FileUtils class for an example of such an strategy.
I am new to accessing DLLs from Java using JNA. I need to access methods from a class within a DLL(written in .net). Form this sample DLL below, I am trying to get AuditID and Server ID. I am ending with the following error while I am running my code. Any guidance really appreciated.
/// Error ///
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'GetEnrollcontext': The specified procedure could not be found.
//DLL File Code//
SampleDLL.ProfileEnroll enrollcontext = new SampleDLL.ProfileEnroll();
enrollcontext.Url =” url”;
enrollcontext.AuditIdType = SampleDLL.ProfileId;
enrollcontext.AuditId = “22222222 “;
enrollcontext.ServerId = “server1”;
/// Java Code ///
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import dllExtract.DLLExtractTest.SampleDLL.Enrollcontext;
public class SampleDLLExtract {
public interface SampleDLL extends Library {
SampleDLL INSTANCE = (SampleDLL) Native.loadLibrary("SampleDLL",
SampleDLL.class);
public static class Enrollcontext extends Structure {
public String auditId;
public String serverId;
}
void GetEnrollcontext(Enrollcontext ec); // void ();
}
public static void main(String[] args) {
SampleDLL sdll = SampleDLL.INSTANCE;
SampleDLL.Enrollcontext enrollContext = new SampleDLL.Enrollcontext();
sdll.GetEnrollcontext(enrollContext);
System.out.println(sdll.toString(sdll.GetEnrollcontext(enrollContext)));
}
}
in fact there is a solution for you to use C#, VB.NET or F# code via JNA in Java (and nothing else)! and it is also very easy to use:
https://www.nuget.org/packages/UnmanagedExports
with this package all you need to do is, add [RGiesecke.DllExport.DllExport] to your methods like that:
C# .dll Project:
[RGiesecke.DllExport.DllExport]
public static String yourFunction(String yourParameter)
{
return "CSharp String";
}
Java Project:
public interface jna extends Library {
jna INSTANCE = (jna) Native.loadLibrary("yourCSharpProject.dll", jna.class);
public String yourFunction(String yourParameter);
}
use it in the code:
System.out.println(jna.INSTANCE.yourFunction("nothingImportant"));
Viola!
As already mentioned it works very easy, but this solution has some limitations:
only available for simple datatypes as parameter & return values
no MethodOverloading available. yourFunction(String yourParameter) and yourFunction(String yourParameter, String yourSecondParameter) does not work! you have to name them differently
Use arrays as parameter or return values. (JNA offers StringArray, but I am not able to use them in C#) (maybe there is a solution, but I couldn't come up with one so far!)
if you export a method you can't call it internally in your C# code (simple to bypass that by the following:
.
[RGiesecke.DllExport.DllExport]
public static Boolean externalAvailable(String yourParameter)
{
return yourInternalFunction(yourParameter);
}
With C# it works great, with VB.NET and F# I have no experience.
hope this helps!
I am using dll in java using JNA, but i am getting below error
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'GetStatus': The specified procedure could not be found.
Not getting how to resolve this issue?
Please help.
Here is java code
import com.sun.jna.Library;
import com.sun.jna.Native;
/** Simple example of native library declaration and usage. */
public class First {
public interface TEST extends Library {
public String GetStatus();
}
public static void main(String[] args) {
TEST obj = (TEST ) Native.loadLibrary("TEST ", TEST .class);
System.out.println( obj.GetStatus());
}
}
This Nugget is super easy to use and works perfectly. https://www.nuget.org/packages/UnmanagedExports
You need Visual Studio 2012 (express).
Once installed, just add [RGiesecke.DllExport.DllExport] before any static function you want to export. That's it!
Example:
C#
[RGiesecke.DllExport.DllExport]
public static int YourFunction(string data)
{
/*Your code here*/
return 1;
}
Java
Add the import at the top:
import com.sun.jna.Native;
Add the interface in your class. Its your C# function name preceded by the letter "I":
public interface IYourFunction extends com.sun.jna.Library
{
public int YourFunction(String tStr);
};
Call your DLL where you need it in your class:
IYourFunction iYourFunction = (IYourFunction )Native.loadLibrary("full or relative path to DLL withouth the .dll extention", IYourFunction.class);//call JNA
System.out.println("Returned: " + IYourFunction.YourFunction("some parameter"));
EDIT:
If the DLL is 32bit, the JDK/JRE has to be 32bit as well. Add the following check to your code:
if(!System.getProperty("os.arch").equals("x86")) {
throw new Exception(".NET DLL " + "32bits JRE/JDK is required. Currently using " + System.getProperty("os.arch") + ".\r\nTry changing your PATH environement variable.");
}