- Implementing a Client
- Parsing Strings by Using StringTokenizer
- Example: A Client to Verify E-Mail Addresses
- Example: A Network Client That Retrieves URLs
- The URL Class
- WebClient: Talking to Web Servers Interactively
- Implementing a Server
- Example: A Simple HTTP Server
- RMI: Remote Method Invocation
- Summary
17.6 WebClient: Talking to Web Servers Interactively
In Section 17.4 (Example: A Network Client That Retrieves URLs) and Section 17.5 (The URL Class) we illustrated how easy it is to connect to a Web server and request a document. The programs presented accept a URL from the command line and display the retrieved page to the output stream. In this section, we present WebC_lient, a simple graphical interface to HTTP servers. This program, Listing 17.13, accepts an HTTP request line and request headers from the user. When the user presses Submit Request, the request and headers are sent to the server, followed by a blank line. The response is displayed in a scrolling text area. Downloading is performed in a separate thread so that the user can interrupt the download of long documents.
The HttpClient class, Listing 17.14, does the real network communication. It simply sends the designated request line and request headers to the Web server, then reads, one at a time, the lines that come back, placing them into a JTextArea until either the server closes or the HttpClient is interrupted by means of the isInterrupted flag.
The LabeledTextField class, Listing 17.15, is a simple combination of a JTextField and a JLa_bel and is used in the upper panel of WebClient for user input. The Interruptible class, Listing 17.16, is a simple interface used to identify classes that have an isInterrupted method. Interruptible is used by HttpClient to poll WebClient to see if the user has interrupted the program.
As always, the source code can be downloaded from the on-line archive at http://www.corewebprogramming.com/, and there are no restrictions on use of the code.
A representative conversation with http://www.coreservlets.com is shown in Figure 171. Here, only a GET request was sent to the server, without any additional HTTP request headers. For more information on HTTP request headers, see Section 19.7 (The Client Request: HTTP Request Headers), and for HTTP response headers, see Section 19.8 (The Server Response: HTTP Response Headers).
Figure 171 A conversation with http://www.coreservlets.com shows a typical request and response.
Listing 17.13 WebClient.java
import java.awt.*; // For BorderLayout, GridLayout, Font, Color. import java.awt.event.*; import java.util.*; import javax.swing.*; /** A graphical client that lets you interactively connect to * Web servers and send custom request lines and * request headers. */ public class WebClient extends JPanel implements Runnable, Interruptible, ActionListener { public static void main(String[] args) { WindowUtilities.setNativeLookAndFeel(); WindowUtilities.openInJFrame(new WebClient(), 600, 700, "Web Client", SystemColor.control); } private LabeledTextField hostField, portField, requestLineField; private JTextArea requestHeadersArea, resultArea; private String host, requestLine; private int port; private String[] requestHeaders = new String[30]; private JButton submitButton, interruptButton; private boolean isInterrupted = false; public WebClient() { setLayout(new BorderLayout(5, 30)); int fontSize = 14; Font labelFont = new Font("Serif", Font.BOLD, fontSize); Font headingFont = new Font("SansSerif", Font.BOLD, fontSize+4); Font textFont = new Font("Monospaced", Font.BOLD, fontSize-2); JPanel inputPanel = new JPanel(); inputPanel.setLayout(new BorderLayout()); JPanel labelPanel = new JPanel(); labelPanel.setLayout(new GridLayout(4,1)); hostField = new LabeledTextField("Host:", labelFont, 30, textFont); portField = new LabeledTextField("Port:", labelFont, "80", 5, textFont); // Use HTTP 1.0 for compatibility with the most servers. // If you switch this to 1.1, you *must* supply a // Host: request header. (continued) requestLineField = new LabeledTextField("Request Line:", labelFont, "GET / HTTP/1.0", 50, textFont); labelPanel.add(hostField); labelPanel.add(portField); labelPanel.add(requestLineField); JLabel requestHeadersLabel = new JLabel("Request Headers:"); requestHeadersLabel.setFont(labelFont); labelPanel.add(requestHeadersLabel); inputPanel.add(labelPanel, BorderLayout.NORTH); requestHeadersArea = new JTextArea(5, 80); requestHeadersArea.setFont(textFont); JScrollPane headerScrollArea = new JScrollPane(requestHeadersArea); inputPanel.add(headerScrollArea, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); submitButton = new JButton("Submit Request"); submitButton.addActionListener(this); submitButton.setFont(labelFont); buttonPanel.add(submitButton); inputPanel.add(buttonPanel, BorderLayout.SOUTH); add(inputPanel, BorderLayout.NORTH); JPanel resultPanel = new JPanel(); resultPanel.setLayout(new BorderLayout()); JLabel resultLabel = new JLabel("Results", JLabel.CENTER); resultLabel.setFont(headingFont); resultPanel.add(resultLabel, BorderLayout.NORTH); resultArea = new JTextArea(); resultArea.setFont(textFont); JScrollPane resultScrollArea = new JScrollPane(resultArea); resultPanel.add(resultScrollArea, BorderLayout.CENTER); JPanel interruptPanel = new JPanel(); interruptButton = new JButton("Interrupt Download"); interruptButton.addActionListener(this); interruptButton.setFont(labelFont); interruptPanel.add(interruptButton); resultPanel.add(interruptPanel, BorderLayout.SOUTH); add(resultPanel, BorderLayout.CENTER); } (continued) public void actionPerformed(ActionEvent event) { if (event.getSource() == submitButton) { Thread downloader = new Thread(this); downloader.start(); } else if (event.getSource() == interruptButton) { isInterrupted = true; } } public void run() { isInterrupted = false; if (hasLegalArgs()) new HttpClient(host, port, requestLine, requestHeaders, resultArea, this); } public boolean isInterrupted() { return(isInterrupted); } private boolean hasLegalArgs() { host = hostField.getTextField().getText(); if (host.length() == 0) { report("Missing hostname"); return(false); } String portString = portField.getTextField().getText(); if (portString.length() == 0) { report("Missing port number"); return(false); } try { port = Integer.parseInt(portString); } catch(NumberFormatException nfe) { report("Illegal port number: " + portString); return(false); } requestLine = requestLineField.getTextField().getText(); if (requestLine.length() == 0) { report("Missing request line"); return(false); } getRequestHeaders(); return(true); } (continued) private void report(String s) { resultArea.setText(s); } private void getRequestHeaders() { for(int i=0; i<requestHeaders.length; i++) { requestHeaders[i] = null; } int headerNum = 0; String header = requestHeadersArea.getText(); StringTokenizer tok = new StringTokenizer(header, "\r\n"); while (tok.hasMoreTokens()) { requestHeaders[headerNum++] = tok.nextToken(); } } }
Listing 17.14 HttpClient.java
import java.net.*; import java.io.*; import javax.swing.*; /** The underlying network client used by WebClient. */ public class HttpClient extends NetworkClient { private String requestLine; private String[] requestHeaders; private JTextArea outputArea; private Interruptible app; public HttpClient(String host, int port, String requestLine, String[] requestHeaders, JTextArea outputArea, Interruptible app) { super(host, port); this.requestLine = requestLine; this.requestHeaders = requestHeaders; this.outputArea = outputArea; this.app = app; (continued) if (checkHost(host)) { connect(); } } protected void handleConnection(Socket uriSocket) throws IOException { try { PrintWriter out = SocketUtil.getWriter(uriSocket); BufferedReader in = SocketUtil.getReader(uriSocket); outputArea.setText(""); out.println(requestLine); for(int i=0; i<requestHeaders.length; i++) { if (requestHeaders[i] == null) { break; } else { out.println(requestHeaders[i]); } } out.println(); String line; while ((line = in.readLine()) != null && !app.isInterrupted()) { outputArea.append(line + "\n"); } if (app.isInterrupted()) { outputArea.append("---- Download Interrupted ----"); } } catch(Exception e) { outputArea.setText("Error: " + e); } } private boolean checkHost(String host) { try { InetAddress.getByName(host); return(true); } catch(UnknownHostException uhe) { outputArea.setText("Bogus host: " + host); return(false); } } }
Listing 17.15 LabeledTextField.java
import java.awt.*; // For FlowLayout, Font. import javax.swing.*; /** A TextField with an associated Label. */ public class LabeledTextField extends JPanel { private JLabel label; private JTextField textField; public LabeledTextField(String labelString, Font labelFont, int textFieldSize, Font textFont) { setLayout(new FlowLayout(FlowLayout.LEFT)); label = new JLabel(labelString, JLabel.RIGHT); if (labelFont != null) { label.setFont(labelFont); } add(label); textField = new JTextField(textFieldSize); if (textFont != null) { textField.setFont(textFont); } add(textField); } public LabeledTextField(String labelString, String textFieldString) { this(labelString, null, textFieldString, textFieldString.length(), null); } public LabeledTextField(String labelString, int textFieldSize) { this(labelString, null, textFieldSize, null); } public LabeledTextField(String labelString, Font labelFont, String textFieldString, int textFieldSize, Font textFont) { this(labelString, labelFont, textFieldSize, textFont); textField.setText(textFieldString); } /** The Label at the left side of the LabeledTextField. * To manipulate the Label, do: * <PRE> * LabeledTextField ltf = new LabeledTextField(...); * ltf.getLabel().someLabelMethod(...); * </PRE> */ public JLabel getLabel() { return(label); } /** The TextField at the right side of the * LabeledTextField. */ public JTextField getTextField() { return(textField); } }
Listing 16.16 Interruptible.java
/** An interface for classes that can be polled to see * if they've been interrupted. Used by HttpClient * and WebClient to allow the user to interrupt a network * download. */ public interface Interruptible { public boolean isInterrupted(); }