Java Network Programming, Second Edition By Elliotte Rusty Harold This errata page lists errors outstanding in the most recent printing. If you have technical questions or error reports, you can send them to booktech@oreilly.com. (Please specify the printing date of your copy.) This page was last modified on November 1, 2004. 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 Confirmed errors: (xxi) 4th paragraph; At the bottom of the page it says "You can download the source code from http://metalab.unc.edu/javafaq/books/jnp2e and http://www.reilly.com/catalog/9781565928701/." http://www.reilly.com/catalog/9781565928701/. SHOULD SAY http://www.oreilly.com/catalog/9781565928701/. (7) first paragraph; "content handers" should read "content handlers" (42) Table 2-2 (Last Selection on that Page; RFC 792); Page 42 in "Table 2-2. Selected Internet RFCs" on the last RFC of that page: RFC 792 Internet Control Message Protocol (ICMF) It should be "ICMP" not ICMF :-) p. 60: In the first line of the first text paragraph, "HTT 1.0" should be "HTTP 1.0" p. 88: In the first sentence of the second paragraph, "servers as a buffer" should be "serves as a buffer". {99} last code; The CipherOutputStream() signature is wrong , it reads : public CipherOutputStream(InputStream in, Cipher c); should be public CipherOutputStream(OutputStream out,Cipher c); [134] Code segment: one definate error is that the method declaration is out of order for "public void synchronized sendDigest (byte[] digest) {" it should read: "public synchronized void sendDigest (byte[] digest) {" AUTHOR: This is correct. p. 161: If there are directories, already gzipped files, or other incompressible files, the thread is likely not to exit when it should. To fix this, move the call to incrementFilesCompressed() from the fifth line on p. 162 to right after the line that removes a file from the pool on p. 161. Then the run() method should read as follows: public void run() { while (filesCompressed != GZipAllFiles.getNumberOfFilesToBeCompressed()) { File input = null; synchronized (pool) { while (pool.isEmpty()) { if (filesCompressed == GZipAllFiles.getNumberOfFilesToBeCompressed()) { System.out.println("Thread ending"); return; } try { pool.wait(); } catch (InterruptedException ex) { } } input = (File) pool.remove(pool.size()-1); incrementFilesCompressed(); } // don't compress an already compressed file if (!input.getName().endsWith(".gz")) { try { InputStream in = new FileInputStream(input); in = new BufferedInputStream(in); File output = new File(input.getParent(), input.getName() + ".gz"); if (!output.exists()) { // Don't overwrite an existing file OutputStream out = new FileOutputStream(output); out = new GZIPOutputStream(out); out = new BufferedOutputStream(out); int b; while ((b = in.read()) != -1) out.write(b); out.flush(); out.close(); in.close(); } } catch (IOException e) { System.err.println(e); } } // end if } // end while } // end run p. 163: The example is not always adding files at the begining of the queue like it should. In the synchronized block change pool.add(files[j]); to pool.add(0, files[j]); [163] Towards the bottom of the main method.; This code may enter an infinite wait state. When the main method finishes adding all the files to the pool it calls for (int i = 0; i < threads.length; i++) threads[i].interrupt(); } If a thread picks up the last file and is still processing it when this loop is called, all the threads will wake up, notice that the pool is empty, but the condition (filesCompressed == GZipAllFiles.getNumberOfFilesToBeCompressed()) will still be false until the running thread finishes the last file. This will place all other threads in a wait state never to be woken up. This happened to me which is how I was alerted to the problem. A way to fix this is to ammend the loop to read: while(threads[0].getFilesCompressed() != filesToBeCompressed); for (int i = 0; i < threads.length; i++ ) { threads[i].interrupt(); } Of course this will necessitate adding a public int getFilesCompressed() method to the GZipThread class. AUTHOR: This is correct. p. 170: In the third text paragraph, "explicitly via getAddress" should be "explicitly via getHostName". p. 189: In the processLogFile() method in Example 6-11, PooledWeblog, the body of the try block should read as follows: String entry = in.readLine(); while (entry != null) { if (entries.size() > numberOfThreads) { try { Thread.sleep((long) (1000.0/numberOfThreads)); } catch (InterruptedException e) {} continue; } synchronized (entries) { entries.add(0, entry); entries.notifyAll(); } entry = in.readLine(); Thread.yield(); } // end while (252) First paragraph; Text refers to ParserGetter class from example 8-5 This class is in fact from example 8-6 p. 265: Two changes should be made to Example 8-10 on this page. First you need to test if the local directory exists before trying to make it. That is, change if (localDirectory.mkdirs()) { to if (localDirectory.exists() || localDirectory.mkdirs()) { Next, to make PageSaver work with documents that use a meta tag such as , you need to change parser.parse(r, callback, false); to parser.parse(r, callback, true); [275] 1st paragraph: Example 9-5 is a complete applet that downloads a sound file called gong.au, which is located in the same directory as the applet, ... should be: Example 9-5 is a complete applet that downloads a sound file called beep.au, which is located in the sounds directory just below the document base, ... {312} first code fragment; Uses deprecated Socket constructor AUTHOR: The fix is to change Socket theSocket = new Socket("java.sun.com", 80, true); to Socket theSocket = new Socket("java.sun.com", 80, true); That is, delete the third argument. p. 364: In the first line, jServerSocket should be ServerSocket (delete the initial j). {372}: In the main() method, on the last line of the page, "args.length >= 2" should be "args.length > 2". (374) Last sentence of the 1st paragraph: "However, that would raise some additional issues of thread safety that Example 11-5 doesn't have to address because it's immutable." should be: "However, that would raise some additional issues of thread safety that Example 11-6 doesn't have to address because it's p. 384: In order to better handle unexpected network failures (broken sockets), near the bottom of the page change if (c == '\r' || c == '\n') break; to if (c == '\r' || c == '\n' || c == -1) break; (391) IN PRINT: Note, last sentence; "...Scott Oak's 'Java Security'..." SHOULD BE: "...Scott Oaks' 'Java Security'..." p. 392: In the third paragraph, jre/lib/ext/security/java.security should be jre/lib/security/java.security. That is, delete ext/ from the path. p. 392: In the second to last paragraph, java.policy should be java.security. {414} Figure 13-1; Eighth byte: "destination port" Should read: "checksum" {427-429}: Example 13-3 and 13-4 share a mutual bug. Both assume they're using the local host's default encoding. However, is these two programs run of different hosts (for instance, one is a Japanese system and one is a French system) that may well not be the same for each. The encoding needs to be explicitly specified in both programs. On p. 427 in Example 13-3 change byte[] data = theLine.getBytes() to byte[] data = theLine.getBytes("UTF-8") On p. 429 in Example 13-4 change String s = new String(packet.getData(), 0, packet.getLength()) to String s = new String(packet.getData(), 0, packet.getLength(), "UTF-8") This will allow the two hosts to exchange data regardless of differing encodings. p. 433: In the fith code line, change new ServerSocket(2048) to new DatagramSocket(2048). It should read: DatagramSocket ds = new DatagramSocket(2048) p. 448: In the second paragraph, EchoInputThread should be SenderThread. [465] 3rd code snippet; in the 3rd code snippet it says: ia = new InetAddress("metalab.unc.edu"); shouldn't it say ia = InetAddress.getByName("metalab.unc.edu"); Their is no public constructor for InetAddress(jdk 1.3) (476) 1st paragraph, 1st line: Example 15-5 is not right exmaple in this paragraph. instead of Example 15-5, Example 7-5 is right example that should be written. p. 485: In the second line of the first code fragment on the page, last-modification should be last-modified. That is, change long lastModified = uc.getHeaderFieldDate("last-modification", 0); to long lastModified = uc.getHeaderFieldDate("last-modified", 0); (502), in item 1, change "you'll use send data to the CGI program" to "you'll send to the CGI program". [506] 1st paragraph; java.io.Permission should be: java.security.Permission (509) In the first row of the table on this page; "! XPM2" should be "!XPM2", and should be placed in the ASCII column, not the hexadecimal column. {516}: At the end of the first paragraph of the "Handling Server responses" section, delete ", and follows the response MIME header". {520} In Table 15-3, in the 406 row; "Allow field of the MIME request header" should be "Accept field of the MIME request header" (534): In the last heading on the page, change "Protected" to "protected" (536): In the last heading on the page, change "Protected" to "protected" (536): In the last paragraph, change "has been stored in the URL's host field" to "has been stored in the URL's file field" (537-540): In all the headings on these pages, change "Protected" to "protected" (564): In point 5 change "URLConnectiongetContent() method" to "URLConnection.getContent() method". Also, note that the word "method" should not be monospaced here {603} 3rd paragraph; The source code for the FibonacciImpl.java (Chapter 18 , Example 18.2): This class extends UnicastRemoteObject and also calls the exportObject(this) on this class, which results in doing the same thing twice.(Which results in an exception stating object already exported..) This is a mistake only in the source code suppiled on the web site, in the text book this class doesn't extend UnicastRemoteObject. Here is the code snippet from web site: public class FibonacciImpl extends UnicastRemoteObject implements Fibonacci { public FibonacciImpl() throws RemoteException { UnicastRemoteObject.exportObject(this); // setLog(System.out); } Fix: Only one of the two things shd be done:- 1) Extend the UnicastRemoteObject class 2) use exportObject() method {606} 12th line of the example 18-4; The error message of the first System.err.println should be "Usage: java FibonacciClient ..." instead of "Usage: java Fibonacci client ...", because class' name is FibonacciClient. {614} 12th line of the example 18-8; The error message of the first System.err.println should be "Usage: java FibonacciClient ..." instead of "Usage: java Fibonacci client ...", because the class' name is FibonacciClient. (622): In the 3rd paragraph, "perfomed" should be "performed" p. 636: In the third paragraph, delete "like the one shown in Figure 19-1". (648): In the last paragraph, "a simple HELLO command" should be "a simple HELO command" (654): In the note, "java.netPasswordAuthentication" should be "java.net.PasswordAuthentication" and "javax.mailPasswordAuthentication" should be "javax.mail.PasswordAuthentication" p. 693: In Example 19-12, AllPartsClient, I have a couple of small improvements. Change the two lines: if (fileName == null && (disposition.equals(Part.ATTACHMENT) || !contentType.equals("text/plain"))) { to if (fileName == null && (Part.ATTACHMENT.equalsIgnoreCase(disposition) || !contentType.equalsIgnoreCase("text/plain"))) { This avoids a possible NullPointerException and handles case insensitive MIME type and disposition matching. p. 727: In the second column the reference to "SO TIMEOUT socket optionn" should be "SO_TIMEOUT socket option"; that is, use an underscore instead of a space..