timout on readLine

Hello,
   How would you write java code to timeout on a call to BufferedReader.ReadLine() ?

I would like to make the call and if nothing comes back in a minute then terminiate the read.


LVL 1
calvinrsmithAsked:
Who is Participating?

[Webinar] Streamline your web hosting managementRegister Today

x
 
imladrisConnect With a Mentor Commented:
Do the readLine in a separate thread. Then, when the main thread has waited long enough, it can carry on. It may want to kill the "read" thread. This can be done with the stop method, or (since stop has been deprecated in 1.2) by calling interrupt and the read thread catching the InterruptedException.
0
 
mbormannCommented:
readymade code,pls look and use it...
Notice that this code is JDK1.2 compliant ,I am closing the underlying InputStream in ,so

if you have to close the socket read/write
you have to close() the underlying input/output stream.
Get a reference like
InputStream in=sock.getInputStream();
Use in program like
BufferedReader buff = new BufferedReader(new InputStreamReader(sock.getInputStream()));

The reason is that both read() and close() are synchronized in
Wrapper classes like BufferedReader & BufferedInputStream in JDK 1.2
& up ,so this above program will only work in JDK 1.1.

Translated into simpler terms when you call close() ,it will block
forever.

So use

in.close() instead of buff.close()

public class AsyncReadBuffer
{
    private StringBuffer result;

    public AsyncReadBuffer()
    {
        result = new StringBuffer(500);
    }

    //Get the string already read from the socket so far.
    //This method is used by the Applet or external thread
    //to obtain the data in a synchronous manner.
    public synchronized String getResult()
    {
        String retval = result.toString();
        result.setLength(0);
        return retval;
    }

    //Put new data into the buffer to be returned
    //by the getResult method
    public synchronized void update(char c)
    {
        result.append(c);
    }

public synchronized void update(String s)
    {
        result.append(s);
    }
}


import java.util.*;
import java.io.*;
import java.net.*;

public class ClientHandler extends Thread
{
    private InputStream in;
    private BufferedReader buff;
    private Socket sock;
    private AsyncReadBuffer aReader;
    private boolean isDone = false;
    private Object lock = new Object();
private int timeout;
private boolean isReadLineMode=false;

    class ClientHandlerThread extends Thread
    {
        public void run()
        {
            String s;
int c;
            try
            {
if(isReadLineMode)
{
while ((s = buff.readLine()) != null)
    aReader.update(s);
}
else
{
while ((c = buff.read()) != -1)
    aReader.update((char)c);
}
}
            catch (Exception e)
            {}

            isDone = true;
            synchronized(lock)
            {
                lock.notify();
            }
        }
    }

    public ClientHandler(AsyncReadBuffer a, Socket s,int t,boolean isReadLine)throws Exception
    {
this.aReader=a;
        this.sock=s;
this.isReadLineMode=isReadLine;
if(t<0 || t==Integer.MAX_VALUE)
throw new Exception("Specify correct timeout value");
this.timeout=t;
    }

    public void run()
    {
try
{
    in=sock.getInputStream();
    this.buff = new BufferedReader(new InputStreamReader(in));
    Thread t = new ClientHandlerThread();
    t.start();
    synchronized(lock)
    {
        while (!isDone)
        {
            try
            {
                lock.wait(this.timeout);
            }
            catch (InterruptedException ie)
            {
                isDone = true;
                try
                {
                    t.interrupt();
                    this.in.close();this.in=null;
                    this.sock.close();this.sock=null;
                }
                catch (Exception e)
                {}
            }
        }
    }
}
catch(Throwable t)
{
System.out.println("Caught Exception during run() in timeout main thread");
t.printStackTrace();
}
}
}
0
 
Jim CakalicSenior Developer/ArchitectCommented:
I've been working on trying to find a non-threaded solution. After a good bit of experimentation, I determined that the code appearing below SHOULD work. Unfortunately, it does not work on win32 systems because the BufferedReader implementation -- at least in jdk1.3rc2 -- does not correctly (or at least to my liking) implement the semantics of readLine().

The API docs for readLine state: "Read a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed." This gives one (one being me in this case) the impression that the entire line-termination sequence will consumed. On win32 systems, line-termination is "\r\n", two characters. Unfortunately, the implementation does not consume both characters immediately. It finds the carriage return but leaves the newline in the buffer. As a result, ready() returns true but the subsequent read blocks when the only content in the buffer is the second half of the win32 line termination sequence.

This same problem does not appear to plague the InputStreamReader so it would be possible to use the technique implemented below with that reader. Perhaps I should provide a readLine() implementation for InputStreamReader? If anyone is interested, let me know.

Here is the correct but, on win32 systems, non-working code:

---------- TimedReader.java ----------
import java.io.InputStreamReader;
//import java.io.BufferedReader;
import java.io.Reader;
import java.io.IOException;

public class TimedReader {
    public static void main(String[] args) {
        InputStreamReader isr = new InputStreamReader(System.in);
        BufferedReader in = new BufferedReader(isr);
        while (true) {
            try {
                System.out.print("Enter a String: ");
                if (ready(in, 5000)) {
                    String s = in.readLine();
                } else {
                    System.out.println("timeout");
                }
            } catch (IOException e) {
                // ignore until we know what to do
            }
        }
    }

    public static boolean ready(Reader in, long timeout) throws IOException {
        while (true) {
            long now = System.currentTimeMillis();
            try {
                while (in.ready() == false && timeout > 0) {
                    Thread.sleep(100);
                    timeout -= 100;
                }
                return in.ready();
            } catch (IOException e) {
                throw e;
            } catch (Exception e) {
                // ignore
            } finally {
                // adjust timer by length of last nap
                timeout -= System.currentTimeMillis() - now;
            }
        }
    }
}
---------- end ----------

Since you didn't state your platform, this code may work just fine for you. I hope so. I have a modified version of BufferedReader.java which fixes the problem -- but I don't know how robust that fix is or whether it would prevent this scenario in all circumstances.

Best regards,
Jim Cakalic
0
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
mbormannCommented:
I think Jim ur code would work on JDK 1.2.2 ,what do you think ?

Have they introduced a bug in JDK1.3?
0
 
Jim CakalicSenior Developer/ArchitectCommented:
Amit, if you have a previous version of the JDK, would you care to give it a try? I only have 1.3 at my disposal presently.

You may be right, though, about changed behavior. In Harold's Java I/O book from O'Reilly, he states that BufferedReader.readLine() has "the potential to hang on a long carriage return that ends the stream. This problem is especially acute on networked connections, where readLine() should never be used." Browsing the bug database at JDC, I found quite a few complaints about the hanging carriage return problem. Perhaps they introduced a 'fix' in 1.3. Again, I don't have what I need to check this out so any help would be appreciated.

Jim
0
 
Jim CakalicSenior Developer/ArchitectCommented:
Just realized that, since I have JBuilder 3 I have jdk1.2.2 installed. Found the source and the code for BufferedReader.readLine() has been changed. Compiled and tested the old code with my program above and it works!!!!! I'm not going crazy after all.

Guess I'll post something on the bug parade. Doubt if it will get into jdk1.3, though. They're already in rc2.

Jim
0
 
calvinrsmithAuthor Commented:
  I'm still using JDK1.1.8 I'll give the above ideas a try and let you know what I find.  
0
 
mbormannCommented:
calvinrsmith,
Oops sorry I realized that I had cut-pasted wrong explanation in my long comment above. But the code is perfect. You can use that too .

But I think u would be happy with Jim's solution.
:)

Jim , it works. And I will post this code sometime later today to the question from which I cut-pasted the original code.

Cheers fellows
0
 
Jim CakalicSenior Developer/ArchitectCommented:
Those guys at Sun are fast. I submitted a bug report this afternoon and already got back a reply that it is considered to be a new defect and has been assigned id 4329985. It may not show up on the bug parade for several days according to the email I received.
0
 
mbormannCommented:
Wow ! Yes I also reported a 'bug' but it was submitted just sometime b4 I sent them a mail and they gave the same reply.

They work round the clock is what I think,must be having guys all over the world in differect time zones.

Cheers Jim
0
 
calvinrsmithAuthor Commented:
jim_cakalic,
  I tested your code with jdk1.1.8 on win98 and found that in.ready() always returns true, even if your ready function just started.
0
 
Jim CakalicSenior Developer/ArchitectCommented:
Calvin, I don't know what to tell you about this other than jdk1.1.8 is notoriously buggy. Running in win95 or win98 doesn't help. Is it possible for you to use a more recent release of the jdk? Any of the 1.2 releases would be better. Frankly, many suggestions I have seen are to stick with 1.1.7 if 1.2 is not an option.

I've got IBM's port of 1.1.8 to AIX that I can look at. Barring that, I'd have to download and unpack (if not install) another jdk version. Painful but I'd consider doing it.

Anybody else have any ideas?
Jim
0
 
leach072899Commented:
I checked this code on VisualAge with JDK 1.1.7 - works perfectly.
0
 
Jim CakalicSenior Developer/ArchitectCommented:
Hi, leach.

This one has certainly been around for awhile, hasn't it? Thanks for testing it. I figured Calvin had abandoned the question. Looked on the JDC and the bug has 21 votes but apparently never got fixed in the 1.3 release.

Jim
0
 
leach072899Commented:
Hi Jim,
Well it was about something i was looking for.
Moving to Java 2 (any version) looks a bit farther then i want it to be, for us VisualAge users.
My problem is a bit more trickier so i opened another question on it - see if you can answer.

lee
0
 
leach072899Commented:
I said it runs with JDK 1.1.7 - i must retrackt that.
At least on the IBM implementation the timeout works perfectly when needed - but when all is well - using the timeout after the connection was closed (from either end) will result in a timeout condition. And there is no way (that i found yet) to distinguish the two cases: timeout and connection closed.

lee
0
All Courses

From novice to tech pro — start learning today.