Uniform Resource Locators (URLs) are perhaps the most noticeable and least understood aspect of the Internet phenomenon. You would be hard-pressed to find a magazine or television program today that doesn't include the now familiar Web address of the company paying for an advertisement. It is not uncommon to hear a newscaster or radio personality confidently recite a URL on the air, "h-t-t-p-colon-slash-slash...." Fortunately, all the public needs to know is that if they type this odd-looking sequence of characters into their Web browser, graphically presented information and, thanks to Java, active content will fill the screen.
This chapter discusses the definition of a URL, tells how it can be used to locate Web and other resources, and shows what classes are provided by Java to allow you to seamlessly work with URLs in your programs.
Although the World Wide Web has dominated the Internet in recent years, there are many other types of resources that provide a diverse range of services on the Internet. For example, electronic mail, file transfer, and search tools were among the first services to be developed for the Internet. In fact, much of the success of the Web is due to its capability to bring together many of these resources into one interface. Indeed, the two most popular Web browsers available today are able to perform file transfers, send and receive electronic mail, and interact with news services in addition to their normal Web duties. The URL is the key to defining the method, location, and name of the many types of resources available on the Internet.
The general format of a URL is composed of a protocol name, a colon, and some protocol specific information about a resource:
protocol name:protocol specific information
A resource's protocol is the language used to interpret the data
that is shared across a network connection about that resource.
The protocol name portion is not case-sensitive and can include
letters, numbers, a plus sign (+), period (.),
and hyphen (-). The protocol-specific information portion
can include any character. However, non-printable, reserved, and
unsafe characters must be escaped, or encoded, to ensure the original
meaning of the URL is maintained. Once received by the server,
the URL is decoded before it is interpreted. Characters are escaped
by replacing them with the percent (%) sign and their
hexadecimal value. For example, if the less-than (<)
character is to be used in a URL, it must be escaped to %3C
because it is one of the unsafe characters. The unsafe group of
characters includes <, >, ",
#, %, {, }, [, ],
|, \, ^, ~, and '.
These characters have special meaning as part of the URL itself
or on the systems transporting or interpreting the URL. For example,
the tilde (~) is commonly used in a Web URL to designate
the path to a user's home directory on a server. A URL of http://www.jory.com/~james/homepage.html
points to my personal home page on my server located in my user
directory (/james). If I wanted to use a tilde as part
of my home page filename (home~page.html), the tilde
in the filename should be escaped to eliminate any confusion over
the tilde used to designate my user directory. The correct URL
would then be http://www.jory.com/~james/home%7Epage.html.
| NOTE |
See Request for Comments (RFC) 1738 for more information on URLs. The current library of RFCs can be found on the accompanying CD-ROM. |
Table 17.1 lists some of the more common URLs for resources found
on the Internet.
| Resource | Sample URL |
| Hypertext Transfer Protocol (HTTP) | http://somehost.com |
| Simple Mail Transfer Protocol (SMTP) | mailto:someone@somehost.com |
| File Transfer Protocol (FTP) | ftp://somehost.com/someplace/somefile.ext |
| Local file | file:/c:/someplace/somefile.ext |
| News (Usenet News) | news:some.news.group |
| Telnet (interactive session) | telnet://somehost.com |
| Wide Area Information Server (WAIS) | wais://somehost.com/database?search |
The URLs with two slashes after the colon use a common syntax for the protocol-specific information. These types of URLs are used to access a resource on a specific host. The general format for host-based resources is defined like this:
protocol name://user:password@host:port/path
The user, password, and port fields
are optional and if missing will cause the default behavior to
be used. For example, FTP URLs found on Web pages typically do
not include a user and password. In these cases, the Web browser
attempts to initiate an anonymous FTP session by logging on to
the FTP server on your behalf using anonymous as the
user ID and your electronic mail address as the password. Likewise,
if the port is missing from the URL, the default port is used
based on the specified protocol. For example, the HTTP protocol
uses port 80 by default.
| NOTE |
A port represents the logical connection point between a client and server process communicating over a network connection. It is analogous to a channel on television or frequency on the radio. Port numbers up to and including 1024 are reserved for system use and when assigned are often referred to as well-known sockets, services, or ports. Port numbers above 1024 and below 65535 are unassigned and can be used for user applications. Ports and the underlying protocol suite used to transport data across the Internet are discussed in Chapter 18, "Networking with Datagrams and Sockets." |
Now that you have seen how URLs are used to identify resources on the Internet, take a look at the classes provided by Java to access them.
Encapsulating the inherent complexity and flexibility of all known
URL formats as well as those to come in a set of classes would
certainly be a daunting task. Fortunately, the Java implementers
have taken care of this for you. The java.net package includes
four URL-related classes and one interface. The URL class
drives the whole process and takes care of interacting with the
other classes. Adding support for new or custom URL formats is
also possible by subclassing the appropriate classes, which will
be shown later in this chapter.
| NOTE |
Unfortunately, security constraints imposed by Web browsers limit the type of URLs and the hosts that you can communicate with in your applets. In general, you are limited to communicating with the host serving your applet or the HTML page containing your applet and only then using protocols supported and allowed by the browser's Java Virtual Machine. The current implementations of Netscape's Navigator and Microsoft's Internet Explorer limit your applets to working with HTTP URLs only. However, there are a few ways to get around this restriction. Of course, Java applications are not bound by the same constraints. |
As a result of the restriction placed on applets to have access
only to Web URLs, the design of the URL classes were naturally
slanted toward HTTP resources. The default behavior is to expect
a Web URL, but these effects can be overcome through subclassing
and overriding the appropriate methods. Table 17.2 lists the methods
of interest from the URL class.
| Method | Description |
| URL(String, String, int, String) | Creates an absolute URL based on the protocol, host, port, and filename passed as parameters. |
| URL(String, String, String) | Creates an absolute URL based on the protocol, host, and filename passed as parameters. Because the port is not specified, the default port is used based on the protocol. |
| URL(String) | Creates an absolute URL based on the unparsed string passed as a parameter. |
| URL(URL, String) | Creates a new URL relative to the URL passed as a parameter using the filename parameter. |
| int getPort() | Returns the port number of the URL. |
| String getProtocol() | Returns the protocol name of the URL. |
| String getHost() | Returns the host name of the URL. |
| String getFile() | Returns the filename of the URL. |
| String getRef() | Returns the reference of the URL. |
| URLConnection openConnection() | Returns an instance of a URLConnection via an internal reference to a URLStreamHandler object. Depending on the protocol of the URL, an actual connection to the host may not be made at this time. |
| InputStream openStream() | Opens a connection and returns an InputStream to the URL. If an InputStream is not supported by the URL, an UnknownServiceException is thrown. |
| Object getContent() | Opens a connection and returns an object representing the contents of the URL. |
| SetURLStreamHandlerFactory (URLStreamHandlerFactory) | A static method that sets the URLStreamHandlerFactory based on the parameter. |
Each URL constructor will throw a MalformedURLException
if a URLStreamHandler could not be found for the URL's
protocol. Once constructed, access to the actual resource associated
with the URL can be obtained through the openConnection(),
openStream(), or getContent() methods. The method
used depends on the nature of the resource to which you are connecting.
If you need detailed information about the resource and want to
perform input and output operations, use openConnection(),
which provides the most control. The SMTP example applet discussed
later in this chapter uses this type of access. However, if you
just need read access to the resource (such as a text file), openStream()
will do the job. Finally, if a content handler is available for
the resource, getContent() will return an instance of
an Object that represents the resource. The current implementation
of Java includes content handlers for the following MIME types:
text/plain, image/gif, image/jpeg,
image/x_xbitmap, and image/x_xpixmap. Content
handlers will be discussed again later in this chapter.
| NOTE |
The Multipurpose Internet Mail Extensions (MIME) standard was created to expand the type of data that could be delivered with electronic mail. Under MIME, non-text-based attachments to electronic mail notes are preceded by header information capable of describing the type of data in the attachment, the encoding scheme used, and even the application that should be used to interpret the data. The Content-type header field is the key to interpreting the resource. In its basic form, the content-type is composed of a type and subtype in the form type/subtype. For example, a content-type of text/html defines a resource (such as a Web page) that is text-based and in HTML format. Because the nature of the resources found on the Web are also as diverse as the types of attachments to electronic mail, MIME was adopted for the Web and thus is supported by Java. |
| CAUTION |
Unless you intend to open more than one connection to a resource, do not call openConnection(), openStream(), or getContent() more than once, because each call opens a new connection. Calling one of these methods and then another has the same effect because all three methods refer to the same resource. |
URLStreamHandler is an abstract base class used to create
connections to URL resources as well as provide parsing functionality
for URLs. To create handlers for new or currently unsupported
URLs, this class must be subclassed. The current implementation
of Java provides handlers for the HTTP, file, doc, and news URLs.
However, as mentioned, only http resources can be used by applets.
Table 17.3 lists the methods of interest for URLStreamHandler.
| Method | Description |
| URLConnection openConnection(URL) | This abstract protected method is called by URL to create a connection to the specified URL. This method must be overridden by a subclass. |
| parseURL(URL, String, int, int) | URL objects created with and based on unparsed URLs will call this method to interpret the unparsed URL and give the URLStreamHandler an opportunity to interpret the URL based on its own format. |
Remember from our discussion of the URL class that the URL class was designed primarily with the HTTP protocol and URL format in mind? Well, for protocols that have URL formats different from HTTP, the URL class will call parseURL() to give the URLStreamHandler an opportunity to parse the URL in its intended context. Once successfully parsed, URLStreamHandler sets the parsed URL values in the attached URL object through the protected setURL() method.
This is another abstract base class used in conjunction with the
URL and URLStreamHandler classes. It provides
more direct access to the resource than the URL class,
as well as detailed MIME information about the resource. Table
17.4 lists several methods from URLConnection.
| Method | Description |
| connect() | This abstract protected method is called by URL to create a connection to the specified URL. This method must be overridden by a subclass. |
| URL getURL() | Returns the URL object associated with this connection. |
| int getContentLength() | Returns the length of the resource in bytes. |
| String getContentType() | Returns the content-type for the resource. |
| String getContentEncoding() | Returns the content-encoding for the resource. |
| long getExpiration() | Returns the expiration of the resource. |
| long getDate() | Returns the sending date of the resource. |
| long getLastModified() | Returns the date that the resource was last modified. |
| Object getContent() | Returns an instance of an object representing the contents of the resource. |
| InputStream getInputStream() | Returns an input stream to the resource. |
| OutputStream getOutputStream() | Returns an output stream to the resource. |
| SetContentHandlerFactory (ContentHandlerFactory) | Sets the ContentHandlerFactory to the value passed as a parameter. |
Depending on the type of resource and the level of detail available in the MIME header for the resource, some of the methods listed in Table 17.4 might not return valid information.
Recall from the URL class that when a URL object is created, a URLStreamHandler must be found that supports the protocol of the resource. If not, a MalformedURLException is thrown. Part of the process of finding a URLStreamHandler for the resource involves consulting a reference to an object that implements the URLStreamHandlerFactory interface. If the URLStreamHandlerFactory's createURLStreamHandler() method does not support the requested protocol or a URLStreamHandlerFactory was not set in URL via the URL.setURLStreamHandlerFactory() method, URL will check the protocol against Java's built-in protocol handlers as a last resort. Figure 17.1 illustrates the process that URL goes through to find a URLStreamHandler for a resource.
Figure 17.1 : URL's search for a URLStreamHandler.
| NOTE |
After the URL.setURLStreamHandlerFactory() has been called once, it cannot be changed again. For applets, a default URLStreamHandlerFactory that only supports HTTP URLs is set when the applet's frame is initialized. This is the reason support for new or unsupported URL formats cannot be added to applets. However, default URLStreamHandler-Factory is not set for Java applications, so you are free to set your own. |
Now that we have covered the main URL-related classes, let's subclass them to create a basic Simple Mail Transfer Protocol (SMTP) client application.
To illustrate how new or unsupported URL formats can be added to Java applications, the following example will implement the mailto URL. Three utility classes will be constructed to complete the example: the SmtpURLConnection, SmtpURLStreamHandler, and SmtpURLStreamHandlerFactory classes.
Let's start with the SmtpURLStreamHandlerFactory class. Once it is set as the default URLStreamHandlerFactory for the URL class, its createURLStreamHandler() is called whenever a URL is created for a resource for which URL does not already have a URLStreamHandler. Our handler factory's job is to return an instance of SmtpURLStreamHandler when a mailto URL is requested. Otherwise null is returned, which causes URL to check the protocol against the built-in types. Listing 17.1 shows the source code for SmtpURLStreamHandlerFactory.
Listing 17.1. SmtpURLStreamHandlerFactory.java.
import java.net.*;
import SmtpURLStreamHandler;
public class SmtpURLStreamHandlerFactory implements URLStreamHandlerFactory
{
public URLStreamHandler createURLStreamHandler(String protocol)
{
if (protocol.equalsIgnoreCase("mailto"))
return new SmtpURLStreamHandler();
else
return null;
}
}
The next class, SmtpURLStreamHandler, is a subclass of URLStreamHandler. Its jobs are to parse the mailto URL for the receiver's electronic mail ID and create a new SmtpURLConnection object when the openConnection() method is called. Listing 17.2 lists the source code for this class.
Listing 17.2. SmtpURLStreamHandler.java.
import java.net.*;
import java.io.*;
public class SmtpURLStreamHandler extends URLStreamHandler
{
String to;
protected URLConnection openConnection(URL u)
{
return new SmtpURLConnection(u, to);
}
protected void parseURL(URL u, String spec, int start, int limit)
{
String protocol = u.getProtocol();
String host = u.getHost();
int atSign = spec.indexOf('@', start);
if (atSign == -1)
to = "";
else
{
to = spec.substring(start, atSign - 1);
host = spec.substring(atSign + 1, limit);
}
setURL(u, protocol, host, 25, "", "");
}
}
The last SMTP class is SmtpURLConnection and is a subclass of URLConnection. It provides methods to set the from and to address for the note as well as sending the note itself. An output stream can also be retrieved from SmtpURLConnection but not an input stream. The smtpClient private data member, borrowed from the sun.net.smtp package, actually takes care of reading and validating the responses from the SMTP server. Listing 17.3 lists the source code for SmtpURLConnection.
Listing 17.3. SmtpURLConnection.java.
import java.net.*;
import java.io.*;
public class SmtpURLConnection extends URLConnection
{
private sun.net.smtp.SmtpClient smtpClient;
protected String to;
protected String from;
protected String smtpServer;
public SmtpURLConnection(URL u, String to)
{
super(u);
this.to = to;
smtpClient = null;
}
public void setFrom(String from)
{
this.from = from;
}
public void setTo(String to)
{
this.to = to;
}
public void setSmtpServer(String smtpServer)
{
this.smtpServer = smtpServer;
}
public void sendIt() throws IOException
{
if (connected)
{
smtpClient.closeServer();
connected = false;
}
else
throw new IOException("nothing to send.");
}
public void closeServer() throws IOException
{
if (connected)
smtpClient.closeServer();
}
protected void finalize()
{
try
{
closeServer();
}
catch (IOException e) {}
}
public void connect() throws IOException
{
if (!connected)
{
smtpClient = null;
try
{
smtpClient = new sun.net.smtp.SmtpClient(smtpServer);
}
catch (Throwable e)
{
if (smtpClient != null)
smtpClient.closeServer();
if (e instanceof SecurityException)
{
throw (SecurityException)e;
}
throw (e instanceof IOException ? (IOException) e
: new IOException(e.toString()));
}
if (smtpClient == null)
throw new IOException("Couldn't connect to " + smtpServer);
connected = true;
smtpClient.from(from);
smtpClient.to(to + "@" + url.getHost());
}
}
public OutputStream getOutputStream() throws IOException
{
if (!connected)
connect();
return smtpClient.startMessage();
}
}
Finally, the source for the SMTP client application is shown in Listing 17.4, and a screen shot is shown in Figure 17.2. The bulk of the code deals with setting up and managing the interface of the application. The critical sections are the last line of the constructor where the URL URLStreamHandlerFactory is set to SmtpURLStreamHandlerFactory, and the send() method where the mail is sent to the recipient.
Figure 17.2 : SMTP client application in action.
Listing 17.4. EX17A.java.
import java.awt.*;
import java.net.*;
import java.io.*;
import SmtpURLConnection;
import SmtpURLStreamHandler;
import SmtpURLStreamHandlerFactory;
public class EX17A extends Frame
{
TextField from, to, smtpServer, status;
TextArea note;
Button send, close;
public EX17A(String caption)
{
super(caption);
from = new TextField(20);
to = new TextField(20);
smtpServer = new TextField(40);
note = new TextArea(10, 40);
status = new TextField(40);
status.setEditable(false);
send = new Button("Send");
close = new Button("Close");
Label l;
GridBagLayout gbag = new GridBagLayout();
GridBagConstraints cons = new GridBagConstraints();
cons.insets = new Insets(5, 5, 5, 5);
setLayout(gbag);
cons.anchor = GridBagConstraints.NORTHWEST;
l = new Label("From:");
gbag.setConstraints(l, cons);
add(l);
cons.gridwidth = GridBagConstraints.REMAINDER;
cons.fill = GridBagConstraints.HORIZONTAL;
gbag.setConstraints(from, cons);
add(from);
cons.gridwidth = 1;
cons.fill = GridBagConstraints.NONE;
l = new Label("To:");
gbag.setConstraints(l, cons);
add(l);
cons.gridwidth = GridBagConstraints.REMAINDER;
cons.fill = GridBagConstraints.HORIZONTAL;
gbag.setConstraints(to, cons);
add(to);
cons.gridwidth = 1;
cons.fill = GridBagConstraints.NONE;
l = new Label("SMTP Server:");
gbag.setConstraints(l, cons);
add(l);
cons.gridwidth = GridBagConstraints.REMAINDER;
cons.fill = GridBagConstraints.HORIZONTAL;
gbag.setConstraints(smtpServer, cons);
add(smtpServer);
cons.fill = GridBagConstraints.NONE;
cons.insets.bottom = 0;
l = new Label("Note:");
gbag.setConstraints(l, cons);
add(l);
cons.insets.bottom = 5;
cons.weighty = 1.0;
cons.fill = GridBagConstraints.BOTH;
gbag.setConstraints(note, cons);
add(note);
cons.gridwidth = 1;
cons.fill = GridBagConstraints.NONE;
cons.weighty = 0;
l = new Label("Status:");
gbag.setConstraints(l, cons);
add(l);
cons.gridwidth = GridBagConstraints.REMAINDER;
cons.fill = GridBagConstraints.HORIZONTAL;
cons.weightx = 1.0;
gbag.setConstraints(status, cons);
add(status);
Panel buttons = new Panel();
buttons.setLayout(new FlowLayout(FlowLayout.CENTER, 20, 5));
buttons.add(send);
buttons.add(close);
cons.gridwidth = GridBagConstraints.REMAINDER;
cons.fill = GridBagConstraints.NONE;
cons.anchor = GridBagConstraints.CENTER;
gbag.setConstraints(buttons, cons);
add(buttons);
pack();
show();
URL.setURLStreamHandlerFactory(new SmtpURLStreamHandlerFactory());
}
public boolean handleEvent(Event evt)
{
if (evt.target == send)
{
if (to.getText().length() > 0 && smtpServer.getText().length() > 0)
send();
else
status.setText("Enter destination address and SMTP Server.");
return true;
}
else if (evt.target == close || evt.id == Event.WINDOW_DESTROY)
{
dispose();
System.exit(0);
return true;
}
else
return super.handleEvent(evt);
}
protected void send()
{
SmtpURLConnection mailConn = null;
status.setText("Sending note...");
try
{
URL url = new URL("mailto:" + to.getText());
mailConn = (SmtpURLConnection)url.openConnection();
mailConn.setFrom(from.getText());
mailConn.setSmtpServer(smtpServer.getText());
PrintStream os = new PrintStream(mailConn.getOutputStream());
os.print(note.getText());
mailConn.sendIt();
status.setText("Note successfully sent to " + to.getText());
}
catch (MalformedURLException e)
status.setText("URL Error: " + e);
catch (UnknownHostException e)
status.setText("Host could not be found. Try again.");
catch (IOException e)
status.setText("Stream Error: " + e);
finally
{
if (mailConn != null)
{
try
mailConn.closeServer();
catch (IOException e) {}
}
}
}
public static void main(String args[])
{
new EX17A("Java SMTP Client");
}
}
The Common Gateway Interface (CGI) and Internet Server Application Programming Interface (ISAPI) are commonly used on Web sites to get information from visitors. An HTML page can be created with input controls such as edit fields, list boxes, and check boxes, using the HTML FORM, INPUT, TEXTAREA, and SELECT tags. When the form is submitted back to the server by the user, the browser simply packages the responses in a URL-encoded sequence of characters and either sends the output on the end of the URL (GET method) or in a separate transaction (POST method). The method used to send the responses back to the server is specified in the Web page.
Using the classes already covered in this chapter plus the URLEncoder class discussed below, you will see how Java applets can extend the limited use of CGI scripts and ISAPI extensions by providing a more robust and sophisticated interface.
URLEncoder is a utility class that cannot be instantiated (the default constructor is declared private) but provides a static method called encode() that returns an x-www-form-urlencoded version of the string passed as an argument. The x-www-form-urlencoded format has all unsafe, reserved, and non-printable characters encoded, or escaped, within the string. See the section titled "What Is a URL?" at the beginning of this chapter for a more detailed description of URL encoding.
The applet shown in Figure 17.3 allows the user to search for detailed information about a product by entering the name of the product and pressing the Search button. The encode() method is used to format the product name before it is sent back to the Web server using an HTTP POST transaction. Since the applet is using the HTTP protocol to communicate with the server, it does not matter if the server processes the request using a CGI script or ISAPI extension. Listing 17.4 includes the source code for the applet.
Figure 17.3 : CGI or ISAPI from an applet.
Listing 17.4. EX17B.java.
import java.applet.Applet;
import java.awt.*;
import java.net.*;
import java.io.*;
public class EX17B extends Applet
{
TextField prodName;
TextArea prodInfo;
Button search;
Label status;
URL url;
public void init()
{
prodName = new TextField(30);
prodInfo = new TextArea(10, 30);
prodInfo.setEditable(false);
search = new Button("Search");
status = new Label();
Label l;
GridBagLayout gbag = new GridBagLayout();
GridBagConstraints cons = new GridBagConstraints();
cons.insets = new Insets(5, 5, 5, 5);
setLayout(gbag);
l = new Label("World Distributors Product Information");
l.setFont(new Font("Helvetica", Font.BOLD, 18));
cons.gridwidth = GridBagConstraints.REMAINDER;
gbag.setConstraints(l, cons);
add(l);
cons.anchor = GridBagConstraints.NORTHWEST;
cons.gridwidth = 1;
l = new Label("Product Name:");
gbag.setConstraints(l, cons);
add(l);
cons.fill = GridBagConstraints.HORIZONTAL;
gbag.setConstraints(prodName, cons);
add(prodName);
cons.fill = GridBagConstraints.NONE;
cons.gridwidth = GridBagConstraints.REMAINDER;
gbag.setConstraints(search, cons);
add(search);
cons.fill = GridBagConstraints.HORIZONTAL;
gbag.setConstraints(status, cons);
add(status);
l = new Label("Product Information:");
gbag.setConstraints(l, cons);
add(l);
cons.fill = GridBagConstraints.BOTH;
gbag.setConstraints(prodInfo, cons);
add(prodInfo);
resize(300, 400);
}
public boolean handleEvent(Event evt)
{
if (evt.target == search)
{
if (prodName.getText().length() > 0)
search();
else
status.setText("Please enter a product name.");
return true;
}
else
return super.handleEvent(evt);
}
public void start()
{
try
url = new URL("http", getCodeBase().getHost(),
getParameter("script"));
catch (MalformedURLException e)
{
status.setText("Error initializing URL to server.");
url = null;
}
}
public void search()
{
if (url == null)
{
status.setText("Unknown URL; cannot perform query.");
return;
}
try
{
status.setText("Querying server...");
URLConnection conn = url.openConnection();
PrintStream os = new PrintStream(conn.getOutputStream());
os.println(URLEncoder.encode(prodName.getText()));
os.close();
status.setText("Retrieving response...");
DataInputStream is = new DataInputStream(conn.getInputStream());
String line = is.readLine();
prodInfo.setText("");
while (line != null)
{
prodInfo.appendText(line + "\n");
line = is.readLine();
}
is.close();
status.setText("");
}
catch (IOException e)
{
status.setText("Error processing query: " + e);
}
}
}
Because the URL to the script will not change during the life of the applet, the start() method creates the URL once. To allow this applet to be reused without needing to be modified, only the name of the CGI script or ISAPI extension in the HTML page must be changed. The search() method is called each time the user presses the Search button. Within search(), a connection is made to the CGI script, an output stream is obtained from the URLConnection, and the contents of the Product Name field are encoded and sent to the script for processing. To retrieve the results from the script, an input stream is obtained from the connection and each line is read and appended to the Product Information TextField.
Although this is a simple application of using a CGI script or ISAPI extension with a Java applet, the advantages of using an applet over a traditional HTML form are apparent. Not only can client-side edits be performed with applets to eliminate unnecessary submissions of requests to the server, but the look and feel of the applet is much more interactive and sophisticated.
Like URLStreamHandlers, content handlers can be used to extend Java's built-in support for Internet resources. Whereas URLStreamHandlers apply to URLs, content handlers are used to wrap the content of resources into Java-based objects. However, content handlers are much less restricted in applets than URLStreamHandlers. Indeed, you are free to create your own custom content handlers and content handler factories in applications as well as applets.
ContentHandler is an abstract base class that declares the getContent() method called by URLConnection. To define your own content handler, you must subclass ContentHandler and provide a getContent() method to create an Object instance representing your resource. Rather than create a custom content handler or call Applet.getImage(), the applet in Listing 17.5 invokes the content handler for an image/gif resource by calling the getContent() method of URLConnection.
Listing 17.5. EX17C.java.
import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import sun.awt.image.URLImageSource;
public class EX17C extends Applet
{
Image img;
String error = "";
public void init()
{
resize(200, 100);
}
public void paint(Graphics g)
{
if (error.length() > 0)
g.drawString(error, 10, 10);
else
g.drawImage(img, 10, 10, getBackground(), this);
}
public void start()
{
try
{
URL url = new URL("http://www.jory.com/gifs/someimage.gif");
URLImageSource imgsrc = (URLImageSource)url.getContent();
img = Toolkit.getDefaultToolkit().createImage(imgsrc);
}
catch (MalformedURLException me)
error = me.toString();
catch (IOException e)
error = e.toString();
}
}
Following along in the code in Listing 17.5, a URL to the image is created as normal. However, instead of calling the getImage() method of the Applet class, the URLImageSource content handler is used. Once the content handler is created, the image itself can be created by calling the toolkit's createImage() method. Now the image can be displayed in the applet's paint() method. In fact, this is the same process that getImage() goes through to create images! Of course, you would normally use getImage() to load images in your applets, but this example illustrates how content handlers are used for accessing resources. Before running the example, be sure to change the URL to point to a valid image file either on a Web server or your local hard disk.
The ContentHandlerFactory functions exactly like the URLStreamHandlerFactory interface, except for content handlers. To create your own content handler factory, implement the ContentHandlerFactory interface in a class and call URLConnection.setContentHandlerFactory() to install your factory. Only one content handler factory can be set for URLConnection, though. Fortunately, the content handler factory is not automatically set for applets like the URL stream handler factory in URL.
The URL classes and content handlers provide a powerful and extensible framework for implementing handlers for your own resource and MIME types. Hopefully, the security constraints that currently limit the usefulness of URLs in applets will be lifted in the future so that these classes can be used to their fullest potential.