2

*Note: To clarify, maybe this question wasn't quite clear on the InputStream being "alive." The connection to the telnet weather service (see below for link) is kept open. The goal is to get all lines coming from the server.*

Building from the sample Apache WeatherTelnet code, I'm using InputStream.read to output the server results (inspired by a javamex tutorial idiom) one char at a time, using the chars method:

thufir@dur:~$ 
thufir@dur:~$ java -jar NetBeansProjects/Teln/dist/Teln.jar 
------------------------------------------------------------------------------
*               Welcome to THE WEATHER UNDERGROUND telnet service!            *
------------------------------------------------------------------------------
*                                                                            *
*   National Weather Service information provided by Alden Electronics, Inc. *
*    and updated each minute as reports come in over our data feed.          *
*                                                                            *
*   **Note: If you cannot get past this opening screen, you must use a       *
*   different version of the "telnet" program--some of the ones for IBM      *
*   compatible PC's have a bug that prevents proper connection.              *
*                                                                            *
*           comments: jmasters@wunderground.com                              *
------------------------------------------------------------------------------

Press Return to continue:
^Cthufir@dur:~$ 

which is the desired output. However, reading the InputStream with a BufferedReader results in dropping the last line. (Or, at least, it's not printed to the console.) Bad output, using lines method:

thufir@dur:~$ 
thufir@dur:~$ java -jar NetBeansProjects/Teln/dist/Teln.jar 

------------------------------------------------------------------------------

*               Welcome to THE WEATHER UNDERGROUND telnet service!            *

------------------------------------------------------------------------------

*                                                                            *

*   National Weather Service information provided by Alden Electronics, Inc. *

*    and updated each minute as reports come in over our data feed.          *

*                                                                            *

*   **Note: If you cannot get past this opening screen, you must use a       *

*   different version of the "telnet" program--some of the ones for IBM      *

*   compatible PC's have a bug that prevents proper connection.              *

*                                                                            *

*           comments: jmasters@wunderground.com                              *

------------------------------------------------------------------------------



^Cthufir@dur:~$ 
thufir@dur:~$ 

StreamReadercode:

package teln;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class StreamReader {

    private StreamReader() {
    }

    StreamReader(InputStream inputStream) throws IOException {
        lines(inputStream);
    }

    private void chars(InputStream inputStream) throws IOException {
        do {
            char ch = (char) inputStream.read();
            System.out.print(ch);
        } while (true);
    }

    private void lines(InputStream inputStream) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        String line = "";
        do {
            System.out.println(line);
        } while ((line = br.readLine()) != null);

        System.out.println(line);

    }
}

Presumably that last line is "null" somehow? Can the logic be altered so that the final line is printed from within lines just as it is from chars?

Thufir
  • 8,216
  • 28
  • 125
  • 273
  • Um, which line are you referring to? I'm not seeing any difference in your outputs, other than blank lines in the second version... unless you meant the "Press Return to continue" which looks like it may well not be in the input data at all. (Your `chars` method will continue until an exception is thrown, for example...) – Jon Skeet Aug 29 '13 at 18:18
  • 1
    reading line by line has this disadvantage, that line has to end with `\n` character, if it does not, it is not considered as a complete line. – Flash Thunder Aug 29 '13 at 18:18
  • @JohnKugelman: It would still be returned. I suspect there's something else going on here - unfortunately we don't have enough information about the input data to know what. A short but *complete* program demonstrating the problem would really help. – Jon Skeet Aug 29 '13 at 18:19
  • 1
    not it would not... telnet is such a lazy protocol that it does not end last line, so it is not being considered as complete... that's why most telnet implementations use `waitfor` routine – Flash Thunder Aug 29 '13 at 18:21
  • The `null` you see at the end is the cause of the last `System.out.println(line);` – Sotirios Delimanolis Aug 29 '13 at 18:25
  • @FlashThunder thanks, can you expand on what to waitfor? I'll look into that. I just want wait, I guess, for "no more data" being sent. At least naively that's my guess. I'll look into that! :) – Thufir Aug 29 '13 at 18:25
  • @JonSkeet exactly, the blank "press return" is missing. Adapted from the apache code linked in the question. – Thufir Aug 29 '13 at 18:28
  • @Thufir: Well what's it reading from? And why do you have a `while (true)` loop in `chars`? – Jon Skeet Aug 29 '13 at 18:32
  • @JonSkeet it's an Apache `TelnetClient` which returns an `InputStream`, very similar to the linked `WeatherTelnet` class. The while true loop comes from the linked tutorial. – Thufir Aug 29 '13 at 18:34
  • @Thufir: Right. That makes all the difference - because that means it's entirely likely that the server is just not closing the stream. – Jon Skeet Aug 29 '13 at 18:44
  • @JonSkeet exactly. The stream is, for these purposes, never closed. In that circumstance, I want to "get" all data from the server, including the last lines. Pardon for the confusion, I'll come back (in a bit) to try and fix the question to clarify that. – Thufir Aug 29 '13 at 18:50

4 Answers4

4

You haven't explained where the data is coming from, but I suspect it's coming from some source which doesn't have a line terminator at the end and doesn't close the stream.

If the stream were closed - e.g. by killing the connection, if this is a client/server app - then you would see the final line. BufferedReader will definitely return the last line of data even if it doesn't end with a line terminator, but only when it knows it's got to the end of the stream.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • the data is coming from http://svn.apache.org/repos/asf/commons/proper/net/tags/NET_1_0_0/src/java/examples/weatherTelnet.java well, the weather server which that sample uses. I've just broken it up a bit, but same idea. `TelnetClient` is used. Without killing the connection, I want to get the last line. I have some reading to do on this! Many ideas, thanks all :) – Thufir Aug 29 '13 at 19:07
  • 1
    BufferedReader.readLine() doesn't return until the end of line character(s) are sent. This means if you want to see a prompt before a new line, you have to read the characters yourself. – Peter Lawrey Aug 29 '13 at 19:16
3

As Jean mentioned, this is a shortcoming of using the BufferReader if you know your input never ends with either the linefeed '\n' or carriage return '\r' character. After your while loop you may want to use the ready() method to test if there is indeed more text after the last line read, and use one of the read() methods to pull in that remaining text. Or, substitute another text reader for BufferReader all together.

  • I see `ready` here: http://seb.citycolor.net/products/citycolorftp/doc/com/citycolor/net/telnet/TelnetReader.html#ready%28%29 but I don't see `ready` in http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#reset%28%29 or http://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/telnet/TelnetClient.html . – Thufir Aug 29 '13 at 20:22
  • http://stackoverflow.com/a/9260158/262852 explains it, I think. I can just read the chars, and check for 10 and 13 (newline or carrage (sp) return). – Thufir Aug 29 '13 at 20:39
2

A BufferedReader#readLine() reads the stream until it reaches either \n, \r, or the end of the stream, returning what it read up to then or null. You're not showing us something. Why are there new lines between lines in your output?

As for the null at the end of your output

private void lines(InputStream inputStream) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
    String line = "";
    do {
        System.out.println(line); // will print an empty line to start
    } while ((line = br.readLine()) != null);

    System.out.println(line); // will print null

}

Your loop should be

while ((line = br.readLine()) != null) {
    System.out.println(line);
}

to avoid printing the first empty one. Also, don't print the line outside the while loop. It will always be null, obviously (you wouldn't be outside the while otherwise).

Your chars() method is also looping forever.

It's very possible your server isn't closing the stream and so the underlying read calls that the BufferedReader makes aren't returning -1 to indicate EOF. Are you killing the application yourself?

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • I'm not sure why there are new lines between the lines in the output. I didn't notice at first, but tried switching to println, but that just mangled the output. Yes, I'm killing the application myself because it's meant to stay open indefinitely. (Like a MUD client.) The `chars` method is supposed to loop forever, it's from the linked tutorial. – Thufir Aug 29 '13 at 18:53
  • There isn't `null` at the end of the output. And the while loop is not wrong if he wants a blank line on the begin of the output. – Jean Waghetti Aug 29 '13 at 18:58
  • @Thufir Well there you go. Because the `readLine` call hasn't reached a `\n` or the end of the stream, it blocks. – Sotirios Delimanolis Aug 29 '13 at 18:58
  • @JeanWaghetti There's no `null` because the stream is blocked and so hasn't reached the line that executes and prints it. the last `println(line)` in his method will print `null`. Have you even tried running it? – Sotirios Delimanolis Aug 29 '13 at 18:59
  • @SotiriosDelimanolis Yes, I did. But you said "As for the null at the end of your output". There isn't. Although it would, if the application wasn't on the while loop. I didn't say the last `System.out.println` was correct. – Jean Waghetti Aug 29 '13 at 19:07
  • @Jean OP stated _Presumably that last line is "null" somehow_ and I originally thought that's what they were referring to. I see now that it was not. However it would still appear if the stream ended and didn't cause the `readLine()` call to block, so it's still valid. – Sotirios Delimanolis Aug 29 '13 at 19:08
  • I'll come back and put all the code up, but I don't think it's relevant. So far as I know, the stream is *open* and remains so, at least I hope so. See the `WeatherTelnet` client linked in the question to see the connection parameters, etc. – Thufir Aug 29 '13 at 19:11
  • 1
    @Thufir All that is fine, but do you understand what is going on? The `readLine()` method call is blocked, having read the last line but hasn't yet reached something to indicate to stop reading. Because of this the `println()` containing the last line is never printed and so you never see it. You then shutdown the application yourself, so you won't see it there either. – Sotirios Delimanolis Aug 29 '13 at 19:13
0

Ok, here goes the kludge:

package teln;

import static java.lang.System.out;
import java.io.IOException;
import java.io.InputStream;

public class StreamReader {

    private StreamReader() {
    }

    StreamReader(InputStream inputStream) throws IOException {
        printEachLine(inputStream);
    }

    private void printEachLine(InputStream inputStream) throws IOException {
        int foo = 0;
        char ch = 0;
        StringBuilder sb = new StringBuilder();
        out.println("lines..");
        boolean isEOL = false;  //err, need to set this or get rid of it
        do {
            foo = inputStream.read();
            ch = (char) foo;
            //out.print(foo);
            sb.append(ch);
            if ((ch == 10)) {
                out.print(sb);
                sb = new StringBuilder();
                foo = inputStream.read();
                ch = (char) foo;
                sb.append(ch);
                if (ch != 13) {
                    while ((255 > ch) && (ch >= 0)) {
                        sb = new StringBuilder();
                        foo = inputStream.read();
                        ch = (char) foo;
                        sb.append(ch);
                        out.print(sb);
                    }
                }
                sb.append(ch);
            }

        } while (!isEOL);

    }
}

this depends upon LF and CR following a specific pattern. LF, 10, is always followed by CR, 13, unless it's the first or last line. So, in that special case this code will look for the missing CR and identify that as needing special output.

However, it's a huge assumption and may be specific to this weather telnet service. Also, seems incredibly fragile.

For now, I'll go with this, but will keep looking for another answer.

Thufir
  • 8,216
  • 28
  • 125
  • 273