Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 5 of 5
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ErrorDisplay extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String code = null, message = null, type = null;
Object codeObj, messageObj, typeObj;
// Retrieve the three possible error attributes, some may be null
codeObj = req.getAttribute("javax.servlet.error.status_code");
messageObj = req.getAttribute("javax.servlet.error.message");
typeObj = req.getAttribute("javax.servlet.error.exception_type");
// Convert the attributes to string values
// We do things this way because some old servers return String
// types while new servers return Integer, String, and Class types.
// This works for all.
if (codeObj != null) code = codeObj.toString();
if (messageObj != null) message = messageObj.toString();
if (typeObj != null) type = typeObj.toString();
// The error reason is either the status code or exception type
String reason = (code != null ? code : type);
out.println("<HTML>");
out.println("<HEAD><TITLE>" + reason + ": " + message + "</TITLE></HEAD>");
out.println("<BODY>");
out.println("<H1>" + reason + "</H1>");
out.println("<H2>" + message + "</H2>");
out.println("<HR>");
out.println("<I>Error accessing " + req.getRequestURI() + "</I>");
out.println("</BODY></HTML>");
}
}
But what if the error page could contain the exception stack trace or the URI of the servlet that truly caused the problem (since it's not always the originally requested URI)? With API 2.2, that wasn't possible. With API 2.3, that information is available with two new attributes:
javax.servlet.error.exception: A Throwable object that is the actual exception thrown
javax.servlet.error.request_uri: A String telling the URI of the resource causing problems
Those attributes let the error page include the stack trace of the exception and the URI of the problem resource. The servlet below has been rewritten to use the new attributes. (It fails gracefully if they don't exist, for backward compatibility.)
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ErrorDisplay extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String code = null, message = null, type = null, uri = null;
Object codeObj, messageObj, typeObj;
Throwable throwable;
// Retrieve the three possible error attributes, some may be null
codeObj = req.getAttribute("javax.servlet.error.status_code");
messageObj = req.getAttribute("javax.servlet.error.message");
typeObj = req.getAttribute("javax.servlet.error.exception_type");
throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
uri = (String) req.getAttribute("javax.servlet.error.request_uri");
if (uri == null) {
uri = req.getRequestURI(); // in case there's no URI given
}
// Convert the attributes to string values
if (codeObj != null) code = codeObj.toString();
if (messageObj != null) message = messageObj.toString();
if (typeObj != null) type = typeObj.toString();
// The error reason is either the status code or exception type
String reason = (code != null ? code : type);
out.println("<HTML>");
out.println("<HEAD><TITLE>" + reason + ": " + message + "</TITLE></HEAD>");
out.println("<BODY>");
out.println("<H1>" + reason + "</H1>");
out.println("<H2>" + message + "</H2>");
out.println("<PRE>");
if (throwable != null) {
throwable.printStackTrace(out);
}
out.println("</PRE>");
out.println("<HR>");
out.println("<I>Error accessing " + uri + "</I>");
out.println("</BODY></HTML>");
}
}
Servlet API 2.3 also adds two new request attributes that can help a servlet make an informed decision about how to handle secure HTTPS connections. For requests made using HTTPS, the server will provide these new request attributes:
javax.servlet.request.cipher_suite: A String representing the cipher suite used by HTTPS, if any
javax.servlet.request.key_size: An Integer representing the bit size of the algorithm, if any
A servlet can use those attributes to programmatically decide if the connection is secure enough to proceed. An application may reject connections with small bitsizes or untrusted algorithms. For example, a servlet could use the following method to ensure that its connection uses at least a 128-bit key size.
public boolean isAbove128(HttpServletRequest req) {
Integer size = (Integer) req.getAttribute("javax.servlet.request.key_size");
if (size == null || size.intValue() < 128) {
return false;
}
else {
return true;
}
}
Note: The attribute names in the Proposed Final Draft use dashes instead of underscores; however, they're being changed, as shown here before the Final Release, to be more consistent with existing attribute names.
A number of small changes also made it into the API 2.3 release. First, the getAuthType() method that returns the type of authentication used to identify a client has been defined to return one of the four new static
final String constants in the HttpServletRequest class: BASIC_AUTH, DIGEST_AUTH, CLIENT_CERT_AUTH, and FORM_AUTH. This allows simplified code like:
if (req.getAuthType() == req.BASIC_AUTH) {
// handle basic authentication
}
Of course, the four constants still have traditional String values, so the following code from API 2.2 works too, but is not
as fast or as elegant. Notice the reverse equals() check to avoid a NullPointerException if getAuthType() returns null:
if ("BASIC".equals(req.getAuthType())) {
// handle basic authentication
}
Another change in API 2.3 is that HttpUtils, also known as "the class that never should have been made public," has been deprecated. HttpUtils has always stood out as an odd collection of static methods -- calls that were useful sometimes, but might have been better
placed elsewhere. In case you don't recall, the class contained methods to reconstruct an original URL from a request object
and to parse parameter data into a hashtable. API 2.3 moves this functionality into the request object where it more properly
belongs, and deprecates HttpUtils. The new methods on the request object are:
StringBuffer req.getRequestURL(): Returns a StringBuffer containing the original request URL, rebuilt from the request information.
java.util.Map req.getParameterMap(): Returns an immutable Map of the request's parameters. The parameter names act as keys and the parameter values act as map values. It has not been
decided how parameters with multiple values will be handled; most likely, all values will be returned as a String[]. These methods use the new req.setCharacterEncoding() method to handle character conversions.
API 2.3 also adds two new methods to ServletContext that let you obtain the name of the context and a list of all the resources it holds:
String context.getServletContextName(): Returns the name of the context as declared in the web.xml file.
java.util.Set context.getResourcePaths(): Returns all the resource paths available in the context, as an immutable set of String objects. Each String has a leading
slash ('/') and should be considered relative to the context root.
There's also a new method on the response object to increase programmer control of the response buffer. API 2.2 introduced
a res.reset() method to reset the response and clear the response body, headers, and status code. API 2.3 adds a res.resetBuffer() that clears just the response body:
void res.resetBuffer(): Clears the response buffer without clearing headers or the status code. If the response has already been committed, it throws
an IllegalStateException.
And finally, after a lengthy debate by a group of experts, Servlet API 2.3 has clarified once and for all exactly what happens
on a res.sendRedirect("/index.html") call for a servlet executing within a non-root context. The issue is that Servlet API
2.2 requires an incomplete path like "/index.html" to be translated by the servlet container into a complete path, but doesn't
say how context paths are handled. If the servlet making the call is in a context at the path "/contextpath," should the redirect
URI translate relative to the container root (http://server:port/index.html) or the context root (http://server:port/contextpath/index.html)?
For maximum portability, it's imperative to define the behavior; after lengthy debate, the experts chose to translate relative
to the container root. For those who want context relative, you can prepend the output from getContextPath() to your URI.
Finally, Servlet API 2.3 ties up a few loose ends regarding the web.xml deployment descriptor behavior. It's now mandated that you trim text values in the web.xml file before use. (In standard non-validated XML, all white space is generally preserved.) This rule ensures that the following two entries can be treated identically:
<servlet-name>hello<servlet-name>
and
<servlet-name> hello </servlet-name>
API 2.3 also allows an <auth-constraint> rule, so the special value "*" can be used as a <role-name> wildcard to allow all roles. That lets you write a rule, like the following, that lets all users enter as soon as they've been properly identified as belonging to any role in the Web application:
<auth-constraint> <role-name>*</role-name> <!-- allow all recognized roles --> </auth-constraint>
Lastly, it's been clarified that you can use a role name declared by a <security-role> rule as a parameter to the isUserInRole() method. For example, with the following snippet of a web.xml entry:
<servlet>
<servlet-name>
secret
</servlet-name>
<servlet-class>
SalaryViewer
</servlet-class>
<security-role-ref>
<role-name>
mgr <!-- name used by servlet -->
</role-name>
<role-link>
manager <!-- name used in deployment descriptor -->
</role-link>
</security-role-ref>
</servlet>
<!-- ... -->
<security-role>
<role-name>
manager
</role-name>
</security-role>
the servlet secret can call isUserInRole("mgr") or isUserInRole("manager") -- they will give the same behavior. Basically, security-role-ref acts to create an alias, but isn't necessary. That is what you'd naturally expect, but the API 2.2 specification could be
interpreted as implying that you could only use roles explicitly declared in a <security-role-ref> alias rule. (If that doesn't
make sense to you, don't worry about it; just be aware that things are now guaranteed to work as they should.)
As I've described in this article, Servlet API 2.3 includes an exciting new filter mechanism, an expanded lifecycle model, and new functionality to support internationalization, error handling, secure connections, and user roles. The specification document has also been tightened to remove ambiguities that could interfere with cross-platform deployment. All in all, there are 15 new classes (most involving the new lifecycle event model, the others involving filters), four methods added to existing classes, four new constant variables, and one deprecated class. For a cheat sheet on moving from 2.2 to 2.3, see the sidebar.
Read more about Enterprise Java in JavaWorld's Enterprise Java section.