2

This problem has me stumped. I've written a method which is called from inside a loop. The first time it works perfectly, after that it just hangs.

This is the method:

public static String promptUser(){
    String path = "";
    Scanner reader = new Scanner(System.in);  

    while (!path.contains(ROOT_FOLDER)){
        System.out.println(String.format("Please paste in the directory path from WinSCP, including %s: ", ROOT_FOLDER));

        while (true) {
            if (reader.hasNext()){
                path = reader.nextLine();
                break;
            }
        }
    }
    reader.close();
    return path;
}

And this is the loop

        while (true) {
        try {
            listFiles(promptUser());
            break;
        } 
        catch (Exception e) {
            e.printStackTrace();
            System.err.println(e.getMessage());
        }
    }

The first time I run it, it will prompt the user as many times as it takes to get the info we need (a directory path). It then sends a request for that to an FTP server and if that path does not exist I want it to continue prompting the user. But my debugger is telling me on the second go round it just hangs on:

        if (reader.hasNext()){ 

No amount of hitting the enter key gets it to continue. On the first invocation of promptUser I could enter something without the root folder and it would just keep asking until it got the root folder. So why doesn't it do that on the second invocation?

What is going on?

clickcell
  • 439
  • 3
  • 6
  • 20

3 Answers3

2

The first time you call promptUser it does the following:

  1. Create a Scanner that wraps System.in.
  2. Call hashNext which blocks until there is data to be read.
  3. Read a single line.
  4. Close the Scanner.
  5. Return the line.

The problem is step 4. When you close the Scanner, you also close System.in.

Next time you call promptUser

  1. Create another Scanner that wraps System.in. System.in is closed at this point.
  2. Call hashNext() which immediately returns false ... because the stream wrapped by the Scanner is closed.
  3. Repeat 2. ad nauseam. You have an infinite loop.

Solution: don't close System.in or a Scanner that wraps it. And don't just create a second Scanner that wraps System.in. Instead, reuse the original Scanner.


But is it necessary to close the scanner at some point?

You should never need to close a Scanner that wraps (the original) System.in stream. The reasons that we close streams are:

  • to cause buffered data to be written (for an output stream), and
  • to avoid file descriptor leaks.

The problem with leaking file descriptors is that a process (i.e. your JVM) can only have a certain number of files open at a time. If you leak file descriptors, you may find that attempts to open files start to fail with unexpected IOExceptions.

In the case of System.in there is no data to be written. Since you can't open System.in again (because you don't know what it is connected to) at most one file descriptor can be "leaked", so this is not a problem.

Eclipse warns that I should.

The Eclipse heuristics about closing scanners, etcetera are rather simplistic. In this case, the warning is probably a false alarm, though I would need to see the code to be sure.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Thanks, this has fixed my problem and you explained the issue much better than the other questions I found on the subject. But is it necessary to close the scanner at some point? Eclipse warns that I should. – clickcell Apr 20 '16 at 11:03
1

You should not use break inside the loop when you fetch the next element. This causes your while(true) loop to exit. This means that you get the first item and finish.

You should change it to something like this:

while(reader.hasNext()) {
    path = reader.nextLine();
    // do something with the path here...
}
GHajba
  • 3,665
  • 5
  • 25
  • 35
  • This is the first thing I tried, but it gives me a different problem. Once again it works correctly on the first invocation, but on the second invocation it will just keep printing out the message to the console over and over without waiting for any input. That is why I wrapped it in two loops, it doesn't matter if the while(true) loop exists because it's inside another loop that waits for the correct input. I really can't understand why the same method seems to have two different behaviours. – clickcell Apr 20 '16 at 10:14
-1

Read the documentation of hasNext()

Returns true if this scanner has another token in its input. This method may block while waiting for input to scan. The scanner does not advance past any input. Returns: true if and only if this scanner has another token

So, hasNext() is waiting for you to enter some value. It'll wait till it finds a single string entered by user.

Mangu Singh Rajpurohit
  • 10,806
  • 4
  • 68
  • 97
  • It does the first time I call promptUser, but the second time, I can hit Return all day long and nothing happens. – clickcell Apr 20 '16 at 10:20