JavaCPP, UnsatisfiedLinkError when native library is archived in JAR - jar

I'm trying to call Haskell code from Java, using JavaCPP to help create the necessary JNI binding, as already discussed in this question.
This is how I'm using it:
<rootdir>
/javacpp.jar
/build (destination of libraris)
/src (contains Haskell code)
/com/example/HSCode.java (Java class to load and use native lib)
Content of HScode.java:
package com.example;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
#Platform(include={"<HsFFI.h>","HScode_stub.h"})
public class HScode {
static { Loader.load(); }
public static native void hs_init(int[] argc, #Cast("char***") #ByPtrPtr PointerPointer argv);
public static native String code_hs(String text);
public static void main(String[] args) throws FileNotFoundException {
String s = new Scanner(new File("test.txt")).useDelimiter("\\Z").next();
hs_init(null, null);
String s1 = code_hs(s);
System.out.println(s1);
}
}
Compilation:
cd <rootdir>
ghc --make -isrc -dynamic -shared -fPIC src/HScode.hs \
-o build/libHScode.so -lHSrts-ghc7.8.4 -optl-Wl,-rpath,.
javac -cp javacpp.jar com/example/HScode.java
java -jar javacpp.jar -d build \
-Dplatform.compiler=ghc -Dplatform.includepath="src:com/example" \
-Dplatform.compiler.output="-optl-Wl,-rpath,. -optc-O3 -Wall build/libHScode.so -dynamic -fPIC -shared -lstdc++ -lHSrts-ghc7.8.4 -o " com.example.HScode
Following this approach, I can create a libHScode.so and a libjniHScode.so using javacpp, which runs fine with:
$ java -cp javacpp.jar:. com.example.HScode
Jar
Now, the following step is that I want to package everything in a jar, and be able to use this jar's com.example.HScode from a larger java project.
JavaCPP's page mentions:
[...] Moreover, at runtime, the Loader.load() method automatically
loads the native libraries from Java resources, which were placed in
the right directory by the building process. They can even be archived
in a JAR file, it changes nothing. Users simply do not need to figure
out how to make the system load the files.
So I thought this should work.
However, if I make a jar HScode.jar out of the content of the build folder above, so that my jar contains both libjniHScode.so and libHScode.so, and run it with:
$ java -cp javacpp.jar:HScode.jar:. com.example.HScode
then it cannot find my native code (exception edited for anonymization):
Exception in thread "main" java.lang.UnsatisfiedLinkError: no jniHScode in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1865)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at org.bytedeco.javacpp.Loader.loadLibrary(Loader.java:597)
at org.bytedeco.javacpp.Loader.load(Loader.java:438)
at org.bytedeco.javacpp.Loader.load(Loader.java:381)
at com.example.HScode.<clinit>(HScode.java:13)
Caused by: java.lang.UnsatisfiedLinkError: /compilation-path/linux-x86_64/libjniHScode.so: HScode.so: cannot open shared object file: No such file or directory
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1937)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1822)
at java.lang.Runtime.load0(Runtime.java:809)
at java.lang.System.load(System.java:1086)
at org.bytedeco.javacpp.Loader.loadLibrary(Loader.java:580)
What am I missing? Does anyone know whether JavaCPP can actually find the native code when it's archived in a jar?

Building for the native libraries by calling javacpp -jar javacpp.jar com.example.HScode outputs them in com/example/linux-x86_64/ automatically and the Loader loads them from there. So when building the native libraries by some other means, they still need to be moved to com/example/linux-x86_64/, whether inside a JAR file or outside as normal files, if we want the Loader to find them.

Related

Use WebAssembly module compiled with Emscripten in Next JS

I am trying to build a Next JS project with an imported WebAssembly module compiled using Emscripten.
The problem seems to be related to the WebPack loader being unable to import the .wasm module via the generated .js.
I would greatly appreciate some help.
My C++ file hello.cpp:
#include <math.h>
extern "C" {
int int_sqrt(int x) {
return sqrt(x);
}
}
I compile using:
em++ hello.cpp -o "../v2/lib/wasm/test.js" -s MODULARIZE -s WASM=1 -s EXPORT_NAME="SZU" -s ENVIRONMENT="web" -s EXPORTED_FUNCTIONS=_int_sqrt -s EXPORTED_RUNTIME_METHODS=ccall,cwrap
I try to use the module in one of the components like so:
import SZU from "../../../../../../wasm/test";
var int_sqrt = SZU().cwrap("int_sqrt", 'number', ['number'])
When I run npx next build the build fails with:
Error: not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)
I expect to be able to build the project using npx next build then start the server with npx next start and be able to interact with functions exported in the .wasm/.js module files generated by Emscripten.
With the help of #lab I realized my approach was wrong. This is the way I made my WASM module work:
This is my hello.cpp file. Important notice the extern "C" {} block:
#include <math.h>
extern "C" {
int int_sqrt(int x) {
return sqrt(x);
}
}
I compile using em++ the destination is the public folder in next. We need to serve the .wasm and .js statically like #lab pointed out:
em++ hello.cpp -o "../v2/public/wasm/test.js" -s MODULARIZE -s WASM=1 -s EXPORT_NAME="SZU" -s ENVIRONMENT="web" -s EXPORTED_FUNCTIONS=_int_sqrt -s EXPORTED_RUNTIME_METHODS=ccall,cwrap
I load the Emscripten generated js file using the next/script tag like so:
<Script src="/wasm/test.js" strategy='beforeInteractive'/>
In the Component I want to use my WASM module I load it inside the next/useEffect hook, wrap it with Emscripten cwrap and store the function in a reference object using next/useRef like so:
useEffect(() => {
if(typeof (window as any).SZU === 'function' && WASM.current === null){
console.log("loading WASM!");
(window as any).SZU()
.then((wasm: WASM) => {
console.log("got WASM!");
WASM.current = wasm;
int_sqrt.current = WASM.current.cwrap("int_sqrt", "number", ["number"]);
});
}
}, []);
I can call the function later like so:
console.log(`Square root of 16 is ${int_sqrt.current(16)}`);
I hope this helps someone who had the same problem.
The case for wasm is that it will compile into 2 files: a js file and a wasm file. The wasm file cannot be bundled into your app. You must serve it as a static asset (since it's basically binary code), and configure your path to align with your desired import.
Also you might want to ensure it work first. Try writing a nodejs script that import it and give it a test run. OR you can try import it into a nextjs API route, and invoke it via fetch from client-side and see if your compiled wasm work.

WebConsoleListener IllegalAccessError in JavaFX 12

I recently downloaded the latest JavaFX SDK 12 and I wish to intercept Console Messages in my JavaFX WebView.
So, I have this
WebConsoleListener.setDefaultListener((webView, message, lineNumber, sourceId) -> {
//////// I am listening for a specific console message here in my
///webview
});
but I keep getting
Caused by: java.lang.IllegalAccessError: class rumbler.launcher.ApplicationLoader (in unnamed module #0x5c4c6905) cannot access class com.sun.javafx.webkit.WebConsoleListener (in module javafx.web) because module javafx.web does not export com.sun.javafx.webkit to unnamed module #0x5c4c6905
Here is my build.gradle file
javafx {
version = "12.0.1"
modules = ['javafx.base', 'javafx.controls', 'javafx.web']
}
Here are my VM OPTIONS
--module-path "path_to_\javafx-sdk-11.0.2\lib" --add-modules javafx.controls,javafx.fxml,javafx.web,javafx.base
.Am I missing something?
You are using private API, which is not advised.
Anyway, the error message is quite clear:
module javafx.web does not export com.sun.javafx.webkit to unnamed module #0x5c4c6905
Whenever you want to access some non-exposed package from your project (either modular on non-modular), you need to use --add-exports:
The command line option --add-exports $module/$package=$readingmodule exports $package of $module to $readingmodule. Code in $readingmodule can hence access all public types in $package but other modules can not. [source].
So in this case, the solution is straight forward:
--add-exports javafx.web/com.sun.javafx.webkit=ALL-UNNAMED \
--module-path "path_to_\javafx-sdk-11.0.2\lib" \
--add-modules javafx.web,javafx.fxml

how to enable Visor Command Line In Apache Ignite?

I have started Apache Ignite server via Maven Dependency trough eclipse,can anyone tell me how to monitor cache through visor command? How to enable it when setup Apache Ignite via Maven?
I think the most easy way is to download binary distributive and lunch Visor command line from "\bin" folder. Note, you need to download release that match to that you are using in your Maven based application.
The second way is to use ignite-visor-console module from Maven
And start Visor command line via: org.apache.ignite.visor.commands.VisorConsole object (it extends App). Note, Visor command line is written on Scala.
Sample code:
import org.apache.ignite.visor.commands.VisorConsole;
public class Test {
static public void main(String args[]) {
VisorConsole.main(args);
}
}
Also see Visor command line documentation.
And also give a try for Web Console, as Dmitriy suggested.

Deploy QT plugin with its own DLL depences

I have a QT application app.exe and a QT plugin plugin.dll. My plugin.dll depends on many other dynamic libraries (e.g. lib1.dll, lib2.dll and so on). To distribute my project I have this folder structure (ignoring QT libraries):
app.exe
plugins\
plugin.dll
lib1.dll
lib2.dll
lib3.dll
The problem is that there are too many dependences on libX.dll and I want to hide them in a plugin folder, e.g.:
app.exe
plugin\
plugin.dll
lib1.dll
lib2.dll
lib3.dll
But this way libraries libX.dll are "unseen" to my plugin, so that it cannot be loaded. Is there any way to solve this problem?
I am using this code to import libX.dll in plugin.dll's pro-file:
LIBS += -Lpath -l lib1 -l lib2 -l lib3
One of the ways of solving this problem is:
Link all libraries dynamically (at runtime)
Add extra location to search for the libraries
These changes should be done in plugin.dll code:
/* Declare a pointer to import function */
typedef void (*FUNCTION)();
FUNCTION f;
/* Make system search the DLLs in my plugin folder */
// Variable "app" contains directory of the application, not the plugin
QDir app = QDir(qApp->applicationDirPath());
// Combine path
QString plugin_path = app.filePath("plugins/");
// Adding full path for DLL search
SetDllDirectory(plugin_path.toStdWString().c_str());
/* Linking the library */
QLibrary mylib("mylib.dll");
f = (FUNCTION ) mylib.resolve("function");
if (f != NULL)
f(); // You got the function from DLL
else
return; // DLL could not be loaded
This solution has disadvanges:
It is not platform independent (I think you can avoid using SetDllDirectory in UNIX-like systems but I am not sure)
If you import a lot of functions you will have a lot of pointers
Does any one know pure Qt solution?

How to run standalone TestNG project from jar/bat/

I have a TestNG project. Don't have any main class, currently it is running like "Run As TestNG".
I want to export it as runnable jar or jar so that any one can just hit a command from command line and test cases start running.
Could any one help me out in this? or suggest any other way to deliver the code in runnable form...
I am not using ant or maven.
Thanks
I seem to have found the solution after a bit of googling. This works fine in Eclipse (Juno).
Say, you have a TestNG file named 'Tests.java'. As you rightly pointed out, there won't be a class with main method.
So, we have to create a new Java class file under the same package. Let us name it 'MainOne.java'. This will have a class with main method.
Here is the code you need:
import com.beust.testng.TestNG;
public class MainOne {
public static void main(String[] args) {
TestNG testng = new TestNG();
Class[] classes = new Class[]{Tests.class};
testng.setTestClasses(classes);
testng.run();
}
Run the 'MainOne.java' as a Java application. Then right click on the package -> Export -> Runnable Jar [Choose 'MainOne' as Launch Configuration] -> Finish.
My current understanding is that, in order to benefit from the parallel niftiness of TestNG, one should use the static main method in org.testng's jar file when running the Java class from the command line rather than from inside Eclipse IDE.
The issue then becomes classpath, which defines how java finds all the JAR files. I found http://javarevisited.blogspot.com/2012/10/5-ways-to-add-multiple-jar-to-classpath-java.html to be most useful because it has the * wildcard mentioned --- VERY helpful when you need to reference all the jar files required for Selenum + TestNG + custom test suites.
This is my current Windows BAT file, and it works. ADV.jar contains my custom class but no main method.
setlocal
set r=d:\Apps\Selenium\
cd /d %~dp0
java -classpath %r%Downloaded\*;%r%MyCompany\ADV.jar; org.testng.TestNG .\testng-customsuite-adv.xml
pause
All the JAR files that I downloaded from public places went into my d:\Apps\Selenium\Downloaded folder. I put my custom ADV.jar file in d:\Apps\Selenium\MyCompany to keep it separate.
I created my ADV.jar file from Eclipse using Export Jar file and ignored warnings about a missing main method.
Aside: while this https://stackoverflow.com/a/16879386/424855 was very intriguing, I could not figure out how to make that work.
Here is the better way to do it.
You can just create a main method which will have list of all test classes to be executed as follows:
public static void main(String[] args) {
TestListenerAdapter tla = new TestListenerAdapter();
TestNG testng = new TestNG();
testng.setTestClasses(new Class[] { test_start.class });
testng.addListener(tla);
testng.run();
}
Here is the reference URL from the official testng website.
Run the MainOne.java as a Java application. Then right click on the package -> Export -> Runnable Jar [Choose MainOne as Launch Configuration] -> Finish.

Resources