2

I have a simple line of code that should print out first line of a file:

System.out.println(new BufferedReader(new FileReader("file")).readLine());

The class file with this line is located in c:/users/my/project. A file named "file" exists in c:/users/my/project. If I open CMD, navigate to c:/users/my/project and run

java MyClass 

The first line is printed out and everything is fine.

But if I navigate to C:/ in CMD, and run

java -Duser.dir=c:/users/my/project MyClass

I get a response that file "file" could not be found.

If I move "file" to c:/ and run the same command again then it is found.

As I understand it, changing the user.dir should be equivalent to me being in the folder that it points to but that doesent seem to be true. The class file is found in user.dir, but files are still found in the folder I ran the command in, not the folder that user.dir points to.

Why is this?

geco17
  • 5,152
  • 3
  • 21
  • 38
donald tbd
  • 51
  • 5
  • 1
    Could you try just before the System.out.println: `System.out.println("user.dir: " + System.getProperty("user.dir")); System.setProperty("user.dir", "c:/users/my/project");` – Joop Eggen Jul 02 '18 at 11:52
  • try -Duser.dir=/users/my/project – Boris Chistov Jul 02 '18 at 11:54
  • @JoopEggen Added those lines, it prints out the correct user.dir. Output: user.dir: c:/users/my/project Exception in thread "main" java.io.FileNotFoundException: file (The system cannot find the file specified) – donald tbd Jul 02 '18 at 14:05
  • @BorisChistov tried it. Class file is found as it was previously, but i still get file not found exception: Exception in thread "main" java.io.FileNotFoundException: file (The system cannot find the file specified) – donald tbd Jul 02 '18 at 14:07
  • Is there any specific reason you use the old file API instead of NIO? (`Files`, `Paths`, `Path`) – Zabuzard Jul 02 '18 at 16:38

2 Answers2

2

If you navigate to another directory and try to run your program with a different user.dir, you also have to set the classpath. The error you get is because it can't find the class, not because it can't find the file.

Try

java -Duser.dir=c:/users/my/project -classpath c:/users/my/project MyClass

Regarding the user.dir property, have a look at the documentation for the File class. It states

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.

EDIT

I did a test on my machine and found that file reader looks in the directory that the application was launched from (which may not coincide with the user.dir property). See below:

public static void main(String...args) {
    try {
        System.out.println("abs path to test.txt : " + new java.io.File("test.txt").getAbsolutePath());
        System.out.println("user home : " + System.getProperty("user.home"));
        System.out.println("user.dir : " + System.getProperty("user.dir"));
        System.out.println("running from : " + new java.io.File(".").getAbsolutePath());

        System.out.println(new java.io.BufferedReader(new java.io.FileReader("test.txt")).readLine());
    } catch (Exception e) {
        System.out.println("Not found");
    }
}

Launching this from a directory that doesn't have the file in it I get

C:\Users>java -Duser.home=C:\Users\william -Duser.dir=C:\Users\william -classpath C:\Users\william\Desktop Test
abs path to test.txt : C:\Users\william\test.txt
user home : C:\Users\william
user.dir : C:\Users\william
running from : C:\Users\william\.
Not found

Whereas launching it from a directory that has the file I'm looking for I get

C:\Users\william>java -Duser.home=C:\Users\william -Duser.dir=C:\Users\william -classpath C:\Users\william\Desktop Test
abs path to test.txt : C:\Users\william\test.txt
user home : C:\Users\william
user.dir : C:\Users\william
running from : C:\Users\william\.
hello world

ANOTHER EDIT

I'm using the openjdk 8, so in my case the logic is as follows, from its source:

FileReader constructor with String arg

public FileReader(String fileName) throws FileNotFoundException {
    super(new FileInputStream(fileName));
}

FileInputStream constructors

public FileInputStream(String name) throws FileNotFoundException {
    this(name != null ? new File(name) : null);
}

public FileInputStream(File file) throws FileNotFoundException {
    String name = (file != null ? file.getPath() : null);
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkRead(name);
    }
    if (name == null) {
        throw new NullPointerException();
    }
    if (file.isInvalid()) {
        throw new FileNotFoundException("Invalid file path");
    }
    fd = new FileDescriptor();
    fd.attach(this);
    path = name;
    open(name);
}

FileInputStream open(String)

private void open(String name) throws FileNotFoundException {
    open0(name);
}

The open0(String) method is a native call (see below)

private native void open0(String name) throws FileNotFoundException;

Digging deeper, into the native call in FileInputStream.c

JNIEXPORT void JNICALL
Java_java_io_FileInputStream_open0(JNIEnv *env, jobject this, jstring path) {
    fileOpen(env, this, path, fis_fd, O_RDONLY);
}

Therefore the name is what becomes the path that the native fileOpen method will use. This translates to file.getPath(), which is different from file.getAbsolutePath().

file.getPath() with file as new File("test.txt") returns test.txt, which may not be found.

That's the best I can do, I hope it helps.

See this bug report and this question. In short, setting the user.dir via the command line is bad news.

geco17
  • 5,152
  • 3
  • 21
  • 38
  • I dont think you are correct. The error i got was not because of class not found, it was file not found: Exception in thread "main" java.io.FileNotFoundException: file (The system cannot find the file specified). Also I added code to print out all entries of classpath to beginning of my code. When launching the class with my command: java -Duser.dir=c:/users/my/project MyClass from c:/ then only classpath entry is c:/users/my/project so it seems classpath is set to user.dir automatically. – donald tbd Jul 02 '18 at 14:02
  • @donaldtbd Interesting question, I did some tests and updated the answer – geco17 Jul 02 '18 at 16:36
  • So it seems to a bug/feature of FileReader? – donald tbd Jul 02 '18 at 18:01
  • @donaldtbd I updated the answer again, it's just the expected behaviour, at least with openjdk 8 – geco17 Jul 02 '18 at 19:19
  • But that "expected behaviour" goes against the documentation that states: "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." – donald tbd Jul 03 '18 at 06:24
  • @donaldtbd I found some more info, updated the question again. Basically you shouldn't count on setting `user.dir` from the command line. – geco17 Jul 03 '18 at 06:33
0

You are getting FileNotFound exception because the program is not able to find the file you are trying to read (In your case fileName is also "file").

The problem is coming because you have defined only the file name not the path. You should either give absolute or relative path for it to make work from any directory. In absence of the path definition the program will try to read from a file in current directory from where your command is executing.

Let's dive into the details: you have the text file in path : c:/users/my/project/file You have your Java file in path : c:/users/my/project/MyClass

now when you run inside c:/users/my/project directory , you get the expected output because the text file "file" is defined in this directory only.

when you switch to c: and try to run the program, you get exception because it is trying to find "c:/file" which does not exist.

try to change the line to mention absolute path instead:

System.out.println(new BufferedReader(new FileReader("c:/users/my/project/file")).readLine());

Priya Jain
  • 795
  • 5
  • 15
  • Thats my point. It should not be looking for the file in c:/file because of user.dir property i defined. If you read documentation of file: "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" FileReader is in java.io package, and my user.dir is set to c:/users/my/project so i expect it to look for the file there, but it doesent. – donald tbd Jul 03 '18 at 06:15
  • check https://stackoverflow.com/questions/840190/changing-the-current-working-directory-in-java/840229#840229 it seems only changing the user.dir will not help. you need to give the user.dir path as relative path in program too : **System.out.println(new BufferedReader(new FileReader(new File(System.getProperty("user.dir"), "file1"))).readLine())** – Priya Jain Jul 03 '18 at 22:25