I'm facing a strange behaviour of the JVM. I wanted to change the user directory, i.e. the dir where files are looked up, that normally corresponds to the path where the java command is run.
So I wrote the following code:
System.setProperty("user.dir", "/tmp/");
File f = new File("myfile");
System.out.println(f.exists());
System.out.println(f.getCanonicalFile().exists());
The file /tmp/myfile exists and is readable by the JVM, but if I'm not in /tmp/ when I run that code, the result is:
false
true
They are the same file, Java is able to retrieve the correct canonical form of it, but the relative one does not exist, while the canonical one exists.
Is it a bug? Is there a way to reliably change the JVM user directory?
Changing the code is not an option, as I'm trying to run external libraries.
This behavior is normal, the reason is, there is a difference between
File f = new File("myfile");
and
File cf = new File("myfile").getCanonicalFile();
The first denotes a filepath relative to your current WORKING DIR, which could be your project path. Using the relative path, the user.dir property is NOT USED, even when setting user.dir as JVM parameter -Duser.dir=/tmp/. The resolution of Java file handles to native file entities is done natively by the underlying Fileystem implementation.
But when invoking getCanoncialFile() before resolving the native file handle the relative path is resolved using the user path information - in your case user.dir = /tmp/.
Apparently, there is no file myfile in <WORKING_DIR> but in /tmp/.
The behavior is the same for f.getAbsoluteFile().
I'd like to pass a file path argument to my application in a relative form, e.g ~/test.conf or ../test.conf, but i can't get a proper full file path, though i've tried it with old java.io and new java.nio Files/Paths. Is there a general way to get a resolved file path without large amount of code? It would be fine for the solution to work only in unix envs like OsX or Debian.
Update
With a provided argument like ~/test.conf
in case of getAbsolutePath it returns a path with a prefixed current folder - /Users/currentUser/Projects/Personal/TestProject/~/text.conf. Canonical path returns the same.
The hard bit here is dealing with POSIX home directories, and sure you deal with ~otheruser/dir/test.conf too (if you want to do it properly).
Luckily that's covered in How to handle ~ in file paths.
TL;DR - use something like:
path.replace("~/", System.getProperty("user.home") + "/");
Once you've done that, and as others have commented, you can just use standard java.io methods (including getCanonicalPath()).
I have for example .pdf file (path to that file). How to open this file in default application (probably Acrobat Reader) from SWT application (for example on Button click) ?
You should be able to use:
Program.launch(file);
to open the file (using the default application or creator). From the javadoc:
Launches the operating system executable associated with the file or URL (http:// or https://). If the file is an executable then the executable is launched. Note that a Display must already exist to guarantee that this method returns an appropriate result.
Note that there are some peculiarities in Program.launch() (or at least there were, though these may have been fixed in more recent versions of the runtime.) I don't really remember the specifics of the bugs, but we do some checks to work around some issues:
If you're on a Unix platform, and you're specifying an absolute path, there may be trouble opening that file. We prefix absolute paths with /. - so that /tmp/foo would be translated to /./tmp/foo - although I don't really remember the specifics of this bug any more than that.
On Windows, if you're trying to open a UNC path - for example \\server\bar - you need to wrap the string in double-quotes. For example: Program.open("\"\\server\bar\"");
Try Desktop.open:
Desktop.getDesktop().open(file);
Maybe this can help to find a decision: we ran into PermGen space trouble upon call Desktop.open() - which is in AWTpackage - out of our SWT application.
So I would prefer Program.launch() over Desktop.open() in a SWT-environment.
I have installed a java batch process using the version of procrun that ships with tomcat 5.5.33:
Commons Daemon Service Runner version 1.0.5.0/Win32 (Jan 5 2011)
Copyright (c) 2000-2011 The Apache Software Foundation.
In the installation, I specify (among other JVM options):
--JvmOptions="-Duser.dir=C:\LOCAL\serverapps"
My log4j.properties configuration includes:
log4j.appender.InfoLogFile.File=../logs/info.log
However, the info.log file is being written to:
C:\WINDOWS\logs
I've checked the value of user.dir at many different points and it's always C:\LOCAL\serverapps.
But, log4j is behaving as if user.dir=C:\Windows\System32 (or some other subir of C:\Windows).
From what I can tell from the log4j source (1.2.16), the FileAppender deals only with the java.io.FileOutputStream and File classes which claim to make paths relative from the user.dir location.
I've worked around the issue, but I am curious: has anyone else has encountered this type of behaviour? If so, what's really going on?
FileAppender, when given a relative path, creates a file withing the current working directory - not the user home directory.
You need to pass the ${user.dir} within the filename.
SRC:
http://svn.apache.org/viewvc/logging/log4j/trunk/src/main/java/org/apache/log4j/FileAppender.java?view=markup
EDIT: see comment below for correction - user.dir != user.home
http://bugs.sun.com/view_bug.do?bug_id=4117557
I have used ${user.dir} in the lo4j.properties and it has worked. Have you tried?
log4j.appender.InfoLogFile.File=${user.dir}/logs/info.log
PhilW's comment points to the correct answer to the original question. That is, Oracle/Sun declares an issue http://bugs.sun.com/view_bug.do?bug_id=4117557 when user.dir is set via the command line. That is the reason why the relative path is not properly understood when logging files are written out.
By using a an absolute path (even prefixing with ${user.dir} -- which can be trusted at that point - even if the JVM gets the value wrong internally) as Phil, Amir and I all suggest, you avoid the issue altogether.
How can I load a custom dll file in my web application? I've tried the following:
Copied all required dlls in system32 folder and tried to load one of them in Servlet constructor System.loadLibrary
Copied required dlls into tomcat_home/shared/lib and tomcat_home/common/lib
All these dlls are in WEB-INF/lib of the web-application
In order for System.loadLibrary() to work, the library (on Windows, a DLL) must be in a directory somewhere on your PATH or on a path listed in the java.library.path system property (so you can launch Java like java -Djava.library.path=/path/to/dir).
Additionally, for loadLibrary(), you specify the base name of the library, without the .dll at the end. So, for /path/to/something.dll, you would just use System.loadLibrary("something").
You also need to look at the exact UnsatisfiedLinkError that you are getting. If it says something like:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no foo in java.library.path
then it can't find the foo library (foo.dll) in your PATH or java.library.path. If it says something like:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.example.program.ClassName.foo()V
then something is wrong with the library itself in the sense that Java is not able to map a native Java function in your application to its actual native counterpart.
To start with, I would put some logging around your System.loadLibrary() call to see if that executes properly. If it throws an exception or is not in a code path that is actually executed, then you will always get the latter type of UnsatisfiedLinkError explained above.
As a sidenote, most people put their loadLibrary() calls into a static initializer block in the class with the native methods, to ensure that it is always executed exactly once:
class Foo {
static {
System.loadLibrary('foo');
}
public Foo() {
}
}
Changing 'java.library.path' variable at runtime is not enough because it is read only once by JVM. You have to reset it like:
System.setProperty("java.library.path", path);
//set sys_paths to null
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);
Please, take a loot at: Changing Java Library Path at Runtime.
The original answer by Adam Batkin will lead you to a solution, but if you redeploy your webapp (without restarting your web container), you should run into the following error:
java.lang.UnsatisfiedLinkError: Native Library "foo" already loaded in another classloader
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1715)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1646)
at java.lang.Runtime.load0(Runtime.java:787)
at java.lang.System.load(System.java:1022)
This happens because the ClassLoader that originally loaded your DLL still references this DLL. However, your webapp is now running with a new ClassLoader, and because the same JVM is running and a JVM won't allow 2 references to the same DLL, you can't reload it. Thus, your webapp can't access the existing DLL and can't load a new one. So.... you're stuck.
Tomcat's ClassLoader documentation outlines why your reloaded webapp runs in a new isolated ClassLoader and how you can work around this limitation (at a very high level).
The solution is to extend Adam Batkin's solution a little:
package awesome;
public class Foo {
static {
System.loadLibrary('foo');
}
// required to work with JDK 6 and JDK 7
public static void main(String[] args) {
}
}
Then placing a jar containing JUST this compiled class into the TOMCAT_HOME/lib folder.
Now, within your webapp, you just have to force Tomcat to reference this class, which can be done as simply as this:
Class.forName("awesome.Foo");
Now your DLL should be loaded in the common classloader, and can be referenced from your webapp even after being redeployed.
Make sense?
A working reference copy can be found on google code, static-dll-bootstrapper .
You can use System.load() to provide an absolute path which is what you want, rather than a file in the standard library folder for the respective OS.
If you want native applications that already exist, use System.loadLibrary(String filename). If you want to provide your own you're probably better with load().
You should also be able to use loadLibrary with the java.library.path set correctly. See ClassLoader.java for implementation source showing both paths being checked (OpenJDK)
In the case where the problem is that System.loadLibrary cannot find the DLL in question, one common misconception (reinforced by Java's error message) is that the system property java.library.path is the answer. If you set the system property java.library.path to the directory where your DLL is located, then System.loadLibrary will indeed find your DLL. However, if your DLL in turn depends on other DLLs, as is often the case, then java.library.path cannot help, because the loading of the dependent DLLs is managed entirely by the operating system, which knows nothing of java.library.path. Thus, it is almost always better to bypass java.library.path and simply add your DLL's directory to LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (MacOS), or Path (Windows) prior to starting the JVM.
(Note: I am using the term "DLL" in the generic sense of DLL or shared library.)
If you need to load a file that's relative to some directory where you already are (like in the current directory), here's an easy solution:
File f;
if (System.getProperty("sun.arch.data.model").equals("32")) {
// 32-bit JVM
f = new File("mylibfile32.so");
} else {
// 64-bit JVM
f = new File("mylibfile64.so");
}
System.load(f.getAbsolutePath());
For those who are looking for java.lang.UnsatisfiedLinkError: no pdf_java in java.library.path
I was facing same exception; I tried everything and important things to make it work are:
Correct version of pdf lib.jar ( In my case it was wrong version jar kept in server runtime )
Make a folder and keep the pdflib jar in it and add the folder in your PATH variable
It worked with tomcat 6.
If you believe that you added a path of native lib to %PATH%, try testing with:
System.out.println(System.getProperty("java.library.path"))
It should show you actually if your dll is on %PATH%
Restart the IDE Idea, which appeared to work for me after I setup the env variable by adding it to the %PATH%
The issue for me was naming:
The library name should begin with "lib..." such as libnative.dll.
So you might think you need to load "libnative": System.loadLibrary("libnative")
But you actually need to load "native": System.loadLibrary("native")
Poor me ! spent a whole day behind this.Writing it down here if any body replicates this issue.
I was trying to load as Adam suggested but then got caught with AMD64 vs IA 32 exception.If in any case after working as per Adam's(no doubt the best pick) walkthrough,try to have a 64 bit version of latest jre.Make sure your JRE AND JDK are 64 bit and you have correctly added it to your classpath.
My working example goes here:unstatisfied link error
I'm using Mac OS X Yosemite and Netbeans 8.02, I got the same error and the simple solution I have found is like above, this is useful when you need to include native library in the project. So do the next for Netbeans:
1.- Right click on the Project
2.- Properties
3.- Click on RUN
4.- VM Options: java -Djava.library.path="your_path"
5.- for example in my case: java -Djava.library.path=</Users/Lexynux/NetBeansProjects/NAO/libs>
6.- Ok
I hope it could be useful for someone.
The link where I found the solution is here:
java.library.path – What is it and how to use
It is simple just write java -XshowSettings:properties on your command line in windows and then paste all the files in the path shown by the java.library.path.
I had the same problem and the error was due to a rename of the dll.
It could happen that the library name is also written somewhere inside the dll.
When I put back its original name I was able to load using System.loadLibrary
First, you'll want to ensure the directory to your native library is on the java.library.path. See how to do that here. Then, you can call System.loadLibrary(nativeLibraryNameWithoutExtension) - making sure to not include the file extension in the name of your library.