Reading Text from Input Streams
A URL object identifies a resource; calling openConnection() on it returns a URLConnection.
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?
Reveal 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.
URL url = new URL("https://example.com/notes.txt");
URLConnection connection = url.openConnection();
InputStream stream = connection.getInputStream();- 1After line 1, the program holds a URL object. That object names the resource but gives no channel to it.
- 2After line 2, the program holds a URLConnection. This is the object that the program works with when it needs to deal with the resource.
- 3After line 3, the program holds an
InputStreamnamed 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 returnedInputStreamin 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.
| Object | Role in the chain | What it gives your program |
|---|---|---|
| URL | Identifies the resource | A name and address for the resource, with no channel to it |
| URLConnection | Works with the resource | The object created by openConnection(), used to obtain the stream |
| InputStream | Carries the incoming data | The 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.
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.
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.
Mistakes Beginners Make with the Chain
Common mistake Treating the URL object as if it can be read directly
What it looks likeExpecting the URL variable to produce text when it is passed to a reading method
Why it failsA URL object identifies a resource but does not give you a channel to it.
Do this insteadCall 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 likeWriting a getInputStream() call on the variable that holds the URL
Why it failsThe source places
getInputStream()on the URLConnection, not on the URL.Do this insteadCall getInputStream() on the variable that holds the URLConnection.
Common mistake Assuming only one line can fail
Why it failsThe source states that
openConnection(),getInputStream(), and reading from the stream can each throw IOException.Do this insteadTreat 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 failsA
BufferedReaderor Scanner still depends on the stream the connection provided.Do this insteadRemember that the data originates at the resource and passes through the connection and the stream before reaching the wrapper.
Practice Tracing a Failure
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 theInputStreamyour code reads from, and that stream can be read directly or wrapped in aBufferedReaderor 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.