Concepts / Reading Text from Input Streams

Reading Text from Input Streams

A URL object identifies a resource; calling openConnection() on it returns a URLConnection.

  • Programming

A Web Address Is Not Yet a Data Stream

Suppose you want your program to read the text stored at a web address. It seems like one step should be enough: give the program the address and ask for the text. Most Java networking code that reads from a web resource does not work that way. It follows a short chain of three statements. The first turns a text address into a URL object. The second asks that URL object to set up a connection. The third asks that connection for a stream of incoming data. Each statement hands its result to the next one, and that handoff is the key idea of this article.

What do you think happens?

Before reading further: after the first two statements run, do you think the program already has a stream of text to read? What would you expect the second variable to hold?

  • Yes, the URL object already holds the text

  • No, the second variable holds a connection to the resource

  • No, the second variable holds the text as a string

Reveal answer
Answer:

No, the second variable holds a connection to the resource, which is a URLConnection object.

A URL object identifies a resource, and calling openConnection() on it returns a URLConnection. The stream of data comes from a later step, so the program is not yet reading any text at this point.

Tracing the Three Variables

The source describes the pattern as three statements that run in order, with each variable holding the result of the call on the line above it. The following code is a generated illustration of that chain. The address used here is only a placeholder for a real text resource.

Code walkthrough
URL url = new URL("https://example.com/notes.txt");
URLConnection connection = url.openConnection();
InputStream stream = connection.getInputStream();
  1. 1
    After line 1, the program holds a URL object. That object names the resource but gives no channel to it.
  2. 2
    After line 2, the program holds a URLConnection. This is the object that the program works with when it needs to deal with the resource.
  3. 3
    After line 3, the program holds an InputStream named stream. Its contents come from the resource that the original URL identifies. This is the object your code will read from.
  • Line 1 turns the text address into a URL object named url
  • Line 2 calls openConnection() on url and stores the returned URLConnection in connection
  • Line 3 calls getInputStream() on connection and stores the returned InputStream in stream

Notice that each variable has a different type, and each type is responsible for something different. Reading the code line by line, the state of the program changes after every statement. When a result looks wrong later on, you can return to this list of variables and ask which one has the wrong value.

Why a URL Alone Cannot Be Read

A URL object by itself identifies a resource, but it does not give you a channel to that resource. The source is direct on this point: once you have a valid URL object, you call its openConnection() method to set up a connection, and that call returns a URLConnection object. The URL and the URLConnection are two different objects with two different jobs. The URL names the resource. The URLConnection is what you use to work with that resource.

ObjectRole in the chainWhat it gives your program
URLIdentifies the resourceA name and address for the resource, with no channel to it
URLConnectionWorks with the resourceThe object created by openConnection(), used to obtain the stream
InputStreamCarries the incoming dataThe stream returned by getInputStream(), which your code reads from

The three objects in the chain and what each one enables

The source also notes that a single URL can be used to create a connection, so the URL object is not consumed by the connection. The relationship between them is that the URL supplies the address, and openConnection() uses that address to build the connection. The connection is the object the program talks to next.

The Path the Text Takes

The text does not appear in your program by magic. It starts at the remote resource and passes through the URL, the URLConnection, and the InputStream before your code sees it. The diagram below traces that path from left to right. Each hop is handled by a different object, and each object is the one that the next hop depends on.

Remote resourcetext at the addressURL objectnames the resourceURLConnectionfrom openConnection()InputStreamfrom getInputStream()Wrapper (optional)BufferedReader or ScannerProgramreads the textidentified byopenConnection()getInputStream()optionally wrappedreadread directly
How does the text travel from the remote server into the program, and which object is responsible at each hop?

The diagram shows the wrapping step as optional. The source describes wrapping as something you may do, so the stream from getInputStream() can also be used directly. Wrapping in a BufferedReader or a Scanner gives you a more convenient way to pull text out of the stream. Both wrappers still depend on the stream the connection provided, so they cannot replace the connection step.

Sequence of Calls Across Objects

The order of the method calls matters because each call returns an object that the next call needs. The sequence below reads from top to bottom. The program first sends a message to the URL object, then uses the connection that comes back as the object it talks to in its next message. The chain of objects follows the chain of method calls.

ProgramURL objectURLConnectionInputStreamopenConnection()returns URLConnectiongetInputStream()returns InputStreamread text
In what order are the methods called, and what object does each call return to the next step?

Read the diagram as a rule for tracing. Each arrow labeled with a method name is a call, and each return arrow shows the object that call produced. If the program is calling a method on the wrong object, or on an object of the wrong type, the chain breaks at that point. Checking each return type is a practical way to find that break.

Where IOException Can Strike

The source states that openConnection() and getInputStream() can both throw exceptions of type IOException. It also states that reading from the stream can generate exceptions. The failure points are therefore not limited to one line. Any of the three steps in the chain, and the reads that follow them, may fail. The flowchart below shows how a failure at any of these points stops the normal path and reports an IOException instead.

Create URL objecttext addressopenConnection()may throw IOExceptiongetInputStream()may throw IOExceptionRead from streammay throw IOExceptionText obtainednormal pathIOExceptionfailure pathnextsucceedssucceedssucceedsfailsfailsfails
Which steps in the program can fail with IOException, and what path does execution take when one of them fails?

Mistakes Beginners Make with the Chain

  • Common mistake

    Treating the URL object as if it can be read directly

    What it looks like

    Expecting the URL variable to produce text when it is passed to a reading method

    Why it fails

    A URL object identifies a resource but does not give you a channel to it.

    Do this instead

    Call openConnection() on the URL to get a URLConnection, then call getInputStream() on that connection.

  • Common mistake

    Calling getInputStream() on the URL object

    What it looks like

    Writing a getInputStream() call on the variable that holds the URL

    Why it fails

    The source places getInputStream() on the URLConnection, not on the URL.

    Do this instead

    Call getInputStream() on the variable that holds the URLConnection.

  • Common mistake

    Assuming only one line can fail

    Why it fails

    The source states that openConnection(), getInputStream(), and reading from the stream can each throw IOException.

    Do this instead

    Treat every link in the chain, and every read that follows, as a possible failure point.

  • Common mistake

    Wrapping the stream and forgetting where the data came from

    Why it fails

    A BufferedReader or Scanner still depends on the stream the connection provided.

    Do this instead

    Remember that the data originates at the resource and passes through the connection and the stream before reaching the wrapper.

Practice Tracing a Failure

MEDIUM

A program runs the three-statement chain for a text resource. It prints a message showing that the program got past the URL step and the openConnection() step, but it never shows any text. Using the flowchart as a guide, which link in the chain is the most likely place for the IOException, and what question would you ask first to confirm it?

Hints
  • Ask which statement was running when the output stopped.
  • Look at which object that statement was called on and what it was expected to return.

Key Takeaways

  • A URL object identifies a resource, and calling openConnection() on it returns a URLConnection that works with that resource.
  • Calling getInputStream() on the URLConnection creates the InputStream your code reads from, and that stream can be read directly or wrapped in a BufferedReader or a Scanner.
  • The chain of three statements matters because each statement hands its returned object to the next one, so the chain of objects follows the chain of method calls.
  • openConnection(), getInputStream(), and reading from the stream can each throw IOException, so any link in the chain may be where a failure occurs.
  • To locate a failure, check which statement produced the wrong value, which object it was called on, and what type that object returned.