Search the Catalog
Java I/O

Java I/O

By Elliotte Rusty Harold
1st Edition March 1999
1-56592-485-1, Order Number: 4851
596 pages, $34.95

Chapter 5.
Network Streams

In this chapter:
URLs
URL Connections
Sockets
Server Sockets
URLViewer

From its first days, Java has had the network in mind, more so than any other common programming language. Java is the first programming language to provide as much support for network I/O as it does for file I/O, perhaps even more--Java's URL, URLConnection, Socket, and ServerSocket classes are all fertile sources of streams. The exact type of the stream used by a network connection is typically hidden inside the undocumented sun classes. Thus, network I/O relies primarily on the basic InputStream and OutputStream methods, which you can wrap with any higher-level stream that suits your needs: buffering, cryptography, compression, or whatever your application requires.

URLs

The java.net.URL class represents a Uniform Resource Locator like http://metalab.unc.edu/javafaq/. Each URL unambiguously identifies the location of a resource on the Internet. The URL class has four constructors. All are declared to throw MalformedURLException, a subclass of IOException.

public URL(String u) throws MalformedURLException
public URL(String protocol, String host, String file) 
 throws MalformedURLException
public URL(String protocol, String host, int port, String file) 
 throws MalformedURLException
public URL(URL context, String u) throws MalformedURLException

A MalformedURLException is thrown if the constructor's arguments do not specify a valid URL. Often this means a particular Java implementation does not have the right protocol handler installed. Thus, given a complete absolute URL like http://www.poly.edu/schedule/fall97/bgrad.html#cs, you construct a URL object like this:

URL u = null;
try {
  u = new URL("http://www.poly.edu/schedule/fall97/bgrad.html#cs");
}
catch (MalformedURLException e) { }

You can also construct the URL object by passing its pieces to the constructor:

URL u = null;
try {
  u = new URL("http", "www.poly.edu", "/schedule/fall97/bgrad.html#cs");
}
catch (MalformedURLException e) { }

You don't normally need to specify a port for a URL; most protocols have default ports. For instance, the HTTP port is 80. Sometimes the port used does change, and in that case you can use the third constructor:

URL u = null;
try {
  u = new URL("http", "www.poly.edu", 80, "/schedule/fall97/bgrad.html#cs");
}
catch (MalformedURLException e) { }

Finally, many HTML files contain relative URLs. The fourth constructor in the previous code creates URLs relative to a given URL and is particularly useful when parsing HTML. For example, the following code creates a URL pointing to the file 08.html, taking the rest of the URL from u1:

URL u1, u2;
try {
  u1 = new URL("http://metalab.unc.edu/javafaq/course/week12/07.html");
  u2 = new URL(u1, "08.html");
}
catch (MalformedURLException e) { }

Once a URL object has been constructed, there are two ways to retrieve its data. The openStream() method returns a raw stream of bytes from the source. The getContent() method returns a Java object that represents the data. When you call getContent(), Java looks for a content handler that matches the MIME type of the data. It is the openStream() method that is of concern in this book.

The openStream() method makes a socket connection to the server and port specified in the URL. It returns an input stream from which you can read the data at that URL, allowing you to download data from the server. Any headers that come before the actual data or file requested are stripped off before the stream is opened. You get only raw data.

public final InputStream openStream() throws IOException

You read from the input stream using an input stream or reader class. For example:

 try {
  URL u = new URL("http://www.amnesty.org/");
  InputStream in = u.openStream();
  int b;
  while ((b = in.read()) != -1) {
    System.out.write(b);
  }
}
catch (MalformedURLException e) {System.err.println(e);}
catch (IOException e) {System.err.println(e);} 

Applets running under the control of a security manager--that is untrusted applets that run inside a web browser--are normally only allowed to connect back to the host they were downloaded from. This host can be determined from the URL returned by the getCodeBase() method of the Applet class. Attempts to connect to other hosts throw security exceptions. You can create URLs that point to other hosts, but you may not download data from them using openStream() or any other method. (This security restriction for applets applies to any network connection, regardless of how you get it.)

Example 5-1 reads a series of URLs from the command line. The program connects to the specified URLs, downloads the data, and copies it to System.out.

Example 5-1: The URLTyper Program

import java.net.*;
import java.io.*;
import com.macfaq.io.*;
 
public class URLTyper {
 
  public static void main(String[] args) {
 
    if (args.length == 0) {
      System.err.println("Usage: java URLTyper url1 url2 ...");
      return;
    }
 
    for (int i = 0; i < args.length; i++) {
      if (args.length > 1) { 
        System.out.println(args[i] + ":");
      }   
      try {
        URL u = new URL(args[i]);
        InputStream in = u.openStream();
        StreamCopier.copy(in, System.out);
        in.close();
      }
      catch (MalformedURLException e) {System.err.println(e);} 
      catch (IOException e) {System.err.println(e);} 
    }
  }
}

For example, here are the first few lines you see when you connect to http://www.oreilly.com/:

D:\JAVA\ioexamples\05>java URLTyper http://www.oreilly.com/
<HTML>
<HEAD>
<META name="keywords" content="computer books, technical books, UNIX, unix,
Perl, Java, Linux, Internet, Web, C, C++, Windows, Windows NT, Security,
Sys Admin, System Administration, Oracle, design, graphics, online books,
online courses, Perl Conference, Web-based training, Software, open source,
free software">
<META name="description" content="O'Reilly is a leader in technical and
computer book documentation for UNIX, Perl, Java, Linux, Internet,
Web, C, C++, Windows, Windows NT, Security, Sys Admin, System
Administration, Oracle, Design & Graphics, Online Books, Online Courses,
Perl Conference, Web-based training, and Software">
<TITLE>www.oreilly.com -- Welcome to O'Reilly & Associates!</TITLE>

Most network connections, even on LANs, are slower and less reliable sources of data than files. Connections across the Internet are even slower and less reliable, and connections through a modem are slower and less reliable still. One way to enhance performance under these conditions is to buffer the data: to read as much data as you can into a temporary storage array inside the class, then parcel it out as needed. In the next chapter, you'll learn about the BufferedInputStream class that does exactly this.

URL Connections

URL connections are closely related to URLs, as their name implies. Indeed, you get a reference to a URLConnection by using the openConnection() method of a URL object; in many ways, the URL class is only a wrapper around the URLConnection class. However, URL connections provide more control over the communication between the client and the server. In particular, URL connections provide not just input streams by which the client can read data from the server, but also output streams to send data from the client to the server. This is essential for protocols like mailto.

The java.net.URLConnection class is an abstract class that handles communication with different kinds of servers, like FTP servers and web servers. Protocol-specific subclasses of URLConnection, hidden inside the sun classes, handle different kinds of servers.

Reading Data from URL Connections

URL connections take place in five steps:

  1. The URL object is constructed.
  2. The openConnection() method of the URL object creates the URLConnection object.
  3. The parameters for the connection and the request properties that the client sends to the server are set up.
  4. The connect() method makes the connection to the server, perhaps using a socket for a network connection or a file input stream for a local connection. The response header information is read from the server.
  5. Data is read from the connection by using the input stream returned by getInputStream() or through a content handler with getContent(). Data can be sent to the server using the output stream provided by getOutputStream().

This scheme is very much based on the HTTP/1.0 protocol. It does not fit other schemes that have a more interactive "request, response, request, response, request, response" pattern instead of HTTP/1.0's "single request, single response, close connection" pattern. In particular, FTP and even HTTP/1.1 aren't well suited to this pattern. I wouldn't be surprised to see this replaced with something more general in a future version of Java.

URLConnection objects are not constructed directly in your own programs. Instead, you create a URL for the particular resource, and call that URL's openConnection() method. This gives you a URLConnection. Then the getInputStream() method returns an input stream that reads data from the URL. (The openStream() method of the URL class is just a thin veneer over the getInputStream() method of the URLConnection class.) For example:

try { 
  URL u = new URL("http://www.digitalthink.com/");
  URLConnection uc = u.openConnection();
  uc.connect();
  InputStream in = uc.getInputStream();
  //...
}
catch (IOException e) { //...

If the connection cannot be opened (possibly because the remote host is unreachable), an IOException is thrown. Example 5-2 reads a URL from the command line, opens a connection to that URL, then prints the data returned by the server at that URL. This is similar to Example 5-1; WebCat differs primarily in that it uses a URLConnection instead of a URL.

Example 5-2: The WebCat Program

import java.net.*;
import java.io.*;
import com.macfaq.io.*;
 
public class WebCat {
 
  public static void main(String[] args) {
 
    if (args.length == 0) {
      System.err.println("Usage: java WebCat url1 url2 ...");
      return;
    }
 
    for (int i = 0; i < args.length; i++) {
      if (i > 0 && i < args.length) {
        System.out.println();
        System.out.println("----------------------");
        System.out.println();
      }
      System.out.println(args[i] + ":");
      try {
        URL u = new URL(args[i]);
        URLConnection uc = u.openConnection();
        uc.connect();
        InputStream in = uc.getInputStream();
        StreamCopier.copy(in, System.out);
        in.close();
      }
      catch (IOException e) {System.err.println(e);}      
    } // end for
  }
}

Writing Data on URL Connections

Writing data to a URLConnection is similar to reading data. However, you must first inform the URLConnection that you plan to use it for output, and then, instead of getting the connection's input stream and reading from it, you get the connection's output stream and write to it. This is commonly used to talk to CGIs that use the POST method or to store files on web servers through the PUT method, or to communicate with a Java servlet running on a server. Here are the steps for writing data on a URLConnection:

  1. Construct the URL object.
  2. Call the openConnection() method of the URL object to create the URLConnection object.
  3. Pass true to setDoOutput() to indicate that this URLConnection will be used for output.
  4. If you also want to read input from the stream, invoke setDoInput(true) to indicate that this URLConnection will be used for input.
  5. Create the data you want to send, preferably as a byte array.
  6. Call getOutputStream() to get an output stream object. Write the byte array calculated in step 5 onto the stream.
  7. Close the output stream.
  8. Call getInputStream() to get an input stream object. Read and write it as usual.

Example 5-3 uses these steps to implement a simple mail client. It forms a mailto URL from an email address entered on the command line. Input for the message is copied from System.in onto the output stream of the URLConnection using a StreamCopier. The end of the message is indicated by the end-of-stream character.

Example 5-3: The MailClient Class

import java.net.*;
import java.io.*;
import com.macfaq.io.*;
 
public class MailClient {
 
  public static void main(String[] args) {
 
    if (args.length == 0) {
      System.err.println("Usage: java MailClient username@host.com");
      return;
    }
    
    try {
      URL u = new URL("mailto:" + args[0]);
      URLConnection uc = u.openConnection();
      uc.setDoOutput(true);
      uc.connect();
      OutputStream out = uc.getOutputStream();
      StreamCopier.copy(System.in, out);
      out.close();
    }
    catch (IOException e) {System.err.println(e);}      
  }
}

For example, to send email to the author of this book:

% java MailClient elharo@metalab.unc.edu
hi there!
^D

MailClient suffers from a few restrictions. The proper way to detect the end of the message is to look for a period on a line by itself. Proper or not, that style of user interface is really antiquated, so I didn't bother to implement it. To do so properly, you'll need to use a Reader or a Writer; they're discussed in Chapter 15, Readers and Writers. Furthermore, it only works in Java environments that support the mailto protocol; thus, it works under Sun's JDK, but may not work in other environments. It also requires that the local host be running an SMTP server, or that the system property mailhost must contain the name of an accessible SMTP server, or that a machine in the local domain named mailhost be running an SMTP server. Finally, the security manager must permit network connections to that server, although this is not normally a problem in an application.

Sockets

Before data is sent across the Internet from one host to another, it is split into packets of varying but finite size called datagrams. Datagrams range in size from a few dozen bytes to about 60,000 bytes. Anything larger, and often things smaller, must be split into smaller pieces before it can be transmitted. The advantage of this scheme is that if one packet is lost, it can be retransmitted without requiring redelivery of all other packets. Furthermore, if packets arrive out of order, they can be reordered at the receiving end of the connection.

Fortunately, packets are invisible to the Java programmer. The host's native networking software splits data into packets on the sending end and reassembles packets on the receiving end. Instead, the Java programmer is presented with a higher-level abstraction called a socket. The socket represents a reliable connection for the transmission of data between two hosts. It isolates you from the details of packet encodings, lost and retransmitted packets, and packets that arrive out of order. A socket performs four fundamental operations:

  1. Connect to a remote machine
  2. Send data
  3. Receive data
  4. Close the connection

A socket may not be connected to more than one host at a time. However, a socket may both send data to and receive data from the host to which it's connected.

The java.net.Socket class is Java's interface to a network socket and allows you to perform all four fundamental socket operations. It provides raw, uninterpreted communication between two hosts. You can connect to remote machines; you can send data; you can receive data; you can close the connection. No part of the protocol is abstracted out, as it is with URL and URLConnection. The programmer is completely responsible for the interaction between the network client and the server. To create a connection, you call one of the Socket constructors, specifying the host you want to connect to. Each Socket object is associated with exactly one remote host. To connect to a different host, you must create a new Socket object:

public Socket(String host, int port) throws UnknownHostException, IOException
public Socket(InetAddress address, int port) throws IOException
public Socket(String host, int port, InetAddress localAddr, int localPort) 
 throws IOException
public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException

The host is a string like "www.oreilly.com" or "metalab.unc.edu", which specifies the particular host to connect to. It may even be a numeric, dotted quad string like "199.1.32.90". This argument may also be passed as a java.net.InetAddress object.

The port argument is the port on the remote host to connect to. A computer's network interface is logically subdivided into 65,536 different ports. As data traverses the Internet in packets, each packet carries not only the address of the host but also the port on that host at which it's aimed. The host is responsible for reading the port number from each packet it receives to decide which program should receive that chunk of data. Many services run on well-known ports. This means that the protocol specifies that the service should or must use a particular port--for example, HTTP servers generally listen on port 80.

The optional localAddress and localPort arguments specify which address and port on the local host the socket is to connect from, assuming more than one is available. Most hosts have many available ports but only one address. These two arguments are optional. If they're left out, the constructor will choose reasonable values.

Sending and receiving data across a socket is accomplished with output and input streams. These are the methods to get both streams for the socket:

public InputStream getInputStream() throws IOException
public OutputStream getOutputStream() throws IOException

There's also a method to close a socket:

public synchronized void close() throws IOException

This effectively closes the socket's input and output streams as well. Any attempt to read from or write to them after the socket is closed will throw an IOException.

Example 5-4 is yet another program that connects to a web server and downloads a specified URL. However, since this one uses raw sockets, it needs to both send the HTTP request and read the headers in the response. These are not parsed away as they are by the URL and URLConnection classes; you use an output stream to send the request explicitly and an input stream to read the data back, including HTTP headers. Only HTTP URLs are supported.

Example 5-4: The SocketTyper Program

import java.net.*;
import java.io.*;
import com.macfaq.io.*;
 
public class SocketTyper {
 
  public static void main(String[] args) {
 
    if (args.length == 0) {
      System.err.println("Usage: java SocketTyper url1 url2 ...");
      return;
    }
 
    for (int i = 0; i < args.length; i++) {
      if (args.length > 1) { 
        System.out.println(args[i] + ":");
      }   
      try {
        URL u = new URL(args[i]);
        if (!u.getProtocol().equalsIgnoreCase("http")) {
          System.err.println("Sorry, " + u.getProtocol() + 
          " is not yet supported.");
          break;
        }
        
        String host = u.getHost();
        int port = u.getPort();
        String file = u.getFile();
        // default port
        if (port <= 0) port = 80;
        
        Socket s = new Socket(host, port);
        String request = "GET " + file + " HTTP/1.0\r\n"
        + "User-Agent: MechaMozilla\r\nAccept: text/*\r\n\r\n";
        // This next line is problematic on non-ASCII systems
        byte[] b = request.getBytes();
        
        OutputStream out = s.getOutputStream();
        InputStream in = s.getInputStream();
        out.write(b);
        out.flush();
        
        StreamCopier.copy(in, System.out);
        in.close();
        out.close();
        s.close();
      }
      catch (MalformedURLException e) {System.err.println(e);} 
      catch (IOException e) {System.err.println(e);} 
    }
  }
}

For example, when SocketTyper connects to http://www.oreilly.com/, here is what you see:

% java SocketTyper http://www.oreilly.com/
HTTP/1.0 200 OK
Server: WN/1.15.1
Date: Sun, 09 Aug 1998 20:05:03 GMT
Last-modified: Fri, 07 Aug 1998 23:44:36 GMT
Content-type: text/html
Title: www.oreilly.com -- Welcome to O'Reilly & Associates!
Link: <mailto:webmaster@ora.com>; rev="Made"
 
<HTML>
<HEAD>
<META name="keywords" content="computer books, technical books, UNIX, unix,
Perl, Java, Linux, Internet, Web, C, C++, Windows, Windows NT, Security,
Sys Admin, System Administration, Oracle, design, graphics, online books,
online courses, Perl Conference, Web-based training, Software, open source,
free software">

Notice the header lines you didn't see in Example 5-1. When you use the URL class to download a web page, the associated protocol handler never shows you the HTTP header.

Server Sockets

There are two ends to each connection: the client, which initiates the connection, and the server, which responds to the connection. So far, we've only discussed the client side and assumed that a server existed out there for the client to talk to. To implement a server, you need to write a program that waits for other hosts to connect to it. A server socket binds to a particular port on the local machine (the server); once it has successfully bound to a port, it listens for incoming connection attempts from remote machines (the clients). When the server detects a connection attempt, it accepts the connection. This creates a socket between the two machines over which the client and the server communicate.

Many clients can connect to a port on the server simultaneously. Incoming data is distinguished by the port to which it is addressed and the client host and port from which it came. The server can tell for which service (like HTTP or FTP) the data is intended by inspecting the port. It knows where to send any response by looking at the client address and port stored with the data.

No more than one server socket can listen to a particular port at one time. Therefore, since a server may need to handle many connections at once, server programs tend to be heavily multithreaded. Generally, the server socket listening on the port only accepts the connections. It passes off the actual processing of each connection to a separate thread. Incoming connections are stored in a queue until the server can accept them. On most systems, the default queue length is between 5 and 50. Once the queue fills up, further incoming connections are refused until space in the queue opens up.

The java.net.ServerSocket class represents a server socket. Three constructors let you specify the port to bind to, the queue length for incoming connections, and the IP address:

public ServerSocket(int port) throws IOException
public ServerSocket(int port, int backlog) throws IOException
public ServerSocket(int port, int backlog, InetAddress bindAddr) 
 throws IOException

Normally, you specify only the port you want to listen on:

try {
  ServerSocket ss = new ServerSocket(80);
}
catch (IOException e) {System.err.println(e);}

When you create a ServerSocket object, it attempts to bind to the port on the local host given by the port argument. If another server socket is already listening to the port, then a java.net.BindException, a subclass of IOException, is thrown. No more than one process or thread can listen to a particular port at a time. This includes non-Java processes or threads. For example, if there's already an HTTP server running on port 80, you won't be able to bind to port 80. On Unix systems (but not Windows or the Mac) your program must be running as root to bind to a port between 1 and 1023.

0 is a special port number. It tells Java to pick an available port. You can then find out what port it's picked with the getLocalPort() method:

public int getLocalPort()

This is useful if the client and the server have already established a separate channel of communication over which the chosen port number can be communicated. For example, the FTP protocol uses two sockets. The initial connection is made by the client to the server to send commands. One of the commands sent tells the server the port number on which the client is listening. The server then connects to the client on this port to send data.

Once you have a ServerSocket, you need to wait for incoming connections. You do this by calling the accept() method, which blocks until a connection attempt occurs and then returns a Socket that you can use to communicate with the client. The close() method terminates the ServerSocket.

public Socket accept() throws IOException
public void close() throws IOException

That's pretty much all there is, except for a few methods dealing with socket options and some other details. In particular, there aren't methods for getting input and output streams. Instead, accept()returns a Socket object: you call the Socket's getInputStream() or getOutputStream() method. For example:

try {
  ServerSocket ss = new ServerSocket(2345);
  Socket s = ss.accept();
  OutputStream out = s.getOutputStream();
  // Send data to the client.
  s.close();
}
catch (IOException e) {System.err.println(e);}

Notice in this example, I closed the Socket s, not the ServerSocket ss. ss is still bound to port 2345. You get a new socket for each connection and reuse the server socket. For example, the next code fragment repeatedly accepts connections:

try {
  ServerSocket ss = new ServerSocket(2345);
  while (true) {
    Socket s = ss.accept();
    OutputStream out = s.getOutputStream();
    // send data to the client
    s.close();
  }
}
catch (IOException e) {System.err.println(e);}

The program in Example 5-5 reads a port number from the command line. It listens on that port for incoming connections. When it detects one, it answers back with the client's address and port and its own. Then it closes the connection.

Example 5-5: The HelloServer Program

import java.net.*;
import java.io.*;
 
public class HelloServer {
 
  public final static int defaultPort = 2345;
 
  public static void main(String[] args) {
  
    int port = defaultPort;
    
    try {
      port = Integer.parseInt(args[0]);
    }
    catch (Exception e) {}
    if (port <= 0 || port >= 65536) port = defaultPort;
    
    try {
      ServerSocket ss = new ServerSocket(port);
      while (true) {
        try {
          Socket s = ss.accept();
          
          String response = "Hello " + s.getInetAddress() + " on port " 
           + s.getPort() + "\r\n";
          response += "This is " + s.getLocalAddress() + " on port " 
           + s.getLocalPort() + "\r\n";
          OutputStream out = s.getOutputStream();
          out.write(response.getBytes());
          out.flush();
          s.close();
        }
        catch (IOException e) {}
      }
    }
    catch (IOException e) {System.err.println(e);}
  }  
}

Here's some output from our server. The server is running on utopia.poly.edu. The client is connecting from titan.oit.unc.edu. Note how the port from which the connection comes changes each time; like most client programs, the telnet program picks a random local port for outgoing connections:

% telnet utopia.poly.edu 2545
Trying 128.238.3.21...
Connected to utopia.poly.edu.
Escape character is '^]'.
Hello titan.oit.unc.edu/152.2.22.14 on port 50361
This is utopia.poly.edu/128.238.3.21 on port 2545
Connection closed by foreign host.
% telnet utopia.poly.edu 2545
Trying 128.238.3.21...
Connected to utopia.poly.edu.
Escape character is '^]'.
Hello titan.oit.unc.edu/152.2.22.14 on port 50362
This is utopia.poly.edu/128.238.3.21 on port 2545
Connection closed by foreign host.

URLViewer

Example 5-6 is an improved version of the URLViewer you first encountered in Chapter 2. This is a simple application that provides a window in which you can view the contents of a URL. It assumes that those contents are more or less ASCII text. (In future chapters, I'll remove that restriction.) Figure 5-1 shows the result. Our application has a text area in which the user can type a URL, a Load button that the user uses to load the specified URL, and a StreamedTextArea component that displays the text from the URL. Each of these corresponds to a field in the URLViewer class.

Figure 5-1. The URLViewer

 

Example 5-6: The URLViewer Program

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import com.macfaq.awt.*;
import com.macfaq.io.*;
 
public class URLViewer extends Frame 
 implements WindowListener, ActionListener {
 
  TextField theURL = new TextField();
  Button loadButton = new Button("Load");
  StreamedTextArea theDisplay = new StreamedTextArea();
  
  public URLViewer() {
    super("URL Viewer");
  }
 
  public void init() {
  
    this.add("North", theURL);
    this.add("Center", theDisplay);
    Panel south = new Panel();
    south.add(loadButton);
    this.add("South", south);
    theURL.addActionListener(this);
    loadButton.addActionListener(this);
    this.addWindowListener(this);
    this.setLocation(50, 50);
    this.pack();
    this.show();
  }
 
  public void actionPerformed(ActionEvent evt) {
  
    try {
      URL u = new URL(theURL.getText());
      InputStream in = u.openStream();
      OutputStream out = theDisplay.getOutputStream();
      StreamCopier.copy(in, out);
      in.close();
      out.close();
    }
    catch (MalformedURLException ex) {theDisplay.setText("Invalid URL");}
    catch (IOException ex) {theDisplay.setText("Invalid URL");}
  }
  
  public void windowClosing(WindowEvent e) {
  
    this.setVisible(false);
    this.dispose();
  }
  
  public void windowOpened(WindowEvent e) {}
  public void windowClosed(WindowEvent e) {}
  public void windowIconified(WindowEvent e) {}
  public void windowDeiconified(WindowEvent e) {}
  public void windowActivated(WindowEvent e) {}
  public void windowDeactivated(WindowEvent e) {}
 
  public static void main(String args[]) {
 
    URLViewer me = new URLViewer();
    me.init();
  }
}

The URLViewer class itself extends Frame. An alternative would have been to extend Panel, which would have allowed URLViewer objects to be embedded in other containers. However, this application seemed big enough to justify exclusive use of a window. URLViewer implements the WindowListener interface to enable the user to close the window by clicking in the close box. Only the windowClosing() method is nontrivial. The other six window methods are do-nothing methods required to satisfy the contract of the WindowListener interface.

The init() method builds the interface and displays the window. This is invoked by the main() method, which constructs a new URLViewer object. The constructor is quite simple, merely passing the title of the frame to the superclass constructor.

The streamed text area is filled when the user clicks the Load button or hits Return inside the URL text field. The URLViewer object listens to both of these components. The URLViewer's actionPerformed() method constructs a URL from the text in the text field, then opens an input stream from the URL in the text field. Next, StreamCopier from Chapter 3, Input Streams pours the data from the URL's input stream into the text area's output stream. When that's finished, both streams are closed.

Back to: Java I/O


O'Reilly Home | O'Reilly Bookstores | How to Order | O'Reilly Contacts
International | About O'Reilly | Affiliated Companies

© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com