Why a Java file exists only in its canonical form? - java

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().

Related

How is File(".").getAbsolutePath() resolved under the hood?

I was wondering:
What happens after I ask for an absolute path of the current file in Java?
String s = File(".").getAbsolutePath();
How does an interpreter find the path to the current file in the filesystem? Is there some OS system call for this?
Does it return the path of a jar? Or sometimes it can return the path where the file was before compilation?
Every process has a "current directory".
From within the process, the path . refers to this current directory.
The actual path denoted by the current directory is (can be) a parameter to the system call that created the process (see ProcessBuilder for instance), or it can be inherited from the process that creates your process.
The current directory is an attribute of the process, and is completely unrelated to the location of the jar file. The two paths may match by coincidence.
There is no requirement that the . path refer to a writable location, or indeed any location in a filesystem. There may not even be a file system. That is, the path . is not required to exist at all.
For . and .. there is the current working directory, System.getProperty("user.dir"), which may be different at point of excecution, like in the IDE and stand-alone. . has nothing to do with the location of the jar,
but in which directory java is started.
So a path "a/./b/../c" will become "a/c". As there are disk operating systems with ... it just might be that a native call is made. Also Windows might want to replace POSIX / with \.
String to File will always be done at run time. However where the application is started, the current working directory or . - that depends, varies in fact.
Never at compile time, even a static File f = new File("./readme.txt"); creates an object when the class is loaded the first time.
Here File is a class for a phyiscal disk file.
A more general class, Path, is superior in that it can also have its path run inside a zip file, or any feasible URI that provides a file system view.
So I will use Path for further explanation. There is a worthwile utilities class Files with Path operations, allowing you to copy some file from inside a zip archive to some location on hard disk.
Path.absoluteFile is the Path version of getting the absolute path (depending on the file system view).
Path toRealPath​(LinkOption... options) with optional options does disk operating native calls (for a File) when symbolic links are wanted to be replaced with the actual physical paths. /a/b.lnk/c could actually be /a/d/e/c. This call is important should you want to detect if a path unwantedly escapes some restricted directory.

getCanonicalPath returns a different path between different platforms

One of the lower methods in my code is getting the canonical path to the temp folder using the file.getCanonicalPath() function (File was defined as File file = new File("/tmp")). This works in linux and windows OS's, but on macOS, this function returns the following string - "/private/tmp" even though I have a tmp folder in my home directory and I don't have /private directory.
Any idea where does this "private" directory is coming from and why this method is not directing me to "/tmp" in macOS even though its accessible?
Note: if I create a random, non- existing dir File object (File file = new File("/random")) it will return the canonical path just fine.
A symbolic link, also termed a soft link, is a special kind of file
that points to another file, much like a shortcut in Windows or a
Macintosh alias. Unlike a hard link, a symbolic link does not contain
the data in the target file. It simply points to another entry
somewhere in the file system.
On macOS /tmp is symlinked to /private/tmp. The directory /private does exist and contains tmp inside it.

Sharing a Java Object Stream

I've got a project to do with 2 other classmates.
We used Dropbox to share the project so we can write from our houses (Isn't a very good choice, but it worked and was easier than using GitHub)
My question is now about sharing the object stream.
I want to put the file of the stream in same dropbox shared directory of the code.
BUT, when i initialize the file
File f = new File(PATH);
i must use the path of my computer (C:\User**Alessandro**\Dropbox)
As you can see it is linked to Alessandro, and so to my computer.
This clearly won't work on another PC.
How can tell the compiler to just look in the same directory of the source code/.class files?
You can use Class#getResource(java.lang.String) to obtain a URL corresponding to the location of the file relative to the classpath of the Java program:
URL url = getClass().getResource("/path/to/the/file");
File file = new File(url.getPath());
Note here that / is the root of your classpath, which is the top of the directory containing your class files. So your resource should be placed inside the classpath somewhere in order for it to work on your friend's computer.
Don't use absolute paths. Use relative paths like ./myfile.txt. Start the program with the project directory as the current dir. (This is the default in Eclipse.) But then you have to ensure that this both works for development and for production use.
As an alternative you can create a properties file and define the path there. Your code then only refers to a property name and each developer can adjust the configuration file. See Properties documentation.

get a file path

Is there a way to get the full path for a file exists on the computer ?
For example , I want to get the full path for a file in a folder on desktop
I tried using :
File f = new File("help.chm");
String f2=f.getAbsolutePath();
f3=f3.replaceAll("\\\\","/" );
System.out.println("Path:"+f3);
but it gave me the path of the project like this:
C:/Users/toshiba/Documents/NetBeansProjects/test/help.chm
although the file is not located there .
If you create a file using new File("filename") which is the relative path, you cannot get the absolute path of the file using file.getAbsolutePath(), because the relative path is build according to the default user home directory or the JVM path.
Take a look at Java Doc: -
A pathname, whether abstract or in string form, may be either absolute
or relative. An absolute pathname is complete in that no other
information is required in order to locate the file that it denotes.
A relative pathname, in contrast, must be interpreted in terms of
information taken from some other pathname. By default the classes in
the java.io package always resolve relative pathnames against the
current user directory. This directory is named by the system property
user.dir, and is typically the directory in which the Java virtual
machine was invoked.
So, to get the absolute path for this case, you would actually have to write the path yourself. Get the absolute path till the directory where you saved the file, and append the file name to it.
Since the other answers do not cover your question, here is my comment:
To get a file's path, you first need to tell your java program where it is or how to find it.
For your specific example you can get the desktop path using: System.getProperty("user.home") + "/Desktop"; then you can search through folders on your desktop for a matching file name.
Read here to learn how to search for files: docs.oracle.com/javase/tutorial/essential/io/find.html
A File is a representation of a file path, not necessarily an existent file on disk - ie the file doesn't have to exist on disk for a File object to be not null.
That's why there's the File.exists() method.
The path "help.chm" will be relative to the directory from which you started the JVM, which in your case appears to be C:/Users/toshiba/Documents/NetBeansProjects/test/
To get a path to the desktop, you need to use the absolute path of the desktop directory in Windows, which will be something along the lines of C:/Users/toshiba/Desktop/help.chm
You are attempting to read the file from (default folder)
C:/Users/toshiba/Documents/NetBeansProjects/test/
File doesn't exist but the would-be-file's path will be
C:/Users/toshiba/Documents/NetBeansProjects/test/
If you read the file from where it really is:
File f = new File("C:/Users/toshiba/Desktop/help.chm");
You will see that exists() returns true.
System.out.println(f.exists());
Then:
String f2=f.getCanonicalPath();

File paths in Java (Linux)

I have created a Java application that loads some configurations from a file conf.properties which is placed in src/ folder.
When I run this application on Windows, it works perfectly. However when I try to run it on Linux, it throws this error:
java.io.FileNotFoundException: src/conf.properties (No such file or directory)
If you've packaged your application to a jar file, which in turn contains the properties file, you should use the method below. This is the standard way when distributing Java-programs.
URL pUrl = this.getClass().getResource("/path/in/jar/to/file.properties");
Properties p = new Properties();
p.load(pUrl.openStream());
The / in the path points to the root directory in the jar file.
Instead of
String PROP_FILENAME="src/conf.properties";
use
String PROP_FILENAME="src" + File.separator + "conf.properties";
Check the API for more detail: http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html
I would also check what your current working directory is if your path to that file is relative. You just need to make a File test = new File("."); and then print that files canonical path name.
If you are referencing any other locations like user.dir or something to that effect by using System.getProperty(), you'll want to at least verify that the directory you are using as the relative root is where you think it is.
Also, as Myles noted, check the slashes used as file path separators. Although you can always use the "/" and it works.
And if you are referencing the path absolutely, you'll have trouble going between one OS and another if you do something silly like hard-code the locations.
What you want to do is check out System.getProperties() and look for file.separator. The static File.pathSeprator will also get you there.
This will allow you to build a path that is native for whatever system you're running on.
(If indeed that is the problem. Sometimes I like to get the current directory just to make sure the directory I think I'm running in is the directory I'm really running in.)
Check your permissions. If you (or rather, the user that the Java process is running under) doesn't have appropriate permissions to read the file, for example, you would get this error message.
This is a typical Windows -> Linux migration problem. What does ls -l src/conf.properties show when run from a prompt?
Additionally, check capitalisation. Windows isn't case-sensitive, so if the file was actually called e.g. CONF.properties it would still be found, whereas the two would be considered different files on Linux.
You should check the working directory of your application. Perhaps it is not the one you assume and that's why 'src' directory is not present.
An easy check for this is to try the absolute path (only for debugging!).
I would check your slashes, windows often uses '\' vs linux's '/' for file paths.
EDIT: Since your path looks fine, maybe file permissions or executing path of the app is different?
check your slashes and colons
in my case i set my PS1 to following value
PS1='\n[\e[1;32m]$SYSNAME(\u)#[\e[1;33m]\w [\e[1;36m](\d \T) [!]\e[0m]\n\$ '
i am trying to read from the env .such as system.getenv
Java was throwing exception
java.lang.IllegalArgumentException: Malformed \uxxxx encoding
Try the double slash, after doing things in JBoss I often had to refactor my code to use the double slashes

Categories

Resources