Schera Haze
Schera Haze

Reputation: 43

Timelimit for valid Java input without System.exit

I have a question how to implement a variation of whats found here:

Set Time Limit on User Input (Scanner) Java

In my case, I would like to ignore the input if the Timelimit is reached while keeping the program alive.

String str = "";
TimerTask task = new TimerTask(){
  public void run(){
     if(str.equals("")){
         System.out.println("No Valid Input detected");
         //TimerTask should end here along with the Input
         //Other example has System.exit which will also terminate the 
         //entire program
     }else {
         // still keep the program alive. The condition itself isn't 
         //important.
     }
}

Timer timer = new Timer();
timer.schedule(task, 10*1000);
Scanner scanner = new Scanner(System.in);

do{
      System.out.println("Type a message");
      str = scanner.nextLine();
}while(!str.equals("hello"));

timer.cancel();    

REACHED HERE!

If the input is given within 10 seconds(and it's valid), the loop ends and the task is canceled, which is perfect. However, if the input is not valid and the timer ends, I would like for it to stop asking for input and skip to the "REACHED HERE" position. Is that even possible?

Upvotes: 4

Views: 433

Answers (1)

df778899
df778899

Reputation: 10931

As @Sedrick mentions, the simplest solution to this is a second thread. The problem is that reading from System.in is blocking. Clearly the example you linked to solves that problem with a System.exit(), but that's too extreme for your case.

Another spin on it might be to use a Deque (double-ended queue) to relay the input, with the timeout on there:

BlockingDeque<String> deque = new LinkedBlockingDeque<>();

new Thread(() -> {
    Scanner scanner = new Scanner(System.in);
    String input;
    do {
        System.out.println("Type a message");
        input = scanner.nextLine();
        deque.add(input);
    } while (!input.equals("hello"));
}).start();

String str;
do {
    str = deque.poll(10, TimeUnit.SECONDS);
} while (str != null && !str.equals("hello"));

System.out.println("REACHED HERE!");

Expanded answer...

The idea above was to only create the thread once, and re-use the deque as the proxy for System.in. But, in the thread, the read from System.in will always be blocking - there's no clean way to interrupt the thread, short of System.exit().

This can be refined a bit though. Firstly, if the thread is marked as a daemon thread, this allows the JVM to shutdown around it still. E.g. if the main() method completes, the JVM will exit cleanly too:

Thread thread = new Thread(() -> {
    ...
});
thread.setDaemon(true);
thread.start();

However, by using InputStream.available(), it is possible to poll for waiting input. This then makes it possible to interrupt the thread cleanly:

BlockingDeque<String> deque = new LinkedBlockingDeque<>();

Thread thread = new Thread(() -> {
    Scanner scanner = new Scanner(System.in);
    String input;
    try {
        do {
            if (System.in.available() > 0) {
                input = scanner.nextLine();
                deque.add(input);
            } else
                try {
                    Thread.sleep(50);
                } catch (InterruptedException ex) {
                    System.err.println("Thread stopped");
                    break;
                }
        } while (true);
    } catch (IOException ex) {
        ex.printStackTrace();
    }
});
thread.start();

System.out.println("Type a message");
String str;
do {
    str = deque.poll(10, TimeUnit.SECONDS);
} while (str != null && !str.equals("hello"));

System.out.println("REACHED HERE!");

thread.interrupt();

It looks like there's some risk with the user typing a few letters without a line feed. At the moment that would still hang, but this didn't happen on Windows - apparently data from the command window is only released to System.in line by line.

Upvotes: 2

Related Questions