Java Network Programming, 3rd 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 updated May 21, 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 Confirmed errors: {108-109} Example 5-1 should be titled DigestThread, not FileDigestThread. [108] Example 4-1; SafeBufferedReader, can lose data if the last line does not end with a line break character. Here's the corrected version: package com.macfaq.io; import java.io.*; public class SafeBufferedReader extends BufferedReader { public SafeBufferedReader(Reader in) { this(in, 1024); } public SafeBufferedReader(Reader in, int bufferSize) { super(in, bufferSize); } private boolean lookingForLineFeed = false; public String readLine() throws IOException { StringBuffer sb = new StringBuffer(""); while (true) { int c = this.read(); if (c == -1) { // end of stream if (sb.length() == 0) return null; return sb.toString(); } else if (c == '\n') { if (lookingForLineFeed) { lookingForLineFeed = false; continue; } else { return sb.toString(); } } else if (c == '\r') { lookingForLineFeed = true; return sb.toString(); } else { lookingForLineFeed = false; sb.append((char) c); } } } } {118} The paragraph at the top of the page should read: one additional field, an InstanceCallbackDigestUserInterface object called callback. At the end of the run() method, the digest is passed to callback's receiveDigest() method. The InstanceCallbackDigestUserInterface object itself is set in the constructor. {118} At the beginning of the last paragraph change: The CallbackDigestUserInterface class shown in Example 5-8 to The InstanceCallbackDigestUserInterface class shown in Example 5-8 (162): The caption for Example 6-7, should be "Determining whether an IP address is v4 or v6 (164): In the fifth paragraph change "Organization multicast addresses begin with FF05 or FF15" to "Site-wide multicast addresses begin with FF05 or FF15". (165): In the caption for Example 6-8, change "(Java 1.4 only)" to "(Java 1.4 and later)" {171} In the first code fragment on the page, change NetworkInterface.getByName(local) to NetworkInterface.getByInetAddress(local) {180}: There are three errors in Example 6-13. First, change new BufferedReader to new SafeBufferedReader. Then in the ,code>main() method, change e.printStackTrace() to ex.printStackTrace(). Finally, several lines from the processLogFile() method were omitted. The complete, corrected program is: import java.io.*; import java.util.*; import com.macfaq.io.SafeBufferedReader; public class PooledWeblog { private BufferedReader in; private BufferedWriter out; private int numberOfThreads; private List entries = Collections.synchronizedList(new LinkedList()); private boolean finished = false; private int test = 0; public PooledWeblog(InputStream in, OutputStream out, int numberOfThreads) { this.in = new SafeBufferedReader(new InputStreamReader(in)); this.out = new BufferedWriter(new OutputStreamWriter(out)); this.numberOfThreads = numberOfThreads; } public boolean isFinished() { return this.finished; } public int getNumberOfThreads() { return numberOfThreads; } public void processLogFile() { for (int i = 0; i < numberOfThreads; i++) { Thread t = new LookupThread(entries, this); t.start(); } try { 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 } catch (IOException e) { System.out.println("Exception: " + e); } this.finished = true; // finish any threads that are still waiting synchronized (entries) { entries.notifyAll(); } } public void log(String entry) throws IOException { out.write(entry + System.getProperty("line.separator", "\r\n")); out.flush(); } public static void main(String[] args) { try { PooledWeblog tw = new PooledWeblog(new FileInputStream(args[0]), System.out, 100); tw.processLogFile(); } catch (FileNotFoundException e) { System.err.println("Usage: java PooledWeblog logfile_name"); } catch (ArrayIndexOutOfBoundsException e) { System.err.println("Usage: java PooledWeblog logfile_name"); } catch (Exception ex) { System.err.println(ex); ex.printStackTrace(); } } // end main } (190): Remove HTTPS from the list of unsupported protocols. That is, change "This browser supports HTTP, HTTPS, FTP, mailto, file, gopher, doc, netdoc, verbatim, systemresource, and jar but not HTTPS, ldap, Telnet, jdbc, rmi, jndi, finger or daytime." to "This browser supports HTTP, HTTPS, FTP, mailto, file, gopher, doc, netdoc, verbatim, systemresource, and jar but not ldap, Telnet, jdbc, rmi, jndi, finger or daytime." (218): In Example 7-10, the line System.out.println("The host is " + u.getUserInfo()); should be System.out.println("The host is " + u.getHost()); As a result, on p. 219 in the output, "The host is null" should be "The host is www.xml.com" {226}: Change "Finally, the name-value pairs are simply the NAME attributes of the INPUT elements, except for any INPUT elements whose TYPE attributes has the value submit." to "Finally, the names in the name-value pairs are simply the values of the NAME attributes of the INPUT elements. The values of the pairs are whatever the user types into the form." (257) 8-9, This is a really trivial one, that isn't even really an erratum, but I do want to fix it in the next printing for appearance's sake. In the fourth line of code, change for (int i =0; to for (int i = 0; That is add a space after the equals sign. (268) In the second paragraph change "can set include" to "can include". {271} In Example 8-11, the matches() method should also check the domain. That is, it should read: public boolean matches(URI u) { if (isExpired()) return false; String path = u.getPath(); if (path == null) path = "/"; String domain = u.getHost(); if (path.startsWith(this.path) && domain.equals(thisdomain)) { return true; } return false; } (296) first paragraph; The first sentence reads "Java 1.4 adds an isClosed() method that returns true is the socket has been closed," should read "...returns true if the socket has been closed," ^^ {299} Change if (s.getTcpSoLinger() == -1) s.setSoLinger(true, 240); to if (s.getSoLinger() == -1) s.setSoLinger(true, 240); (301): line 2; "However, again thegggg client ..." Should be: "However, again the client ..." (334) third line of code from bottom; println() error message should be "Port must be between 0 and 65535". ^^ {349}: In the main() method, on the last line of the page, "if (args.length >= 2)" should be "if (args.length > 2)". {358} Sample Code - Top of page; The block of the second 'catch' uses the variable 'e' instead of 'ex'. So the code should read: catch (IOException ex) { System.out.println("Server could not start because of an " + ex.getClass()); System.out.println(ex); } {367} 2nd & 3rd paragaraphs "java.policy" should be "java.security" in both paragraphs {426} figure 13-1 of a UDP datagram has an error. There are two fields in the diagram both shown as "destination port (0-65535)". There should be only one such field, the 2nd of the duplicates should instead be indicated as being a checksum. (426) In Figure 13-1 there's an extraneous letter I at the bottom lefthand corner of the picture. That shouldn't be there. {439-441}: 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. 439 in Example 13-3 change byte[] data = theLine.getBytes() to byte[] data = theLine.getBytes("UTF-8") On p. 441 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. {491} In example 14-2, the ,code>for loop should run from 1 to 10, not 1 to 9. That is, for (int i = 1; i < 10; i++) { should be: for (int i = 1; i <= 10; i++) { [519] ex 15-8 source code; QueryString constructor needs parameter The first lines of that example should be import java.net.*; import java.io.*; import com.macfaq.net.*; public class FormPoster { private URL url; // from Chapter 7, Example 7-9 private QueryString query = null; public FormPoster (URL url) { if (!url.getProtocol().toLowerCase().startsWith("http")) { throw new IllegalArgumentException( "Posting only works for http URLs"); } this.url = url; } public void add(String name, String value) { if (query == null) query = new QueryString(name, value); query.add(name, value); } The rest of the example doesn't change. (544) in Example 15-11 package java.net should be package java.net; (544) in the final paragraph; change getOutputStream() to getBody(). {545} in Example 15-13 public abstract class CacheRequest should be public abstract class CacheResponse (549): In the second paragraph change Example 15-13 to Example 15-15. (594) first paragraph; In the first text paragraph "java.protocolhandler.pkgs" should be "java.protocol.handler.pkgs"