Java Servlet Programming, Second Edition By Jason Hunter with William Crawford Unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. This page was updated February 27, 2008. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification UNCONFIRMED errors and suggestions from readers: {xi} New Atlanta Communication haven't been bought by Unify Corp. You can look at: http://www.servletexec.com/rescission/rescission.jsp [xvi] first paragraph; www.servlets.com does not exist anymore... Where can a reader find the com.oreilly.servlet package? It would be helpful if you could find a new "home" for it and post the link in the book's webpage. {3} last paragraph; FastCGI *does* support multiple concurrent requests per process, contrary to the description on this page. It uses a capability protocol to ask each application whether it is capable of handling concurrent requests. (23) 3rd line; Incorrect English grammar. Should be "If you don't mind MY asking," The gerund "asking" in this context demands the possessive case. (29) Example 2-4; Two ssi calls are made in the .shtml with the same parameter (zone), but with different values (first GMT, then EST). The resultant output uses the first input parameter for both calls to the ssi servlet. This behavior was observed using IE5 and jws2.0 on NT4.0. (42) First and subsequent paragraphs; In the Apache tomcat 4.0.1 I downloaded, servlets don't reload by default. The context (in sever.xml) for their "examples" servlets is set-up to reload automatically, but not the ROOT. I had fun figuring it out, but I imagine others might be miffed. {50} Near the bottom of the page; The code, as written, does not make use of the local_count variable. I believe the author's intent was to create a "small as possible" synchronization scope. I believe the author intended the code to be: int local_count; synchronized(this) { local_count = ++count; } if(count % 10 ==0) saveState(); out.println("......"); // etc., etc. (60) 1st paragraph 3rd line; line 3 should read "then automatically present" at the moment it reads "then automatically resent" {61} In PrintMessages method: First, in the while loop towards the bottom of the page, null checks are performed e.g. if (name == null) name = "Unknown user"; The problem is that these will never be performed because what's returned if a user types in no information is an empty string, which is not the same as a null in java. So each of these lines should be if (name.equals("")) name = "Unknown user"; the other problem is in the email null check it is listed as if (name == null) email = "Unknown email"; The problem is the email should be checked not the name so this should be if (email.equals("")) name = "Unknown email"; This whole issue is not very serious though because it's an empty string and not a null so there are no null pointer problems to worry about. This only make's sure that something is printed if the user doesn't enter anything, which was the authors intention. [62] Example 3-10 in handleForm() method: When you set the lastModified time in handleForm you use System.currentTimeMillis(). On page 59 in the Client-Side caching section you mention that "All times returned by getLastModified() should be rounded down..." because the Last-Modified and If-Modified-Since headers are given only to the nearest second. Perhaps I am not understanding something, but it seems to me based on the information on page 59 that you should have used the same "round-down" technique for Example 3-10 in the Server-Side Caching section, so that the code would be: lastModified = System.currentTimeMillis() / 1000 * 1000; [68] code block; the following code is missing from CacheHttpServlet public void resetBuffer() throws IllegalStateException { delegate.resetBuffer(); contentLength = -1; out.getBuffer().reset(); } without this code the class will not compile {76} Middle of page; text reads, "...or the servlet's SessionContext that we'll learn... There is no SessionContext class or interface in the Servlet API. Perhaps this was meant to be a reference to the ServletContext. [103] Section: Reading From an Abstract Resource: "How the URL path .... is determined by the web server." Should read: "How the URL path....is determined by the servlett container." {150} code block; out.println("Error accessing " + req.getRequestURI() + ""); This line does not return the output I would expect as a user, as a programmer I can see why this happens though. The output from this line, when I try an access http://localhost:8080/learn/readme.night is: Error accessing /learn/servlet/ErrorDisplay as a user would expect this to read: Error accessing /learn/readme.night I think this is because it is a separate http request and it simply looses the URI as it has been redirected to this servlet. Therefore the code is actually displaying accurate information its just its usefullness to a user is removed. Some might say that this is a security risk as it unvails your directory structure. I am aware this is an example in a book and not production code. I used this config to run the web-app: ed ErrorDisplay ed /ed.html 404 /ed.html {150} code block; This error has already been posted but I would like to point out that changing: req.getRequestURI() to req.getAttribute("javax.servlet.error.request_uri") (with a check for the attribute being null) would fix the problem. [185] 3rd Paragraph; www.geocities.com/SiliconValley/6742 no longer exists - have these images been moved? {209} 3rd full paragraph; The text: "For example, .foo.com is valid and matches www.foo.com and upload.foo.com but not www.upload.foo.com." should read: "For example, .foo.com is valid and matched www.foo.com and www.upload.foo.com, but not foo.com." According to Netscape's cookie specification and RFC 2109, the domain pattern .foo.com does match www.upload.foo.com. From the links off of Jason Hunter's servlets.com website: http://wp.netscape.com/newsref/std/cookie_spec.html : "A domain attribute of 'acme.com' would match host names 'anvil.acme.com' as well as 'shipping.crate.acme.com'." http://www.servlets.com/rfcs/rfc2109.html "Note that domain-match is not a commutative operation: a.b.c.com domain-matches .c.com, but not the reverse." {256} 5th and 6th paragraps; Finally, ... a servlet can retrieve the client's certificates (plural) as a request attribute: java.security.cert.X509Certificate[] certs = (java.security.cert.X509Certificate[]) reg.getAttribute("javax.servlet.request.X509Certificate"); For any server ... will return a security.cert.X509Certificate[] (array) object ... Example 8-13 is ok ! (202) last line of the Hidden form fields' 1st paragraph ; Replace : "You include hidden form files with HTML like this:" By : "You include hidden form fields with HTML like this:" (270) End of page + start of next page; There is an example class given, ContextProperties, which uses the ServletContext Object. Thoughout this entire example and following example code, the actual object us ed should be the ServletConfig Object ! [295] (example 9-11), middle of the page says (returns added for legibility): while (rs.next()) { name = rs.getString(1); if (rs.wasNull() || name.length() ==0) name = "Unknown user"; email = rs.getString(2); if (rs.wasNull() || email.length() ==0) name = "Unknown email"; comment = rs.getString(3); if (rs.wasNull() || comment.length() ==0) name = "No comment"; ... (more stuff) ... } I'm pretty sure that the wrong variables were set. It should be (arrows on lines that changed): while (rs.next()) { name = rs.getString(1); if (rs.wasNull() || name.length() ==0) name = "Unknown user"; email = rs.getString(2); if (rs.wasNull() || email.length() ==0) email = "Unknown email"; <----- HERE comment = rs.getString(3); if (rs.wasNull() || comment.length() ==0) comment = "No comment"; <----- HERE ... (more stuff) ... } (303) 1st paragraph, 4th line; "to communicate, we're going assume ...." should be: "to communicate, we're going to assume ...." [374] 3rd para (Dispatching by name); The method getNamedDispatcher(String name) should be called by a ServletContext, and not an HTTPServletRequest as shown in example. It looks to me that the example should therefore look like : ServletContext context = getServletContext(); RequestDispatcher dispatcher = context.getRequestDispatcher("searchView"); dispatcher.forward(request, response); [395] Examle 13.3 source code; In most of the examples in Chapter 13, starting with the one on page 395, Content- Type of the responses is set to text/plain (with res.setContentType("text/plain;charset=Something")). This messes up Netscape 4.x browsers, which respect this and do NOT render the HTML, it just types out the "source" HTML codes. The correct type is: res.setContentType("text/html;charset=Something") {641} description of addIntHeader; The description seems to be simply copied from addDateHeader