Talking to Web apps

Web browsers are the universal clients for web apps. They retrieve documents for display and serve as a user interface, primarily through the use of HTML forms and linked documents. Next, we show how to write client-side Java code that uses HTTP through the URL class to work with web apps directly using GET and POST operations to retrieve and send data. Later in this chapter, we'll begin a discussion of web services, which marry HTTP with XML to enable cross-platform app-to-app communications using web standards. There are many reasons an app (or applet) might want to communicate via HTTP. For example, compatibility with another browser-based app might be important, or you might need to gain access to a server through a firewall where direct socket connections (and, hence, RMI or IIOP) are not available. HTTP is the lingua franca of the Net and despite its limitations (or more likely because of its simplicity), it has rapidly become one of the most widely supported protocols in the world. As for using Java on the client side, all the other reasons you would write a client GUI app (as opposed to a pure Web/HTML-based app) also present themselves. A client-side GUI can do sophisticated presentation and validation while, with the techniques presented here, still use web-enabled services over the network. The primary task we discuss here is sending data to the server, specifically HTML form-encoded data. In a web browser, the name/value pairs of HTML form fields are encoded in a special format and sent to the server using one of two methods. The first method, using the HTTP GET command, encodes the user's input into the URL and requests the corresponding document. The server recognizes that the first part of the URL refers to a program and invokes it, passing along the information encoded in the URL as a parameter. The second method uses the HTTP POST command to ask the server to accept the encoded data and pass it to a web app as a stream. In Java, we can create a URL that refers to a server-side program and request or send it data using the GET and POST methods. (In , we'll see how to build web apps that implement the other side of this conversation.)

Using the GET Method

Using the GET method of encoding data in a URL is pretty easy. All we have to do is create a URL pointing to a server program and use a simple convention to tack on the encoded name/value pairs that make up our data. For example, the following code snippet opens a URL to a CGI program called login.cgi on the server myhost and passes it two name/value pairs. It then prints whatever text the CGI sends back:

URL url = new URL(
 // this string should be URL-encoded
 "http://myhost/cgi-bin/login.cgi?Name=Pat&Password=foobar");
BufferedReader bin = new BufferedReader (
 new InputStreamReader( url.openStream( ) ));
String line;
while ( (line = bin.readLine( )) != null )
 System.out.println( line );


To form the URL with parameters, we start with the base URL of login.cgi; we add a question mark (?), which marks the beginning of the parameter data, followed by the first name/value pair. We can add as many pairs as we want, separated by ampersand (&) characters. The rest of our code simply opens the stream and reads back the response from the server. Remember that creating a URL doesn't actually open the connection. In this case, the URL connection was made implicitly when we called openStream( ). Although we are assuming here that our server sends back text, it could send anything. (In theory, we could use the getContentType( ) method of the URL to check the MIME type of any returned data and try to retrieve the data as an object using getContent( ) as well.) It's important to point out that we have skipped a step here. This example works because our name/value pairs happen to be simple text. If any "nonprintable" or special characters (including ? or &) are in the pairs, they must be encoded first. The java.net.URLEncoder class provides a utility for encoding the data. We'll show how to use it in the next example. Another important thing is that although this small example sends a password field, you should never send sensitive data using this simplistic approach. The data in this example is sent in clear text across the network (it is not encrypted). And in this case, the password field would appear anywhere the URL is printed as well (e.g., server logs and tutorialmarks). We'll talk about secure web communications later in this chapter and when we discuss writing web apps using servlets in .

Using the POST Method

Here's a small app that acts like an HTML form. It gathers data from two text fieldsname and passwordand posts the data to a specified URL using the HTTP POST method. This Swing-based client app works with a server-side web-based app, just like a web browser. Here's the code:

//file: Post.java import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Post extends JPanel implements ActionListener {
 JTextField nameField, passwordField;
 String postURL;
 GridBagConstraints constraints = new GridBagConstraints( );
 void addGB( Component component, int x, int y ) {
 constraints.gridx = x; constraints.gridy = y;
 add ( component, constraints );
 }
 public Post( String postURL ) {
 this.postURL = postURL;
 JButton postButton = new JButton("Post");
 postButton.addActionListener( this );
 setLayout( new GridBagLayout( ) );
 addGB( new JLabel("Name:"), 0,0 );
 addGB( nameField = new JTextField(20), 1,0 );
 addGB( new JLabel("Password:"), 0,1 );
 addGB( passwordField = new JPasswordField(20),1,1 );
 constraints.gridwidth = 2;
 addGB( postButton, 0,2 );
 }
 public void actionPerformed(ActionEvent e) {
 postData( );
 }
 protected void postData( ) {
 StringBuffer sb = new StringBuffer( );
 sb.append( URLEncoder.encode("Name") + "=" );
 sb.append( URLEncoder.encode(nameField.getText( )) );
 sb.append( "&" + URLEncoder.encode("Password") + "=" );
 sb.append( URLEncoder.encode(passwordField.getText( )) );
 String formData = sb.toString( );
 try {
 URL url = new URL( postURL );
 HttpURLConnection urlcon =
 (HttpURLConnection) url.openConnection( );
 urlcon.setRequestMethod("POST");
 urlcon.setRequestProperty("Content-type",
 "app/x-www-form-urlencoded");
 urlcon.setDoOutput(true);
 urlcon.setDoInput(true);
 PrintWriter pout = new PrintWriter( new OutputStreamWriter(
 urlcon.getOutputStream( ), "8859_1"), true );
 pout.print( formData );
 pout.flush( );
 // read results...
 if ( urlcon.getResponseCode( ) == HttpURLConnection.HTTP_OK )
 System.out.println("Posted ok!");
 else {
 System.out.println("Bad post...");
 return;
 }
 //InputStream in = urlcon.getInputStream( );
 // ...
 } catch (MalformedURLException e) {
 System.out.println(e); // bad postURL
 } catch (IOException e2) {
 System.out.println(e2); // I/O error
 }
 }
 public static void main( String [] args ) {
 JFrame frame = new JFrame("SimplePost");
 frame.getContentPane( ).add( new Post( args[0] ), "Center" );
 frame.pack( );
 frame.setVisible(true);
 }
}


When you run this app, you must specify the URL of the server program on the command line. For example:

% java Post http://www.myserver.example/cgi-bin/login.cgi


The beginning of the app creates the form; there's nothing here that won't be obvious after you've read s 16 through 18, which cover the AWT and Swing GUI toolkits. All the magic happens in the protected postData( ) method. First, we create a StringBuffer and load it with name/value pairs, separated by ampersands. (We don't need the initial question mark when we're using the POST method because we're not appending to a URL string.) Each pair is first encoded using the static URLEncoder.encode( ) method. We run the name fields through the encoder as well as the value fields, even though we know that in this case they contain no special characters. Next, we set up the connection to the server program. In our previous example, we didn't have to do anything special to send the data because the request was made by the simple act of opening the URL on the server. Here, we have to carry some of the weight of talking to the remote web server. Fortunately, the HttpURLConnection object does most of the work for us; we just have to tell it that we want to do a POST to the URL and the type of data we are sending. We ask for the URLConnection object using the URL's openConnection( ) method. We know that we are using the HTTP protocol, so we should be able to cast it to an HttpURLConnection type, which has the support we need. As of Java 5.0, we can safely make this assumption because HTTP is always supported. We then use setRequestMethod( ) to tell the connection we want to do a POST operation. We also use setRequestProperty( ) to set the Content-Type field of our HTTP request to the appropriate typein this case, the proper MIME type for encoded form data. (This is necessary to tell the server what kind of data we're sending.) Finally, we use the setDoOutput( ) and setDoInput( ) methods to tell the connection that we want to both send and receive stream data. The URL connection infers from this combination that we are going to do a POST operation and expects a response. Next, we get an output stream from the connection with getOutputStream( ) and create a PrintWriter so we can easily write our encoded data. After we post the data, our app calls geTResponseCode( ) to see whether the HTTP response code from the server indicates the POST was successful. Other response codes (defined as constants in HttpURLConnection) indicate various failures. At the end of our example, we indicate where we could have read back the text of the response. For this app, we'll assume that simply knowing the post was successful is sufficient. Although form-encoded data (as indicated by the MIME type we specified for the Content-Type field) is the most common, other types of communications are possible. We could have used the input and output streams to exchange arbitrary data types with the server program. The POST operation could send any kind of data; the server app simply has to know how to handle it. One final note: if you are writing an app that needs to decode form data, you can use the java.net.URLDecoder to undo the operation of the URLEncoder. If you use the Servlet API, this happens automatically, as you'll see in .

The HttpURLConnection

Other information from the request is available from the HttpURLConnection as well. We could use getContentType( ) and getContentEncoding( ) to determine the MIME type and encoding of the response. We could also interrogate the HTTP response headers using getHeaderField( ). (HTTP response headers are metadata name/value pairs carried with the response.) Convenience methods can fetch integer and date-formatted header fields, getHeaderFieldInt( ) and getHeaderFieldDate( ), which return an int and a long type, respectively. The content length and last modification date are provided through getContentLength( ) and getLastModified( ).

SSL and Secure Web Communications

The previous examples sent a field called Password to the server. However, standard HTTP doesn't provide encryption to hide our data. Fortunately, adding security for GET and POST operations like this is easy (trivial in fact, for the developer). Where available you simply have to use a secure form of the HTTP protocolHTTPS:

https://www.myserver.example/cgi-bin/login.cgi


HTTPS is a version of the standard HTTP protocol run over Secure Sockets Layer (SSL), which uses public-key encryption techniques to encrypt the browser-to-server communications. Most web browsers and servers currently come with built-in support for HTTPS (or raw SSL sockets). Therefore, if your web server supports HTTPS, you can use a browser to send and receive secure data by specifying the https protocol in your URLs. There is much more to learn about SSL and related aspects of security such as authenticating whom you are actually talking to, but as far as basic data encryption goes, this is all you have to do. It is not something your code has to deal with directly. As of Java 1.4, the standard distribution from Sun is shipped with SSL and HTTPS support, and as of Java 5.0, all Java implementations must support HTTPS as well as HTTP for URL connections. This applies to applets using the Java Plug-in as well as apps. We'll discuss writing secure web apps in more detail in .

URLs, URNs, and URIs

Earlier, we discussed URLs and distinguished them from the concept of URNs. Whereas a URL points to a specific location on the Net and specifies a protocol or scheme for accessing its contents, a URN is simply a globally unique name. A URL is analogous to giving someone your phone number. But a URN is more like giving them your social security number. Your phone number may change, but your social security number uniquely identifies you forever. While it's possible that some mechanism might be able to look at a given URN and tie it to a location (a URL), it is not necessarily so. URNs are intended only to be permanent, unique, abstract identifiers for an item, whereas a URL is a mechanism you can use to get in touch with a resource right now. You can use a phone number to contact me today, but you can use my social security number to uniquely identify me anytime. An example of a URN is http://www.w3.org/1999/XSL/Transform, which is the identifier for a version of the Extensible Stylesheet Language, standardized by the W3C. Now, it happens that this is also a URL (you can go to that address and find information about the standard), but that is for convenience only. This URN's primary mission is to uniquely label the version of the coding language in a way that never changes. Collectively, URLs and URNs are called Uniform Resource Identifiers or URIs. A URI is simply a URL or URN. So, URLs and URNs are kinds of URIs. The reason for this abstraction is that URLs and URNs, by definition, have some things in common. All URIs are supposed to be human-readable and "transcribable" (it should be possible to write them on the back of a napkin). They always have a hierarchical structure, and they are always unique. Both URLs and URNs also share some common syntax, which is described by RFC 2396. The java.net.URI class formalizes these distinctions. The difference between the URI and URL classes is that the URI class does not try to parse the contents of the identifier and apply any "meaning." The URL class immediately attempts to parse the scheme portion of the URL and locate a protocol handler, whereas the URI class doesn't interpret its content. It serves only to allow us to work with the identifier as structured text, according to the general rules of URI syntax. With the URI class, you can construct the string, resolve relative paths, and perform equality or comparison operations, but no hostname or protocol resolution is done.

Java ScreenShot
Comments