I've got a C++ program that internally uses java (via my C++ dll that wrapps the WebLogic jsmc.dll that internally uses jvm.dll).
When I set CLASSPATH before running my program, all JAR libraries are found and the program works properly.
When I do not set the CLASSPATH before running my program, the JARs are not found, which of course is expected.
Now, when I set the CLASSPATH before running my program, but clear this CLASSPATH environment variable inside the program code before loading my dll that uses java, a strange thing happens: all JARs are still found and the program works as if everything was OK. I have verified by several ways that the CLASSPATH is really deleted from the env variables (e.g. by using ProcessExplorer or by printing its value).
QUESTION:
Can you explain this behaviour to me? I'm not wondering why java ignores the CLASSPATH I set, but how is it possible that java sees the old CLASSPATH value, not the current one? I emphasize that it isn't possible for java to store the old CLASSPATH value somehow because java was not loaded at the the time the old value was available.
How can I make the java to respect the changes in the process env variables?
DETAILS:
The problem above is just a simplification I've made to explore my real problem. I'm trying to set the CLASSPATH from within the program and avoid to have it set externally. But the java uses the externally set CLASSPATH, not the one I set inside the program.
I read and set the env variables values using the Windows API (GetEnvironmentVariableA, SetEnvironmentVariableA). I have verified that the program process environment variables really change after setting them this way. I even printed the CLASSPATH value from the dll that uses java, before calling any java method. I checked using the ProcessMonitor that jvm.dll is really loaded after the CLASSPATH is deleted. I also tried to exclude the possibility that the CLASSPATH is read from the parent process. Now I'm pretty sure that at the time jvm.dll is being loaded, the CLASSPATH is already deleted from the process environment.
I have tried both a Visual C++ 2010 testing program and the HP LoadRunner C-compiler (mmdrv.exe) vuser script, with the same result. LoadRunner is the main reason why I need to solve this problem.
The problem was caused by that the C-runtime somehow caches the environment variables. While I was trying to modify the CLASSPATH using the system function SetEnvironmentVariableA(), jmsc.dll read CLASSPATH from the C-runtime cache. The C-runtime tries to synchronize its cache with the real values in the process environment, but evidently not very successfully. It was necessary for me to replace the system call to SetEnvironmentVariableA() with the call to _putenv() from the C-runtime in order to change the CLASSPATH.
But there was another problem. There were various versions of C-runtime used by my code, each having its own environment cache. My VC code was linked against msvcr100.dll, while jmsc.dll (that instantiates the Java VM) uses msvcrt.dll. The solution was to link my code to msvcrt.dll too, so that my code sets CLASSPATH using _putenv() from the same C-runtime that jmsc.dll reads.
Thanks to Harry Johnston for the crucial hint, and Peter Cetinski for valuable information.
You should not rely upon the CLASSPATH environment variable when invoking a new JVM process from C++. The JNI interface provides a mechanism to specify the classpath of the JVM upon startup.
See http://java.sun.com/docs/books/jni/html/invoke.html#28719
Related
This question already exists:
how to add new class(textio.java) in jdk 12.0.1
Closed 3 years ago.
Where to put the textio.java and its classes. I am using jdk 12.0.1. it is the error cmd is showing on comipiling file that uses textio class.
Please tell me the correct directory where to put textio class file in jdk 12.0.1 folder.
There are numerous ways to add third party libraries to your runtime. Without more information, the lowest energy way you're looking for isn't quite clear.
I believe the CLASSPATH environment variable is what you want. If it's not defined, you may define one by whatever process matches your operating system.
CLASSPATH is one way for your machine to tell your java installation which folders jars and other jvm bytecode are located in so that your java runtime can load them when your program starts. This can be anywhere, but depending on your operating system, there are standard locations that are often good to follow.
In linux/unix/OSX, you may run echo $CLASSPATH, and a non-empty result means that it's set. If it doesn't exist, then you may run this command to create it pointing to the folder you choose. export CLASSPATH=/path/to/jars. Just keep in mind that without some extra steps, this will be reset on next reboot.
If you're using an IDE such as eclipse or idea, then there are project-specific solutions that can be done as configuration options within your IDE, which will keep things you don't want globally available on your machine from being loaded up every time.
The install4j variable sys.installationDir on MacOS (not the folder) returns just /Applications.
This makes sense since technically the directory the application was installed in was "/Applications" but I've used this variable inside my VM options to point to certain dependencies. This is causing me to have runtime errors. Is there another variable I should be using for this?
I've looked and I don't see any other options except to verify the platform and include the rest of the path.
You should use the installer variable ${installer:sys.contentDir} for that purpose, it always points at the directory where the distribution tree is installed.
I have a Java program that uses vlcj to play videos and that packages the VLC libs in the jar. At runtime, the VLC libs are extracted to the user's home, let's say path A. The normal way to indicate this path to vlcj is through the jna method:
NativeLibrary.addSearchPath(RuntimeUtil.getLibVlcLibraryName(), "A");
This works under Windows and MacOSX but not Linux where it throws a UnsatisfiedLinkError.
After some trial and error, I found that the only to get this to work under linux was by using
export LD_LIBRARY_PATH=A
prior to execution and despite the JNA documentation, none of these worked in JVM settings:
-Djava.library.path=A
-Djna.library.path=A
-Djna.platform.library.path=A
My problem with using LD_LIBRARY_PATH is that it is not something I can set at runtime (can I?) which I need to do. Does anyone know of a way to get around this?
I never found an ideal solution to this myself, but this is what I found during my trials with my own vlcj projects.
If you build VLC yourself on Linux you will see these warnings:
If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the `LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the `LD_RUN_PATH' environment variable
during linking
- use the `-Wl,-rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to `/etc/ld.so.conf'
None of those things it suggests can you do from within your JVM, at least not without calling native code with privilege escalation.
So, in general what you are left with is: -Djna.library.path=LIBDIR should work; alternatively in code System.setProperty("jna.library.path", "LIBDIR"); should also work.
In fact I just tested that with my own native library that I happen to use in my own vlcj projects and both of those approaches worked just fine.
However, it seems it is not so easy with VLC itself, probably because of the way VLC loads its plugins.
In theory, if you structure your directories correctly, the plugins should get discovered automatically so you only need to point jna.library.path to the directory that contains the libvlc and libvlccore shared objects. In my build of VLC, the directory structure looks like this:
VLCDIR
VLCDIR/libvlc.so
VLCDIR/libvlc.so.5
VLCDIR/libvlc.so.5.4.0
VLCDIR/libvlccore.so
VLCDIR/libvlccore.so.7.0.0
VLCDIR/vlc/plugins
If this still fails then, again in theory, you can set the VLC_PLUGIN_PATH environment variable to point to the directory containing the VLC plugins. The problem is that this must be set for the native process, it will not work if you set it as a system property from inside your Java application.
I can only really suggest you generate a shell-script file that sets up the environment correctly when you install your application, or if you want to do it programmatically inside the JVM you could maybe have a bootstrap application that prepares the native environment and then kicks off a new Java process for your actual application - but it's messy to do things that way.
What I have also seen on Linux is that the library paths seem "baked in" to the ".so" files, and you can't just copy those files anywhere and still expect it to work. That is why you must use e.g. LD_LIBRARY_PATH or libtool or one of the other proposed solutions.
And this does not even touch on what you do with all the other libraries that VLC and its plugins may depend on at run-time - are you going to ship all those too?
My recommendation really is just to have the user install VLC first, or have your installer application install VLC first, by using the OS native package installation commands. Not ideal, but it works.
I'm trying to make a Java RMI client/server app. I'm running into problems starting up the server side of my app, as it keeps running into a ClassNotFoundException during the call to the Registry.bind() method when I attempt to start up the server side of the app.
I started with the simple tutorial here: http://docs.oracle.com/javase/1.5.0/docs/guide/rmi/hello/hello-world.html. After following those instructions, it was initially throwing a ClassNotFoundException complaining that it couldn't find "example.hello.Hello". I was able to resolve that by starting the rmiregistry FROM the destDir directory in the tutorial, since rmiregistry, apparently, uses its initial starting directory as part of its classpath.
I started on my other test app after that, and I was fine until I started to use third-party jar files in my server class. Now Registry.bind() throws a ClassNotFoundException if my server class references anything in any jar file since the rmiregistry app doesn't know about those jar files.
As far as I can tell, rmiregistry does not accept any sort of classpath startup arg, so I'm wondering how I can tell it what classpath I want it to acknowledge. According to the tutorial here: http://docs.oracle.com/javase/tutorial/rmi/running.html, "you must make sure that the shell or window in which you will run rmiregistry either has no CLASSPATH environment variable set or has a CLASSPATH environment variable that does not include the path to any classes that you want downloaded to clients of your remote objects." That sounds like the opposite of what I need... or am I reading it incorrectly? Has anyone had any success starting up a RMI client/server that uses third-party jars (commons-io, commons-logging, and rmiio, in my case)?
This is on Windows, by they way.
Update
I found a way around it. See my answer below.
The remarks about unsetting the CLASSPATH only apply if you're using the RMI codebase feature, as the cryptic remark about downloading classes is intended to imply.
If you're not using the codebase feature, just either:
Set the CLASSPATH environment variable as required before starting rmiregistry, as shown elsewhere here, or
Start rmiregistry with -J-Djava.class.path=...
Start the Registry inside your server JVM, with LocateRegistry.createRegistry(). This is in many ways the best solution, as it lives and dies with the JVM, shares its classpath, and incidentally complies with the requirements for the codebase feature as well.
If you do (3), you must store the return value into a static variable to prevent it from being garbage-collected, which causes the registry to be unexported, which can lead to DGC of the remote object, which can lead to it being GC'd, which will lead to the listening thread exiting, which can lead to the whole JVM exiting.
There is no RMI Registry specific java class path. It should use the same classpath as the rest of the Java system. You can set your classpath by specifying it on the command line, or in your OS environment. Though a little dated, this doc should point you in the right direction.
(I had originally posted this answer as an update to my question. Per Zecas's suggestion in one of the comments, I'm moving it to the answer section.)
I was able to get the server part to start up by disobeying the suggestion in the second tutorial referenced in my question above:
"you must make sure that the shell or window in which you will run
rmiregistry either has no CLASSPATH environment variable set or
has a CLASSPATH environment variable that does not include the path to
any classes that you want downloaded to clients of your remote
objects."
I created a CLASSPATH environment variable and added my .class output directory and each of the third-party jar files to that. I thought I had tried that before, but I guess not... Just thought I'd leave my solution in case someone else has the same problem.
An example of how to set classpath for rmiregistry
Win32:
set CLASSPATH=c:\home\ann\src;c:\home\ann\public_html\classes\compute.jar
UNIX:
setenv CLASSPATH /home/ann/src:/home/ann/public_html/classes/compute.jar
I'm writing some JNI code in C++ to be called from an applet on Windows XP. I've been able to successfully run the applet and have the JNI library loaded and called, even going so far as having it call functions in other DLLs. I got this working by setting up the PATH system environment variable to include the directory all of my DLLs are in.
So, the problem, is that I add another call that uses a new external DLL, and suddenly when loading the library, an UnsatisfiedLinkError is thrown. The message is: 'The specified procedure could not be found'. This doesn't seem to be a problem with a missing dependent DLL, because I can remove a dependent DLL and get a different message about dependent DLL missing. From what I've been able to find online, it appears that this message means that a native Java function implementation is missing from the DLL, but it's odd that it works fine without this extra bit of code.
Does anyone know what might be causing this? What kinds of things can give a 'The specified procedure could not be found' messages for an UnsatisifedLinkError?
I figured out the problem. This was a doozy. The message "The specified procedure could not be found" for UnsatisfiedLinkError indicates that a function in the root dll or in a dependent dll could not be found. The most likely cause of this in a JNI situation is that the native JNI function is not exported correctly. But this can apparently happen if a dependent DLL is loaded and that DLL is missing a function required by its parent.
By way of example, we have a library named input.dll. The DLL search order is to always look in the application directory first and the PATH directories last. In the past, we always ran executables from the same directory as input.dll. However, there is another input.dll in the windows system directory (which is in the middle of the DLL search order). So when running this from a java applet, if I include the code described above in the applet, which causes input.dll to be loaded, it loads the input.dll from the system directory. Because our code is expecting certain functions in input.dll which aren't there (because it's a different DLL) the load fails with an error message about missing procedures. Not because the JNI functions are exported wrong, but because the wrong dependent DLL was loaded and it didn't have the expected functions in it.
There is a chance that the DLL was built using C++(as opposed to C). unless you took care to do an extern on the procedure,this is one possible reason.
Try exporting all the functions from the DLL. If the list includes your function, then you're good.
Usually, when linking to other libraries, you need to link to the relevant .lib file. It sounds like you aren't referencing all the lib files you need. Check what isn't linking and make sure you add it's lib to the list for the linker.
Did you create the new external DLL using the standard JNI procedure? I.e., using javah and so forth? If so, then I am not sure what is wrong.
If not, then the procedure you're trying to call hasn't been exported (as mentioned by anjanb). I am aware of two way of exporting functions: a separate export list and marking specific functions with __declspec(dllexport).
Can't access variable in C++ DLL from a C app has a little more information the topic of DLLs.
Compile your c++ code in debug mode. Then insert the DebugBreak(); statement where you would like to start debugging. Run the java code. When the DebugBreak() statement is encountered you will get a popup with a Debug button on it. Click on it. Dev Studio will open with your program in machine code. Step over with the debugger twice and you should be able to step over your source code.
If you have done all programming issue at JNI manuals and examples but still you are getting same missing procedure error, problem can be at your path variable probably. Do below steps and run again:
Be sure about you set JAVA_HOME variable to your JDK folder(not JRE because JRE doesnt contain jni header)
Example:
At environment variable settings panel define var:JAVA_HOME val:C:\Program Files\Java\jdk1.7.0_11
add %JAVA_HOME%\bin to your path variable
After doing those steps, your application can find jni procedure name and links to JNI.dll in right way. So, i hope you dont get this missing procedure error again.