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.
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:
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.
| SessionAuthServlet.java
|
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)
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:
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.