I wish to make a java implementation based on a C++ library using the JNI. Unfortunately, when I wish to use the shared library in java it can not be found. The problem with this shared library is that it links another shared library. I have two different cases one works (but shouldn't be used) and the other one doesn't.
The Ana lib must always be static. The Clara lib must always be shared. It works when the Bob lib is static, but it should be shared and it should include Ana and Clara.
Case 1 (Works and can be loaded in the correct java.library.path).
sharedlibJava.so links: staticlibAna.a staticlibBob.a sharedlibClara.so
Case 2 (Doesn't work because it can't be loaded even though the java.library.path is correct).
sharedlibBob.so links: staticlibAna.a and sharedlibClara.so (the basic c++ implementation is tested and it works without problems)
sharedlibJava.so links: sharedlibBob.so (the java implementation should be available using this new shared library).
The problem is that when I try to load the library it doesn't even find it in the correct java.library.path even though it is located there.
I would really appreciate any feedback regarding this issue.
Put all your path locations on LD_LIBRARY_PATH. Alternatively, put them into -Djava.library.path
If you have compiled code properly, I don't think you should experience issues while accessing shared library.
If you compile one code as shared lib, and then refer to it by linking other code with shared lib, it should work just fine
cc -g -shared -fpic c/recipeNo023_AnotherFunction.c -o lib/libAnotherFunction.$(EXT)
cc -g -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/$(ARCH) c/recipeNo023_HelloWorld.c -L./lib -lAnotherFunction -o lib/libHelloWorld.$(EXT)
Then, you should be able to call the code by passing lib location inside -Djava.library.path
java -Djava.library.path=:./lib -cp target recipeNo023.HelloWorld
library: :./lib
Hello world!
Hello from another function!
For the full sample code, take a look here:
https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo023
Related
I need to create an original .so library (C code) that uses another .so library. After creating my .so library I wrote a short test program code on C that invokes functions of my lib. It works, but when I compile this program I use that command:
gcc my_test.c -lm -o my_test_program -lmy_lib -lother_lib -L
I need to use my library in Java code (JNA) but when I try to invoke functions from my lib I get this error:
/usr/lib64/libmy_lib.so.0: undefined symbol
I think that means it has lost dependence of other_lib.
What can I do to add dependence in my_lib? Or other solutions?
I've been working on trying to get COBOL and Java to interact with each other on the mainframe, and have run into trouble with specifically the cob2 compiler, which is the Unix on the mainframe equivalent.
I haven't seen many user experiences with this compiler online, so I was wondering if I asked a more direct question, people would reveal their insight.
IBM has several examples of Java calling COBOL DLL's either directly or indirectly, but they ultimately boil down to compile the COBOL as a dll, use System.load, compile Java and run. These examples haven't worked for me for the following reasons.
When using cob2 with the -c option, it is purported to generate a .o object file. This has not happened for me, although it did generate an empty .lst file. I was able to get around this by simply skipping the -c step and compiling and linking using this series of commands:
` sh ${COB2HOME}/bin/cob2 -o ${DIR}/c2jcr.o
-qdll,thread,case=mixed ${DIR}/c2jcr.cbl;
${COB2HOME}/bin/cob2
-o ${DIR}/libc2jcr.so
-bdll,case=mixed ${DIR}/c2jcr.o
${JAVAHOME}/bin/j9vm/libjvm.x
${COB2HOME}/lib/igzcjava.x `
This appears to provide the .so library that is required for link with the Java program, but upon investigation of the load, and during run, the system declares that the LE CSECT CEESTART is not there.
Am I missing something in my cob2 library that has these LE modules, or somewhere in my scripting? I tried pulling in loads from the mainframe compiled with the LE modules intact and ENTRY CEESTART explicitly stated in the link step, but could not get any further than "UnsatisfiedLinkError" with "Internal Error".
Any wisdom is greatly appreciated, especially if you've gone down a completely different route to call COBOL from Java. Thank you very much.
After conferring with IBM, it turns out I had a couple things missing.
You must have a STEPLIB environment field set to the location of your COBOL compiler on the mainframe, so it can find your IGYCRCTL module.
Second, like other COBOL 5+ compiling, you must allocate a gargantuan amount of space in order to compile. 2 GB is not enough. Since I don't have permission to reallocate this in Unix, I ran a BPXBATCH job with REGION=0M.
After those two changes, -c compiles came out as normally. The "workaround" I provided in the question is completely incorrect. You must use:
sh ${COB2HOME}/bin/cob2 -c -qdll,thread,case=mixed ${DIR}/${COBPROG}.cbl
as your compile step, and the rest is just linkage.
MATLAB is configured to search its static java class path before searching the user-modifiable dynamic path. Unfortunately, the static path contains quite a number of very old public libraries, so if you are trying to use a new version you may end up loading the wrong implementation and get errors.
For instance, the static path contains an old copy of the google-collections.jar, which has long been supplanted by Google's guava library and which has some of the same class names (e.g. com.google.common.base.Objects). As a result, if you invoke a Guava method that uses a newer method of one of such a class, you will end up getting surprising NoSuchMethodErrors because the google-collections jar is found first.
As of R2012b, MATLAB lets you specify additional jars to add to the static path by putting a javaclasspath.txt file in your preferences folder, but that adds jars to the end of the path, and doesn't let you override jars that are built into MATLAB.
So what is the best way around this?
I got an official response from Mathworks:
As of MATLAB R2013a (also in R2012b), classes can be added to the front of the static Java class path by including the following line in javaclasspath.txt:
<before>
Any directory that is after this line in javaclasspath.txt will be added to the front of the static Java class path. This is an undocumented use of javaclasspath.txt as of R2013a.
But overall in MATLAB, the ability to add classes to the front of the static Java classpath is not available through javaclasspath.txt in MATLAB 8.0 (R2012b).
MATLAB searches for classpath.txt in the following order:
In the startup directory. As of MATLAB 8.0 (R2012b) a warning will be shown if the file is found there and it will be ignored.
In the first directory on the MATLABPATH environment variable. (This environment variable is used in the bin/matlab shell script on Linux and in general is not used by the end-user).
In the toolbox/local directory.
Although the MATLABPATH environment variable of point 2 is normally not used by end-users we can use it in a workaround to allow reading a custom classpath.txt outside of the toolbox/local directory.
On Windows:
You will need to create the MATLABPATH environment variable. The first directory on it should be your directory with the custom classpath.txt AND you will also need to add the toolbox\local directory as second option. So from a cmd prompt you could do:
set MATLABPATH=c:\Users\user\Documents\myMATLABClasspath;c:\Program Files\MATLAB\R2012b
\toolbox\local
matlab.exe
One hack that appears to work is to add the jar to the top of the classpath.txt file that can be found in your MATLAB installations toolbox/local folder. Unfortunately, this is automatically generated and may get rewritten at some unspecified time, such as when you install new toolboxes, so this approach would require you to have some way to notice when this happens and reapply the hack.
If you're distributing a jar that's intended to be used with matlab, it may be better to use proguard as described at http://code.google.com/p/guava-libraries/wiki/UsingProGuardWithGuava.
If you specify that all of your classes and their (public) fields and methods are to be preserved and include guava as a program jar (not a library), then it will rename all of guava's methods and update your compiled bytecode to reference the new names.
It seems a bit hackish, but depending on the audience, it may be significantly easier than teaching your users about static vs. dynamic classpath, and it won't break any matlab code that depends on the old behavior.
Instead of obfuscating the package as suggested by #user2443532, I have found it easier to "shade" the conflicting package instead of obfuscating it - unless you actually need obfuscation. One easy way to do this is to build your package using Maven and use the maven-shade-plugin. Internal calls are modified automatically, so you don't need to modify any of the Java code.
Direct calls from Matlab will need to be modified - for example, calls to com.opensource.Class become shaded.com.opensource.Class.
For more info on shading, see What is the maven-shade-plugin used for, and why would you want to relocate Java packages?
I'm new to java development, I just want to use javac for my build system. I'm using java to add a feature to a program someone else wrote, specifically involving GeoTiff images.
I found a class online that I would like to use, however I'm having trouble building the class, no matter what I do I get this message:
javac GeoTiffIIOMetadataAdapter.java
GeoTiffIIOMetadataAdapter.java:11: package com.sun.media.imageio.plugins.tiff does not exist
import com.sun.media.imageio.plugins.tiff.GeoTIFFTagSet;
I'm on RHEL5, so I installed the package I thought I needed, jai-imageio-core.x86_64. But the problem persists. I think that I'm not setting some variable corrently (like -sourcepath or something). I would appreciate any help.
You need to include the jar with -cp or -classpath.
So your compile would be like java -cp "<location to jai_imageio-1.1.jar>" <your java class> .
I think you need this jar file.
You can read more about javac here.
Find out where the package installed the jar file with the class you want to import, and add it to the javac commandline in the -classpath. (You then also need to include it in the classpath when your plugin runs; how to do that may depend on the program it plugs into).
I think that I'm not setting some variable correctly (like -sourcepath or something)
This tutorial briefly introduces the usage of environment variables in Java: PATH and CLASSPATH
This one seems to be the most popular answer to various classpath related questions I've seen at online forums: Setting the class path.
To avoid "blind recommendation" I quickly skimmed through it before adding to this answer and, well... it really covers most of what one needs to know to deal with classpath. Pretty good; the reason why I didn't look into it before is that there always has been some guru nearby who explained stuff to me.
I am trying to load a dll in java using the following code
System.loadLibrary("mydll");
The project is placed in D:\development\project\ and i have placed the dll on D:. I then gave following VM argument in eclipse configuration
-Djava.library.path=D:/
But when i run i get UnsatisifiedLinkerError. After googling a bit, I used
System.load("D:\mydll.dll");
but again getting the same problem, could someone can help?
Where you specify the DLL filename in the library path, omit that. Additionally, your System.loadLibrary call should just be 'mydll'. I can tell you (from experience) that if you put the DLL in the root of your project in Eclipse (i.e., D:\Eclipse Workspace\Proj), it should work. Any further linker errors could be from dependency problems with finding other DLLs. The exception is the same. Use something like Dependency Walker (http://www.dependencywalker.com/) to see if your DLL relies on anything else not on the system library path.
Edit: UnsatisfiedLinkError: Thrown if the Java Virtual Machine cannot find an appropriate native-language definition of a method declared native -- it seems like you are using a JNI function which does not exist.
One problem you have is:
System.load("D:\mydll.dll");
should be
System.load("D:\\mydll.dll");
or
System.load("D:/mydll.dll");
I have had more success with System.load, but loadlibrary is better designed for multiplatform.
It figures out the extension for you.
Check out how to properly set up the native dependencies here. Additionally, make sure you use the correct JVM: in my case, the DLL was not found because it was a 32 bit DLL, but I used the x64 JVM!
Using System.loadLibrary("mydll") works fine, you can also use that one. If you used javah and you think with your DLL everything is fine, there are two possibilies:
The JVM does not find your DLL: In this case, your java library path is not correct (which I doubt) and you should probably set it to . and place your DLL in the current working dir.
The JVM does not find a DLL your DLL depends on: If you have any dependent libraries in your DLL, they are NOT searched by the JVM, but by Windows itself. And Windows does not know the java.library.path, so it will look in the system PATH variable for those. If you have the possibility, you can set the system PATH variable to the location of your DLLs before starting the JVM and everything will be fine. Or you can load all your DLLs using the JVM like this
System.loadLibrary("dll_1");
System.loadLibrary("dll_2");
System.loadLibrary("dll_3");
where dll_3.dll depends on dll_2.dll, which depends on dll_1.dll.
Hope that helps.
System.loadLibrary loads the DLL from the JVM path (JDK bin path).
If you want to load an explicit file with a path, use System.load()
See also: Difference between System.load() and System.loadLibrary in Java
public class MyClass
{
static
{
System.load("MyJNI.dll");
}
}
Put your Almafa.dll into the C:/Java/jre7/lib or /bin sorry, I can`t remember exactly. After you have done no more configuration needed, just say
static{
System.LoadLibrary("Almafa");
}
in the class, where you want to load it. It is works only in Java project, in Android like project you need to use JNI. I had posted now the result of 3 days no sleeping :)
I got my error resolved by using the following:
static {
try {
System.loadLibrary("myDLL");
} catch (Exception e) {
e.printStackTrace();
}
}
Instead of using System.load("myDLL.dll")
#alee- You can just copy and the paste the dll files in system32 folder of your windows and try to call the library through the System.loadLibrary("mydll")... i guess it may work...
Give the library path in your project as native library location,seems to be solved.