[ Team LiB ] Previous Section Next Section

The ServletRequest Class

A ServletRequest object contains information about the request from the browser. In addition to obvious things such as access to form variables, the request object can give you information about the kinds of languages that the browser accepts and the character encoding used in the request. ServletRequest is actually a Java interface, not a class. Because you don't have to create the request object, however, it doesn't really matter whether it's an interface or a class. A ServletRequest is typically made available as a parameter to a method, such as service.

The Request Object Is a ServletRequest

graphics/bytheway_icon.gif

The built-in request object used by JSPs is actually an instance of ServletRequest. In practice, it is usually a subclass of HttpServletRequest.


Accessing Request Parameters

Request parameters are values passed to the server two different ways. First, as you have already seen, you can pass parameters in the URL by appending a ? to the URL, followed by parameters in the form name=value separated by &. Here's an example URL with parameters:

http://localhost:8080/servlet/MyServlet?firstName=Kaitlynn&age=4

When the browser uses an HTTP GET request to send the server a form, it automatically passes parameters as part of the URL. The browser can also pass parameters as part of the body of the request. To pass parameters in the request body, the browser must do an HTTP POST, because the HTTP GET request doesn't have a request body, just request headers.

Parameters, Any Way You Like Them

graphics/bytheway_icon.gif

There is actually a third way for your JSP or servlet to receive a parameter. When you use the <jsp:include> or <jsp:forward> tag, you can supply parameters that are added to the set of parameters that the browser passed. You'll learn more about these tags in Hour 10, "Using JSP Standard Actions to Organize Your Web Application."


No matter how the server gets the parameters, you can use the ServletRequest object to access them. You can use getParameter to retrieve a single parameter (or the first parameter if multiple ones are passed) and getParameterValues to retrieve multiple values:


public String getParameter(String name)
public String[] getParameterValues(String name)

If you request a parameter that doesn't exist, both getParameter and getParameterValues return null. You can find out all the parameter names by calling getParameterNames:


public java.util.Enumeration getParameterNames()

If you want access to the Map that contains the names and values of the parameters, you can use


public java.util.Map getParameterMap()

Accessing Request Attributes

Often, you will need to save information that you want to associate with a page, session, or application. You use an attribute to do this. You can store attributes in the request object yourself. Typically, you need to store attributes in the request object when you want to include or forward to another JSP or servlet. Later in the hour, you'll see how to use session and application-level attributes.

You can use getAttribute, setAttribute, and removeAttribute to retrieve, store, and delete attributes:


public Object getAttribute(String name)
public void setAttribute(String name, Object attr)
public void removeAttribute(String name)

Almost anything can be stored as an attribute, since setAttribute only requires an Object for an attribute.

You can get the names of all the attributes stored in the request by calling getAttributeNames:


public java.util.Enumeration getAttributeNames()

Some Attribute Names Are Reserved

graphics/bytheway_icon.gif

Attribute names of the form java.*, javax.*, and com.sun.* are reserved for use by Sun.


Accessing Protocol-Related Information

You occasionally need to get protocol-related information, such as the kind of protocol or the network addresses of the server and the browser. Many Web servers, like Apache, can handle incoming requests for multiple network addresses and ports at one time. A server that can do this is said to be multihomed. You can find out which hostname and port received a request by calling getServerName and getServerPort:


public String getServerName()
public int getServerPort()

As of the Java Servlet 2.4 specification, you can also use the more aptly named getLocalName, getLocalAddr, and getLocalPort:


public String getLocalName()
public String getLocalAddr()
public int getLocalPort()

You can also retrieve the hostname and IP address and port of the machine that sent the request by calling getRemoteHost, getRemoteAddr, and getRemotePort:


public String getRemoteHost()
public String getRemoteAddr()
public Int getRemotePort()

Listing 6.1 shows a servlet that displays the address of the client accessing the server.

Listing 6.1 Source Code for ShowAddressServlet.java
package examples;

import javax.servlet.*;
import java.io.*;

public class ShowAddressServlet extends GenericServlet
{
    public void service(ServletRequest request,
        ServletResponse response)
        throws IOException
    {
// Tell the Web server that the response is HTML.
        response.setContentType("text/html");

// Get the PrintWriter for writing out the response
        PrintWriter out = response.getWriter();

// Write the HTML back to the browser.
        out.println("<html>");
        out.println("<body>");

        String clientHost = request.getRemoteHost();

        out.println("You are accessing this server from "+
            clientHost);

        out.println("</body>");
        out.println("</html>");
    }
}

The getProtocol method returns the name and version of the protocol used to make the request:


public String getProtocol()

The protocol is returned in the form protocol/version, such as http/1.1. If you are familiar with the CGI protocol, the protocol is in the same form as the SERVER_PROTOCOL server variable.

The getScheme method is similar to the getProtocol method:


public String getScheme()

Example schemes would be http, https, and ftp.

You can determine whether a secure protocol was used to make the request by calling the isSecure method:


public boolean isSecure()

When your servlet processes requests made via the secure protocol HTTPS (HTTP over SSL), you can access the client's security certificate, if it is available, with the attribute javax.servlet.request.X509Certificate. Hour 23, "Security," provides more information about client certificates.

Retrieving Request Data

As you saw in Hour 4, "How the Browser Interacts with the Server," an HTTP POST or PUT request can send data to the server. Most of the time, the data is a list of form variables. There is no restriction on what kind of data can be sent, however. You can upload an image to a servlet or a JSP if you want. You can find out the size and type of the data that was sent by calling getContentLength and getContentType:


public int getContentLength()
public String getContentType()

If the content length is unknown, getContentLength returns –1. If the content type is unknown, getContentType returns null.

If the request contains character data, you can find out the kind of character encoding used to write the request. The getCharacterEncoding method returns the encoding type:


public String getCharacterEncoding()

If the request doesn't specify a character encoding, getCharacterEncoding returns null.

If you need to override the character encoding, you can use setCharacterEncoding:


public void setCharacterEncoding(String env)
throws UnsupportedEncodingException

To read the request content, use either getReader or getInputStream:


public java.io.BufferedReader getReader()
    throws IOException
public ServletInputStream getInputStream()
    throws IOException

When the request contains character data, you should use the BufferedReader to read the data. It performs any necessary character conversions. To read binary data, use getInputStream.

It's One or the Other—Readers or InputStreams

graphics/watchout_icon.gif

You can't use both getReader and getInputStream. If you try to use both methods, the second one throws an IllegalStateException.


Handling Uploaded Files

The ServletInputStream class returned by getInputStream is useful when you want to upload a file to a servlet or a JSP. There are many ways to upload a file, but the easiest way is through a Java client.

Listing 6.2 shows a servlet that receives a file uploaded from a Java client (or any client that can send data via an HTTP POST request).

Listing 6.2 Source Code for FileReceiverServlet.java
package examples;
import javax.servlet.*;
import javax.servlet.http.*;

import java.io.*;

/** Receives a file uploaded via a straight HTTP POST request */


public class FileReceiverServlet extends HttpServlet
{
    public void service(HttpServletRequest request,
        HttpServletResponse response)
        throws java.io.IOException, ServletException
    {
// Get the input stream for reading the file.
        InputStream in = request.getInputStream();

// The file is always saved with the same name (just for this demo).
        OutputStream fileOut = new BufferedOutputStream(
            new FileOutputStream("/tmp/uploaded_file"));
// Create a buffer for reading bytes from the input stream.
        byte[] buff = new byte[4096];
        int len;

// Read bytes from the input stream, and write them to the file.
        while ((len = in.read(buff)) > 0)
        {
            fileOut.write(buff, 0, len);
        }
        fileOut.close();

// Send a response back to the client saying the upload was successful.
        response.setContentType("text/plain");
        PrintWriter out = response.getWriter();

        out.println("Upload successful!");
    }
}

Using Other Paths with File I/O and Servlets

graphics/didyouknow_icon.gif

In Listing 6.2, the file's path was hard-coded to /tmp. If you would like to place the file in the Web application's root directory, you can obtain the path using


String path = getServletContext().getRealPath("/");

If you want to use a subdirectory such as "data" off of the application root, replace "/" with "/data".

Additionally, some servlet containers make a temporary directory available to your application in this way:


File dir = (File) getServletContext().getAttribute("javax.servlet.context.tempdir");

Finally, when opening resource for reading, you can use the preferred method


InputStream is = getServletContext().getResourceAsStream("/");

The path given to getResourceAsStream is relative to the context root. Resources obtained this way are available from any source, including a local or remote file system, a database, or a war file. We'll discuss this further in a moment.


Where, Oh Where, Did My File Go?

graphics/watchout_icon.gif

If you neglect to specify the path for your file when you open it to write, the servlet container will decide where the file is written. Tomcat will write the file in its bin/ directory.


Listing 6.3 shows the Java client class that performs the upload.

Listing 6.3 Source Code for Uploader.java
package examples;

import java.net.*;
import java.io.*;

/** Performs an HTTP POST to upload a file to a servlet */
public class Uploader
{
    public static void main(String[] args)
    {
        if (args.length < 1)
        {
            System.out.println("Please supply the name of the file to upload.");
            System.exit(1);
        }

        try
        {
            File inFile = new File(args[0]);

// Open the file to be uploaded.
            InputStream in = new BufferedInputStream(
                new FileInputStream(inFile));

// Create a URL object for the destination servlet.
            URL destination = new URL("http://localhost:8080/servlet/"+
                "examples.FileReceiverServlet");

// Open a connection to the servlet.
            URLConnection conn = destination.openConnection();

// Tell the connection that there is output to be sent.
            conn.setDoOutput(true);

// Tell the receiver that this is a stream of bytes.
            conn.setRequestProperty("Content-type",
                "application/octet-stream");

// Tell the receiver how many bytes there are.
            conn.setRequestProperty("Content-length",
                ""+inFile.length());

            OutputStream out = conn.getOutputStream();

            byte[] buff = new byte[4096];

            int len;

// Copy bytes from the file to the output stream.
            while ((len = in.read(buff)) > 0)
            {
                out.write(buff, 0, len);
            }

            InputStream response = conn.getInputStream();

// Read the response back from the receiver servlet.
            while ((len = response.read(buff)) > 0)
            {
                System.out.write(buff, 0, len);
            }
        }
        catch (Exception exc)
        {
        }
    }
}

You don't always have the luxury of writing a Java client to perform the upload. Sometimes you must perform a file upload from an HTML page. Listing 6.4 shows a simple HTML page that enables you to select a file to upload.

Listing 6.4 Source Code for FileUploader.html
<html>
<body bgcolor="#ffffff">
<h1>File Uploader</h1>
<form action="/servlet/examples.FileReceiverServlet" method="post"
    enctype="multipart/form-data">
Please select a file to upload: <input type="file" name="foo">
<p>
<input type="submit" value="Upload File!">
</form>
</body>
</html>

The difficulty with receiving a file uploaded from an HTML form is that the content is of the type multipart/form-data. When you upload a file in this format, the content type is usually something like


multipart/form-data; boundary=-------------------
    --------7d03d0287003dc

The actual content starts with an additional header, too:


-----------------------------7d03d0287003dc
Content-Disposition: form-data; name="foo";
    filename="H:\jspbook\ch13\examples\Uploader.java"
Content-Type: application/octet-stream

Because the upload can contain multiple parts, the boundary string is used to separate each part. After the boundary, as shown previously, are several header lines terminated by a blank line. After you read the blank line, the rest of the data is content until you reach another boundary.

Listing 6.5 shows a servlet that can wade through the multipart headers and retrieve an uploaded file.

Listing 6.5 Source Code for ReceiveUploadServlet.java
package examples;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class ReceiveUploadServlet extends GenericServlet
{
    public void service(ServletRequest request,
        ServletResponse response)
        throws IOException
    {
// Get the content type.
        String contentType = request.getContentType();

// Set a default for the boundary string length.
// Find out where the boundary string starts.
        int boundaryIndex = contentType.indexOf("boundary=");

// Get the boundary string.
        String boundary = contentType.substring(boundaryIndex+9);
        int boundaryStrLength = boundary.length();

// Get the input stream to read the uploaded file.
        ServletInputStream servIn = request.getInputStream();

        DataInputStream in = new DataInputStream(servIn);

// Lop off the headers from the content (read until you get a blank line).
        String line;

        while ((line = in.readLine()) != null)
        {
            getServletContext().log("Got line: "+line);
            if (line.trim().length() == 0) break;
        }

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream(
            request.getContentLength());

        byte[] buffer = new byte[4096];
        int len;

// Copy the uploaded file to a byte array.
        while ((len = in.read(buffer)) > 0)
        {
            byteOut.write(buffer, 0, len);
        }

        byte[] outBytes = byteOut.toByteArray();

        FileOutputStream fileOut = new FileOutputStream("/tmp/uploaded_file");

// Write the byte array out to the file, trim off the boundary plus some
// padding surrounding the boundary.
        fileOut.write(outBytes, 0, outBytes.length - boundaryStrLength - 8);
        fileOut.close();

// Tell the Web server that the response is HTML.
response.setContentType("text/html");

// Get the PrintWriter for writing out the response
        PrintWriter out = response.getWriter();

// Write the HTML back to the browser.
        out.println("<html>");
        out.println("<body>");

        String clientHost = request.getRemoteHost();

        out.println("File Accepted, thank you.");

        out.println("</body>");
        out.println("</html>");
    }
}

You can use this servlet with FileUploader.html (Listing 6.4). Just remember to change the form action to point to examples.ReceiveUploadServlet. You may need to clear your browser cache to get it to recognize the new version.

Getting Locale Information

When a browser requests a page, it can send a list of locales that it supports. If you are not familiar with the internationalization features of Java, a locale specifies how Java should represent certain data items, such as dates and currencies. You can even determine the client's preferred language in case you want to support multiple languages on your Web site. You will learn more about locales and internationalization in Hour 22, "Internationalization."

The getLocale method returns the browser's preferred locale, and getLocales returns an enumeration of all the locales the browser says it supports:


public java.util.Locale getLocale()
public java.util.Enumeration getLocales()
    [ Team LiB ] Previous Section Next Section