Java : Reuse a native library already loaded? - java

Disclaimer Not native English speaker, feel free to edit if needed.
I'm having a similar issue that the one explained here :
java.lang.UnsatisfiedLinkError: Native Library XXX.so already loaded in another classloader
I'm trying to follow the answer of user2543253. But I really lacks of knowledge in Java and the context is a bit different.
Links
.dll already loaded in another classloader? Seems also related to this question.
https://github.com/PatternConsulting/opencv/issues/7 Similar.
https://cycling74.com/articles/mxj-class-loading Explains the class loader behavior of MXJ
Context
Edit : Not sure if that context is really important, it seems to be the same problem described in link 1.
I want to use OpenCV inside an Application called Max/MSP.
To give an idea, it looks like this :
Max/MSP allows user to assemble Patch by cabling some objects together that are called externals, most of them are coded in C but you can also create externals in Java. To do so you need to instantiate them through an object called "mxj". For example, if my Java class is called TestOpenCV, I will create a box and put "mxj TestOpenCV" inside.
OpenCV seems correctly implemented, for exemple, I can instantiate a Mat object and post its content to Max console.
Problems appears when I change the Java code of the mxj object. To update my object, I delete it and recreate it again. Then, the same issue that explained here appears...
Max console return this error message :
java.lang.UnsatisfiedLinkError: Native Library
C:\Windows\System32\opencv_java300.dll already loaded in another
classloader at java.lang.ClassLoader.loadLibrary1(Unknown Source) at
java.lang.ClassLoader.loadLibrary0(Unknown Source) at
java.lang.ClassLoader.loadLibrary(Unknown Source) at
java.lang.Runtime.loadLibrary0(Unknown Source) at
java.lang.System.loadLibrary(Unknown Source) at
OpenCVClassLoad.loadNativeLibrary(OpenCVClassLoad.java:5) at
TestOpenCV.(TestOpenCV.java:22) (mxj) unable to alloc instance
of TestOpenCV
What I tried
I tried to implement the answer of user2543253. He advices to create a tiny classes that import the native library and export it as a JAR. So I created a new Eclipse project added a source file to it
import org.opencv.core.Core;
public class OpenCVClassLoad {
public static void loadNativeLibrary() {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
}
I added the openCV JAR to that project and exported it as a JAR.
Then I changed my code according to what user2543253 explained (there is more code,I kept the essential) :
import com.cycling74.max.*;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
public class TestOpenCV extends MaxObject {
static {
// System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
OpenCVClassLoad.loadNativeLibrary();
}
public TestOpenCV(Atom[] args)
{
// ...
}
public void notifyDeleted()
{
// ...
}
public void bang() {
// Executed when I trig the little bang button you can see
Mat m = new Mat(5, 9, CvType.CV_8UC4, new Scalar(0));
post("OpenCV Mat: " + m);
Mat mr1 = m.row(1);
mr1.setTo(new Scalar(1));
Mat mc5 = m.col(3);
mc5.setTo(new Scalar(5));
post("OpenCV Mat data:\n" + m.dump());
}
}
Of course, but that's a bit weird, in order to build correctly that project I kept the JAR from OpenCV in the build path :
As you can see, I also added the tiny class in the project build path.
After all of theses modifications, the mxj object stille loads correctly the first time and the bang() method still works but the problem still there. In fact it doesn't change anything from the past situation : If I modify the Java code, delete the object in Max and create a new one, error appears...
Question
There is a lot of SO questions addressing the same type of prob but context is always different and its hard to figure out what to do, especially with my basic knowledge of Java.
A workaround should be to simply reuse that library already loaded, no ? but how to achieve this ? Because if I check the library has already being loaded, I do it using a Try / Catch, if I do nothing else. The externals acts like if the library had never been loaded...
How to reuse that native library ? (Of course, any alternative solution to this is welcome)

Just remove the second OpenCVClassLoad.loadNativeLibrary(); in your bang() method. In a plain Java application the code in a static block is only executed once.
Alternatively, you can specify the native library location in Eclipse instead of loading the library through Java source code.

Related

How to load video in OpenCV (Java)

I'm trying to load a video file in OpenCV Java, and have narrowed down my issue to the following code:
import org.opencv.core.Core;
import org.opencv.videoio.VideoCapture;
public class OpenCVTest {
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
System.loadLibrary("opencv_videoio_ffmpeg455_64");
VideoCapture capture = new VideoCapture("myVideoFile.avi");
System.out.println(capture.isOpened());
}
}
Of course, this always prints out "false". Accessing my computer's camera with new VideoCapture(0) works fine. After scouring the internet, I'm thoroughly confused as to why loading a video won't work. I followed guides that suggested I needed to add "opencv_videoio_ffmpeg455_64.dll" to my path variable and call System.loadLibrary. I'm new to this, and to be honest, I don't understand what loadLibrary does, or what could be wrong with my setup and code. Any ideas? Thanks in advance.
Here is the answer from similar problem
load the ffmpeg library System.loadLibrary("opencv_ffmpeg300_64");
Open the file:
VideoCapture vC = new VideoCapture("res/video.mp4");
Copy opencv_ffmpeg300_64.dll from opencv\build\x64\vc11\bin to
opencv\build\java\x64
Please note that 64 and .dll may differ from an OS to another, those are for Windows x64
As it turns out, I was using the wrong file path. I (wrongly) assumed that new VideoCapture("my file") would search for "my file" in the directory where the compiled .class files are placed, when in fact it searches in the project root directory. There is no need to call System.loadLibrary("opencv_videoio_ffmpeg455_64");

Loading java code at runtime

I got a little project where I have to compute a list. The computation depends on serveal factors.
The point is that these factors change from time to time and the user should be allowed to change this by it's self.
Up to now, the factors are hard-coded and no changes can be done without recompiling the code.
At the moment the code looks like this:
if (someStatement.equals("someString")) {
computedList.remove("something");
}
My idea is to make an editable and human readable textfile, configfile, etc. which is loaded at runtime/ at startup? This file should hold the java code from above.
Any ideas how to do that? Please note: The targeted PCs do not have the JDK installed, only an JRE.
An effective way of going about this is using a static initializer. Static Block in Java A good and concise explanation can be found under this link.
One option here that would allow this would be to use User Input Dialogs from the swing API - then you could store the users answer's in variables and export them to a text file/config file, or just use them right in the program without saving them. You would just have the input dialogs pop up at the very beginning of the program before anything else happens, and then the program would run based off those responses.
You could use Javascript for the configuration file language, instead of java. Java 7 SE and later includes a javascript interpreter that you can call from Java. it's not difficult to use, and you can inject java objects into the javascript environment.
Basically, you'd create a Javascript environment, insert the java objects into it which the config file is expected to configure, and then run the config file as javascript.
Okay, here we go... I found an quite simple solution for my problem.
I am using Janino by Codehaus (Link). This library has an integrated Java compiler and seems to work like the JavaCompiler class in Java 7.
BUT without having the JDK to be installed.
Through Janino you can load and compile *.java files(which are human readable) at runtime, which was exactly what I needed.
I think the examples and code-snippets on their homepage are just painful, so here's my own implementation:
Step one is to implement an interface with the same methods your Java file has which is loaded at runtime:
public interface ZuordnungInterface {
public ArrayList<String> Zuordnung(ArrayList<String> rawList);}
Then you call the Janino classloader when you need the class:
File janinoSourceDir = new File(PATH_TO_JAVAFILE);
File[] srcDir = new File[] { janinoSourceDir };
String encoding = null;
ClassLoader parentClassLoader = this.getClass().getClassLoader();
ClassLoader cl = new JavaSourceClassLoader(parentClassLoader, srcDir,
encoding);
And create an new instance
ZuordnungsInterface myZuordnung = (ZuordnungInterface) cl.loadClass("zuordnung")
.newInstance();
Note: The class which is loaded is named zuordnung.java, so there is no extension needed in the call cl.loadClass("zuordnung").
And finaly the class I want to load and compile at runtime of my program, which can be located wherever you want it to be (PATH_TO_JAVAFILE):
public class zuordnung implements ZuordnungInterface {
public ArrayList<String> Zuordnung(ArrayList<String> rawList){
ArrayList<String> computedList = (ArrayList<String>) rawList.clone();
if (Model.getSomeString().equals("Some other string")) {
computedList.add("Yeah, I loaded an external Java class");
}
return computedList;
}}
That's it. Hope it helps others with similar problems!

JNI Use native method

I'm working on a project where there's the need to call some methods from dll files.
These two dlls are
EasySign.dll
EasySignJNI
EasySignJNI depends on EasySign.
I wrote the class to load EasySignJNI as follows:
package easysign;
class EasySign {
EasySign(){}
public native String EasyHashFile(String filename);
public native int EasySign(String pkcs11_driver,String pin, int type, String file_data, int out_format, String signed_file, String cert_out, int cert_format);
public native int EasyVerify(String cert_user, String file_data, String signed_file, String crl_file, String ca_file, String out_document);
static {
System.loadLibrary("EasySignJNI");
}
}
Now I would call these method from my main method like this:
public class Test {
public static void main(String[] args) throws IOException{
EasySign es = new EasySign();
System.out.println("EasyHashFile : " + es.EasyHashFile("test.txt"));
}
}
What I have to specify in the -Djava.library.path ? Only the path where my EasySignJNI.dll is located? It is possible to call native method in this way?
I'm using NetBeans for completeness.
EDIT:
I have noticed that the third party dll provided to me (the JNI dll in particular) defines method names without any package, so I'm forced to put the class that loads the dll in the default package. Is there any way to change only the dll method names including my own package name?
EDIT 2:
What I mean is that both EasySign.dll and EasySignJNI.dll are provided me as they are and I can't modify them (I have not the source code). The EasySignJNI is the JNI portion but inspecting it I have noticed thath the method sign is in the form: _java_EasySign_MethodName. When I load the dll in Java from my Easysign class (this class must reside in the "mypackage" package), I receive the jni unsatisfiedlinkerror because, if I understood right, I'm calling the "_java_mypackage_EasySign_MethodName" method, i.e the sign is different from the dll's one. So the only way to make it work is to rewrite the JNI part and build it to have the correct sign of the JNI method?
What I have to specify in the -Djava.library.path ? Only the path where my EasySignJNI.dll is located?
Correct, the operating system will locate the dependent EasySign.dll for you as long as it is available where the OS expects it to be.
It is possible to call native method in this way? I'm using NetBeans for completeness.
I read through your edit and you have successfully lost me. What default package are you referring to? (Remember that none of us know what EasySign.dll is) So, I am going to provide some info about how I do what you originally described and hopefully it will help.
First start by compiling EasySign if you have the src. Do not build a DLL or shared object, instead build a static library. If you do not have the src code for EasySign, or a prebuilt static library, you will be stuck with the dll and can continue to the next step.
Now you are ready to compile the jni portion. All of your JNI C code should basically translate your Java input/output to their JVM/Native types and call the appropriate functions in the DLL library. You want to keep this layer and thin and simple as possible because it is incredibly difficult to debug. Your C++ package names shouldn't really matter here and you can use what ever package name you want for your Java classes. You should be able to compile the JNI code and preferably static link to the EasySign.dll file so you don't need to worry about distributing it. If you must dynamically link, make sure EasySign.dll gets installed to a location that is on the DLL PATH / LDPATH because the OS will need to locate and load that file right after the JVM loads the JNI DLL.
At this point you should just be able to point -Djava.library.path at your JNI DLL's path and all should work.

Any DLL import throws an UnsatisfiedLinkError, though the DLL seems to load

I use Eclipse Java 32Bit JDK. I am trying to import a 32-Bit DLL, which i got very few documentation for. I am using com.sun.jna.
The documentation tells me to call the method InitImagerIPC without any parameters.
As in this example i created an interface:
public interface ImagerIPC extends com.sun.jna.win32.StdCallLibrary{
HRESULT InitImagerIPC ();
}
When I try to import the DLL no error occurs.
ImagerIPC lib = (ImagerIPC) Native.loadLibrary ("ImagerIPC", ImagerIPC.class);
But I receive an UnsatisfiedLinkError if I mistype my DLL name. So I guess it's loaded correctly?
But when I try to call
lib.InitImagerIPC();
I get the UnsatisfiedLinkError. :(
Where's the mistake?
BTW: The DLL Export Viewer pulls out this info for the wanted method:
_InitImagerIPC#0 0x10001fc0 0x00001fc0 45 (0x2d)
ImagerIPC.dll J:\<my Path>\ImagerIPC.dll Exported Function
I am not sure about the naming. Do I also have to add the _?
Your DLL uses the stdcall calling convention, which typically has the compiler mangle function names to include the arguments' stack size. You need to use a StdCallFunctionMapper passed in as the Library.OPTION_FUNCTION_MAPPER option when you load your library (or recompile your library to use undecorated names).
Library.loadLibrary("myLib", myLib.class, new HashMap() { { put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper()); } });

Method to dump classes on the classpath from inside JVM?

My code is failing with a ClassNotFoundException.
I can see that the jar file containing the class is definitely on the classpath from the command prompt execution.
Is there a way to dump the list of classes on the classpath from the JVM? (Ideally some Java code).
(I don't want to see the classes in a directory, I want to see a list of what is loaded into the JVM).
You can programatically display the classpath by looking at the classloaders and dumping the URLs they are loading from.
Something like this:
import java.net.URLClassLoader;
import java.util.Arrays;
public class ClasspathDumper
{
public static void main(String... args)
{
dumpClasspath(ClasspathDumper.class.getClassLoader());
}
public static void dumpClasspath(ClassLoader loader)
{
System.out.println("Classloader " + loader + ":");
if (loader instanceof URLClassLoader)
{
URLClassLoader ucl = (URLClassLoader)loader;
System.out.println("\t" + Arrays.toString(ucl.getURLs()));
}
else
System.out.println("\t(cannot display components as not a URLClassLoader)");
if (loader.getParent() != null)
dumpClasspath(loader.getParent());
}
}
it would produce output similar to:
Classloader sun.misc.Launcher$AppClassLoader#2a340e:
[file:/C:/Java/workspaces/myproject/bin/]
Classloader sun.misc.Launcher$ExtClassLoader#bfbdb0:
[file:/C:/Java/jdk/jdk1.7.0/jre/lib/ext/dnsns.jar, file:/C:/Java/jdk/jdk1.7.0/jre/lib/ext/localedata.jar, file:/C:/Java/jdk/jdk1.7.0/jre/lib/ext/sunec.jar, file:/C:/Java/jdk/jdk1.7.0/jre/lib/ext/sunjce_provider.jar, ...]
..? (Ideally some Java code)
If you were looking only to resolve a Class-Not-Found bug, then adding a dump code within the app can add complexity to turn it off later. Perhaps it would be better to use -verbose:class JVM argument which would output all classes loaded at runtime. Its easy to turn off and output of the console can be easily redirected to a log.
That's actually not what you want to see if you're getting a CNFE, since it's not found. Plus not all available classes will be loaded at any given time.
Start by going through this list. But in general, if it's not found, it's actually not found.
Well, you could create a memory dump (e.g. via jmap) and view it (e.g. via jhat).
Alternatively, IIRC jconsole can show the loaded classes, so you could just view them. I'm not entirely sure though and I don't have a running jconsole right now.
A third alternative (for Java 5+) would be VisualVM which is part of the Java6+ distribution.
However, most certainly your jar file is not on the classpath or you are using some custom classloaders. Could you elaborate on how you put that jar on the classpath?

Categories

Resources