novocode.com
About Novocode.com  |  Contact  |  Site Map  |  Search

Need more information? Shop for books about Servlets at Amazon.com:

4.4 Internationalization

The JDK 1.0.2 (and consequently also the JSDK 1.0 which is built on top of it) introduced Streams for byte-based data. Servlets have a ServletInputStream for reading the request body and a ServletOutputStream for writing the response body. When a 16-bit Unicode character is written to an OutputStream the upper byte is discarded. When a byte is read from an InputStream it is converted to a Unicode character by adding a 0 upper byte. Therefore only the ISO-8859-1 character encoding (page 0 of Unicode) can be used with Streams.

The JDK 1.1 added support for other character encodings with Readers and Writers which use char to byte and byte to char converter classes that can be selected by specifying an encoding name.

[API 2.0] In version 2.0+ of the Servlet API Streams are still used for byte-based binary data (as shown in section 2.4). Text should be read from a Reader which is returned by ServletRequest.getReader() and written to a PrintWriter which is returned by ServletResponse.getWriter(). The getReader method returns a Reader which uses the correct character encoding as specified in the request message's Content-Type header. The getWriter() method uses the character encoding which was specified in the response message before requesting the PrintWriter. If no encoding was specified, the Servlet engine may guess an approriate encoding automatically. Even if the character encoding can be guessed it is advisable to specify it explicitly to avoid unnecessary caching of the response body.

Example. The following code sends the greek alphabet (which cannot be represented in ISO-8859-1) in a text/html body using the Unicode-2-0 character encoding:

res.setContentType("text/html;charset=Unicode-2-0");
PrintWriter out = res.getWriter();
for(char c='\u0391'; c<='\u03A9'; c++) out.print(c);

Here is a snapshot of the output:

A browser window showing greek letters

4.5 User Authentication via Sessions

We've already seen sessions that were created implicitly in the ShoppingCartServlet. The sessions were created as needed and only used to identify an anonymous user.

Sessions can also be used for authentication. In contrast to HTTP Basic Authentication a session can be invalidated which enables users to log out without quitting the Web Browser (which is required with Basic Authentication because there is no way to force a browser to delete the authentication credentials).

The following SessionAuthServlet shows how to do authentication with a Servlet. The doPost method processes requests to log in or out. sendPage is called by both, doGet and doPost.

 1:  import java.io.*;
 2:  import javax.servlet.*;
 3:  import javax.servlet.http.*;
 4:
 5:  public final class SessionAuthServlet extends HttpServlet
 6:  {
 7:    protected void doGet(HttpServletRequest req, HttpServletResponse res)
 8:              throws ServletException, IOException
 9:    {
10:      sendPage(req, res, req.getSession(false));
11:    }
12:
13:    protected void doPost(HttpServletRequest req, HttpServletResponse res)
14:              throws ServletException, IOException
15:    {
16:      if(req.getParameter("login") != null)
17:      {
18:        HttpSession session = req.getSession(true);
19:        String name = req.getParameter("name");
20:        if(name == null || name.length()==0) name = "Anonymous";
21:        session.putValue("name", name);
22:        sendPage(req, res, session);
23:      }
24:      else
25:      {
26:        HttpSession session = req.getSession(false);
27:        if(session != null) session.invalidate();
28:        sendPage(req, res, null);
29:      }
30:    }
31:
32:    private void sendPage(HttpServletRequest req, HttpServletResponse res,
33:                          HttpSession session)
34:            throws ServletException, IOException
35:    {
36:      res.setContentType("text/html");
37:      res.setHeader("pragma", "no-cache");
38:      PrintWriter o = res.getWriter();
39:      o.print("<HTML><HEAD><TITLE>SessionAuthServlet</TITLE></HEAD><BODY>");
40:      if(session == null)
41:        o.print("<FORM METHOD=POST>Please enter your name: "+
42:                "<INPUT TYPE=TEXT NAME=\"name\">"+
43:                "<INPUT TYPE=SUBMIT NAME=\"login\" VALUE=\"Log in\">"+
44:                "</FORM></BODY></HTML>");
45:      else
46:        o.print("Hi " + session.getValue("name") +
47:                "<P><FORM METHOD=POST><INPUT TYPE=SUBMIT NAME=\"logout\" "+
48:                "VALUE=\"Log out\"></FORM></BODY></HTML>");
49:      o.close();
50:    }
51:  }

4.6 Relative URLs

When a Servlet creates a response which contains a relative URL, e.g. a hyperlink in an HTML document, you have to make sure that the URL points to the right directory.

Example. The following document is mounted on a server as /shop/preview/form.html:


<HTML><HEAD><TITLE>Form</TITLE></HEAD><BODY>
<FORM ACTION="/servlet/PreviewServlet" METHOD=GET>
...
</FORM>
</BODY></HTML>

There are also product images in the same directory, e.g. /shop/preview/foo.gif, /shop/preview/bar.gif, ...

The Servlet which is mounted as /servlet/PreviewServlet evaluates the form data and creates an HTML page with IMG tags to the appropriate product images, e.g. <IMG SRC="foo.gif">. But this naive approach does not work. The images are in the same directory as the form but the page is created by the Servlet, so the client sees it as /servlet/PreviewServlet. The base name of this URL is /servlet/, thus the image foo.gif from the above image tag is expected at /servlet/foo.gif and not /shop/preview/foo.gif. If /servlet/ is a pure Servlet directory it can only contain Servlets and no other data (like GIF images). It is therefore not possible to move the images to the Servlet directory (/servlet/). Instead the image tags need to be modified to include the full path to the images, relative to the server root, i.e. <IMG SRC="/shop/preview/foo.gif"> for the foo.gif image.

4.7 Logging

A Web Server is usually running as a background process without connected stdio streams. Even if the server is running in a console, a Servlet can not expect to access that console with the System.in, System.out and System.err streams. Instead all messages should be sent to a server log file with one of ServletContext's log methods:

  • public void log(String msg) writes the specified message to the log file.

  • public void log(String msg, Throwable t) [API 2.1] writes the specified message and a stack trace of the Throwable object to the log file. The message will usually be an explanation of an error and the Throwable an exception that caused the error.

  • public void log(Exception exception, String msg) does the same as the previous method. It is available in earlier API versions and deprecated in version 2.1.

Two convenience methods are implemented in GenericServlet. They automatically prefix the messages with the right Servlet name and then call the appropriate ServletContext method:

  • public void log(String msg)

  • public void log(String msg, Throwable cause) [API 2.1]

Exceptions which cause one of the Servlet lifecycle methods (or a do... method in an HttpServlet) to fail do not need to be caught and logged. They are handled automatically by the Servlet Engine.