| Books & Tools | Techniques | |
|
Comprehensive coverage of Ruby 1.8 and 1.9
"The New Most Important Ruby Book" JudeJude is my Java documentation browser. It combines Sun's definitive javadocs with the easy-to-use format of Java in a Nutshell, and tops it off with easy keyboard-based navigation and full-text searching. Jude is available for free evaluation. See the user's guide for more info Java in a NutshellThe 5th edition is now out, with complete coverage of Java 5.0! It includes a fast-paced tutorial on the language, and a compact quick-reference for the core Java API. Java Examples in a NutshellThe 3rd edition, updated for Java 1.4 This edition has all-new coverage of the NIO and JavaSound APIs, completely rewritten Servlets and XML chapters, and coverage of new Java 1.4 features (assertions, logging, preferences, SSL, etc.) added througout. A great book for those who like to learn by example. 193 working examples: 21,900 lines of carefully commented code to learn from. Java 1.5 Tiger: A Developer's NotebookAmazon incorrectly credits me as the main author on this book. I'm actually the second author: really more of a consultant. This is a good book about all the language changes in the latest version of Java. Effective JavaI didn't write this excellent book, but I wish I had. Author Josh Bloch is probably best known for the collections classes in the java.util package. His experience and wisdom are apparent in this book. I learned from it and recommend it highly. |
January 29, 20040*Infinity==NaN; 1/-Infinity==-0The Java floating-point types have special values for negative zero, positive and negative infinity, and not-a-number, or NaN. I hacked up a short program (below) to print out a multiplication table for these special values, and also tables for the +, -, /, and % operators as well. For the most part, these special values behave as you'd expect when you do arithmetic with them. But some of the values produce results that may seem suprising. 1/0 produces infinity, of course, but 0/0 produces NaN which may not be obvious at first. (If you divide by zero using an integer type instead of a floating point type, you get an ArithmeticException. Floating-point arithmetic never throws exceptions in Java.) The code is below. Cut, paste, compile and run..
public class Arithmetic {
static final double negzero = 1/Double.NEGATIVE_INFINITY;
static double[] operands = new double[] {
0.0, negzero, 1.0,
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN
};
// Print a double value as a string. Java would do this conversion
// for us, but we want "Inf" instead of "Infinity", which is too
// wide for tab-separated tables.
static void print(double x) {
if (Double.isNaN(x)) System.out.print("NaN\t");
else if (Double.isInfinite(x)) {
if (x < 0) System.out.print("-");
System.out.print("Inf\t"); // "Infinity" is too long
}
else if (x == 0) {
if (1/x == Double.NEGATIVE_INFINITY) System.out.print("-");
System.out.print("0.0\t");
}
else System.out.print(x + "\t");
}
// Print the header row for each table
static void printHeader(String operator) {
System.out.println();
System.out.print(operator + "\t");
for(int col = 0; col < operands.length; col++) print(operands[col]);
System.out.println();
}
// print out all the tables.
public static void main(String[] args) {
printHeader("+");
for(int row = 0; row < operands.length; row++) {
print(operands[row]);
for(int col = 0; col < operands.length; col++)
print(operands[row] + operands[col]);
System.out.println();
}
printHeader("-");
for(int row = 0; row < operands.length; row++) {
print(operands[row]);
for(int col = 0; col < operands.length; col++)
print(operands[row] - operands[col]);
System.out.println();
}
printHeader("*");
for(int row = 0; row < operands.length; row++) {
print(operands[row]);
for(int col = 0; col < operands.length; col++)
print(operands[row] * operands[col]);
System.out.println();
}
printHeader("/");
for(int row = 0; row < operands.length; row++) {
print(operands[row]);
for(int col = 0; col < operands.length; col++)
print(operands[row] / operands[col]);
System.out.println();
}
printHeader("%");
for(int row = 0; row < operands.length; row++) {
print(operands[row]);
for(int col = 0; col < operands.length; col++)
print(operands[row] % operands[col]);
System.out.println();
}
}
}
January 27, 2004MyDoom mailbox cleanup as a learning experienceWhen I chose my PopClean.java mailbox cleaner utility as the subject of the first entry of my new blog, I didn't think I'd be using it again just a few days later.... A good spam filter will delete these messages for you, but if you do your filtering on the client side, then you still have to download all the spam. My utility is based on the fact that virus-spawned spam has predictable subject lines and sizes. It deletes the messages directly from the POP mailbox without ever downloading them. If you're going to have to delete a bunch of virus-generated messages from your inbox, you might as well make a learning experience out of it. Here's what I did to clean my mailbox... Before you go any further, though, know that this is not production code, and you use it at your own risk. It works for me, and it is an educational exploration of Java networking and the POP3 protocol. But it permanently deletes data and if you misuse it (or if it has bugs that I don't know about) you could lose email messages. Instead of just compiling and running the code, I suggest you read it through, learn from it, and then decide if you want to use it. With that said, here's how I used it. First, I poked noticed that the MyDoom messages I had already deleted had subject lines like "hi", "hello" and "test" (and also in initial capitals and all caps). I telnetted to my POP server to inspect the size of some of these messages and found (from a small sample) that they appeared to be between about 32,000 and 32,500 bytes long. With that data in hand, I invoked PopClean like this (a single command line wrapped to 2 lines):
java PopClean -host pop.example.com -user dflanagan -pass notreally
-size 32000 -subject "(?i)(hi|hello|test)" -list
The -size argument tells PopClean to only consider messages longer than 32,000 bytes. (I don't have an option for a maximum size, only a minimum size.) The -subject argument is a java.util.regex.Pattern regular expression. The "(?i)" makes the match case-insensitive. The -list argument in the command-line above tells PopClean to just list matching (size and subject line) messages. Once I was satisfied that PopClean was matching the messages I wanted and not others, I took the -list argument away. PopClean printed: Connecting to pop.example.com on port 110 with username dflanagan. Will delete all messages longer than 32000 bytes that have a subject matching: [(?i)(hi|hello|test)] Do you want to proceed (y/n) [n]: I happily responded with 'y', and saw 368 messages that looked like this: Deleted message 3276: test Deleted message 3279: hi Deleted message 3280: TEST Deleted message 3281: Hello Deleted message 3283: hi Deleted message 3289: test Deleted message 3290: hi Deleted message 3296: Test Deleted message 3298: test Deleted message 3299: Hello Bliss! Failure to override22 months ago, I wrote this article for ONJava.com (part of the O'Reilly Network. I'd pretty much forgotten about the article, until I got an e-mail today from a New Zealand reader known simply as "Hemi". He was the first of (presumably) several thousand readers to point out an error in this code from the article:
public class LRUCache extends java.util.LinkedHashMap {
public LRUCache(int maxsize) {
super(maxsize*4/3 + 1, 0.75f, true);
this.maxsize = maxsize;
}
protected int maxsize;
protected boolean removeEldestEntry() {
return size() > maxsize;
}
}
This code is supposed to implement a least-recently-used (LRU) cache on top of the java.util.LinkedHashMap class. Can you spot the error? Hint: the class functions as a cache, but fails the LRU part. Read on to see what I did wrong and learn how you can avoid this category of errors yourself. My class attempts, but fails, to override the removeEldestEntry() method of LinkedHashMap. The problem is that I got the signature wrong. The method I wanted to override takes a Map.Entry object as an argument. My method takes no arguments, and therefore does not actually override anything. The point of overriding this method is to prune old entries from the cache. (See the javadoc for LinkedHashMap for details.) My code looks like it overrides the method, but it doesn't. The upshot is that the cache never purges the least-recently used elements, and just grows and grows. I should have tested my code. Now, getting on to the main point of this post: Java 1.5 has a solution to this general problem of failing to override the method you intend to override. The new Java metadata/annotation language feature defined by JSR-175 specifes an @Overrides annotation that solves exactly this problem. Update: In the proposed final draft for JSR-175, this annotation has been renamed @Override. If I had been writing the article about Java 1.5, and had annotated my method with @Overrides, then the compiler would have issued an error when it noticed that my method did not, in fact, override anything. Here's the code as I should have written it, updated for Java 1.5, and still untested:
public class LRUCache extends java.util.LinkedHashMap {
public LRUCache(int maxsize) {
super(maxsize*4/3 + 1, 0.75f, true);
this.maxsize = maxsize;
}
protected int maxsize;
@Overrides protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > maxsize;
}
}
January 25, 2004Fun with Java SoundOne of the new chapters I added to the 3rd edition of Java Examples in a Nutshell covers the Java Sound API. That chapter was a lot of fun to research and write. To the right is an applet I played around with before writing the example that actually made it into the chapter: its a MIDI drum machine. Click your mouse in the applet to give it keyboard focus and then strike keys at random to generate percussion effects. Move the mouse to the bottom of the applet for louder sounds and to the top of the applet for softer sounds. Play around with the keys: only keys whose keycode maps to a MIDI key number between 35 and 81 produce any sound. You'll need the Java 1.4 plugin installed in your browser for this to work, of course. Don't forget to click on the applet. If you don't it may never get the keyboard events it needs. Please excuse the lame visuals, and take a look at the surprisingly simple source code. January 24, 2004Big Changes in Java 1.5 AlphaJava 1.5 introduces major new changes to the language. The alpha release doesn't include documentation, but the JSRs that define the language features are in public review at the JCP, which means that you can learn about (and coment on, if you choose) these new features. First up is JSR 14 which adds parameterized types to the language. This is huge. You can use types like: Map<String,Integer> map = new HashMap<String,Integer> The public review draft for this is really old, and slighly out of sync with reality. There ought to be a new version soon. Next is JSR 201, a catch-all for five smaller, but no less exciting, language changes. They are:
I served on the expert group for this JSR. Public review has just begun, so read the public review draft, and let us know if you find problems. Finally, JSR 175 adds metadata or annotations to the language. This is a cool, but fairly obscure feature. I believe it is of primary interest to J2EE types who want to be able to add things like deployment information directly to classfiles. I also get the impression that this feature is being added to Java now because C# has it. The public review comment period for this JSR has closed, but you can still read the public review draft to learn about it. January 22, 20040xCoffeeCabalThe other day I tried to come up with a Java hexadecimal literal that was cooler (and less tainted by sexism) than the ubiquitous 0xCAFEBABE. 0xCAFED00D is misspelled, and 0xDEADBEEF is not suitable for vegetarians. So like any good Unix user, I came up with this: grep -i '^[abcdef][abcdefo]*l\?$' /usr/share/dict/words (The trailing letter l in the regular expression is legal: it is used in Java literals to signify a long instead of an int.) My favorites? I thought it was pretty cool that pair BADE and ACCEDED were both legal hexadecimal literals. Programmers who like programming with patterns will be happy to know that FACADE is legal hexadecimal. My favorite, though is C0FFEECABAL. But if you don't like coffee, you might prefer C0C0A. Can you find a better one? Anyone want to submit examples (and translations) using a non-english dictionary? January 21, 2004PopClean.javaPopClean is a simple Java utility program I put together to deal with the flood of viral SoBig.F-generated email messages clogging my POP3 inbox. I have included it as an example in the (newly released) third edition of Java Examples in a Nutshell. PopClean scans a POP3 inbox and can list or delete messages that are above a size you specify. You can also constrain the list or deletion to messages whose Subject: lines match a given regular expression. It does this without having to download the entire message. Since SoBig messages are ~100Kb each, this is a big win in terms of bandwidth. I've placed this program in the public domain. It comes with NO WARRANTY, and you have to use it at your own risk. I've used it on two different POP3 servers, but have not tried to test it rigorously. It requires Java 1.4 (for regular expression matching). I'm sure there are other utilities like this one out there. I wrote this while I was in the middle of updating Java Examples in a Nutshell, however, and rolling my own version just seemed like the right thing to do. In addition to its utility, this program also serves as an example of how easy it is to write networked programs in Java. The code follows...
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;
/**
* A simple utility program for deleting messages from a POP3 mailbox based on
* message size and Subject line. I wrote this to delete the hundreds of
* huge SoBig.F mails that I was receiving without having to download them.
* It has been used on two different POP3 servers, but has not been
* rigorously tested for compliance with the POP3 protocol.
*
* Don't run this program unless you understand what it is doing. It deletes
* e-mail without downloading it: YOU MAY PERMANENTLY LOSE DATA!
*
* Typical usage:
* 1) Look at the subject lines for the big messages you've got
*
* java PopClean -host host -user user -pass pass -size 100000 -list
*
* 2) Create a regular expression to match viral subject lines, and use it
* to delete large matching messages
* java PopClean -host h -user u -pass p -size 100000 \
* -subject 'Thank you!|Re: Your application'
* This will ask for confirmation before proceeding.
*
* 3) If you're confident that all big messages are virus-infected, then
* you can skip the -subject argument and delete on size alone
* java PopClean -host h -user u -pass p -size 100000
* This will ask for confirmation before proceeding.
*
* This program was written by David Flanagan.
* It is placed in the public domain.
* This program is provided AS-IS, with NO WARRANTY, either express or implied.
* Use at your own risk.
*/
public class PopClean {
static BufferedReader in = null;
static PrintWriter out = null;
static Socket s = null;
static boolean debug = false;
public static void main(String args[]) {
try {
String hostname = null, username = null, password = null;
int port = 110;
int sizelimit = -1;
String subjectPattern = null;
Pattern pattern = null;
Matcher matcher = null;
boolean listonly = false;
boolean confirm = true;
// Handle command-line arguments
for(int i = 0; i < args.length; i++) {
if (args[i].equals("-user"))
username = args[++i];
else if (args[i].equals("-pass"))
password = args[++i];
else if (args[i].equals("-host"))
hostname = args[++i];
else if (args[i].equals("-port"))
port = Integer.parseInt(args[++i]);
else if (args[i].equals("-size"))
sizelimit = Integer.parseInt(args[++i]);
else if (args[i].equals("-subject"))
subjectPattern = args[++i];
else if (args[i].equals("-debug"))
debug = true;
else if (args[i].equals("-list"))
listonly = true;
else if (args[i].equals("-force")) // don't confirm
confirm = false;
}
// Verify them
if (hostname == null || username == null || password == null ||
sizelimit == -1)
usage();
// make user the pattern is a valid regexp
if (subjectPattern != null) {
pattern = Pattern.compile(subjectPattern);
matcher = pattern.matcher("");
}
// Say what we are going to do
System.out.println("Connecting to " + hostname + " on port " +
port + " with username " + username + ".");
if (listonly) {
System.out.println("Will list subject lines for messages " +
"longer than " + sizelimit + " bytes");
if (subjectPattern != null)
System.out.println("that have a subject matching: [" +
subjectPattern + "]");
}
else {
System.out.println("Will delete all messages longer than "+
sizelimit + " bytes");
if (subjectPattern != null)
System.out.println("that have a subject matching: [" +
subjectPattern + "]");
}
// If asked to delete, ask for confirmation unless -force is given
if (!listonly && confirm) {
System.out.println();
System.out.print("Do you want to proceed (y/n) [n]: ");
System.out.flush();
BufferedReader console =
new BufferedReader(new InputStreamReader(System.in));
String response = console.readLine();
if (!response.equals("y")) {
System.out.println("No messages deleted.");
System.exit(0);
}
}
// Connect to the server
s = new Socket(hostname, port);
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
out = new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
// Read the welcome message from the server until we get +OK
System.out.println("Connected: " + checkResponse());
// Now log in
send("USER " + username);
send("PASS " + password);
System.out.println("Logged in");
// Check how many messages
String stat = send("STAT");
StringTokenizer t = new StringTokenizer(stat);
System.out.println(t.nextToken() + " messages in mailbox.");
System.out.println("Total size: " + t.nextToken());
// Get a list of message numbers and sizes
send("LIST");
List msgs = new ArrayList();
String line;
for(;;) {
line = in.readLine();
if (line == null) throw new IOException("Unexpected EOF");
if (line.equals(".")) break;
msgs.add(line);
}
// Now loop through the messages one at a time
int nummsgs = msgs.size();
for(int i = 0; i < nummsgs; i++) {
String m = (String) msgs.get(i);
StringTokenizer st = new StringTokenizer(m);
int msgnum = Integer.parseInt(st.nextToken());
int msgsize = Integer.parseInt(st.nextToken());
// If the message is too small, ignore it.
if (msgsize <= sizelimit) continue;
// If we're listing messages, or matching subject lines
// find the subject line for this message
String subject = null;
if (listonly || pattern != null) {
subject = getSubject(msgnum); // get the subject line
// If we couldn't find a subject, skip the message
if (subject == null) continue;
// If this subject does not match the pattern, then
// skip the message
if (pattern != null) {
matcher.reset(subject);
if (!matcher.matches()) continue;
}
// If we are listing, list this message
if (listonly) {
System.out.println("Subject " + msgnum + ": " +
subject);
continue; // so we never delete it
}
}
// If we are not listing, then delete the message
if (!listonly) {
send("DELE " + msgnum);
if (pattern == null)
System.out.println("Deleted message " + msgnum);
else
System.out.println("Deleted message " + msgnum +
": " + subject);
}
}
// When we're done, log out and shutdown the connection
shutdown();
}
catch(Exception e) {
// If anything goes wrong print exception and show usage
System.err.println(e);
usage();
// Always try to shutdown nicely so the server doesn't hang on us
shutdown();
}
}
public static void usage() {
System.err.println("java PopClean <options>");
System.err.println(
"Options are:\n" +
"-host <hostname> # Required\n" +
"-port <port> # Optional; default is 110\n" +
"-user <username> # Required\n" +
"-pass <password> # Required and sent as cleartext; APOP not supported\n" +
"-size <limit> # Message size in bytes. Shorter messages are ignored.\n" +
"-subject <regexp> # Optional java.util.regex.Pattern regular expression\n" +
" # only messages with a matching Subject line are deleted\n"+
"-list # List matching subjects instead of deleting messages\n" +
"-force # Don't ask for confirmation before proceeding\n" +
"-debug # Display POP3 protocol requests and responses\n");
System.exit(1);
}
public static String send(String cmd) throws IOException {
if (debug) System.out.println(">>>" + cmd);
out.print(cmd);
out.print("\r\n");
out.flush();
String response = checkResponse();
if (debug) System.out.println("<<<+OK " + response);
return response;
}
public static String checkResponse() throws IOException {
String response;
for(;;) {
response = in.readLine();
if (response == null)
throw new IOException("Server unexpectedly closed connection");
else if (response.startsWith("-ERR"))
throw new IOException("Error from server: " + response);
else if (response.startsWith("+OK"))
return response.substring(3);
}
}
public static String getSubject(int msgnum) throws IOException {
send("TOP " + msgnum + " 0");
String subject = null, line;
for(;;) {
line = in.readLine();
if (line == null) throw new IOException("Unexpected EOF");
if (line.startsWith("Subject: ")) subject = line.substring(9);
if (line.equals(".")) break;
}
return subject;
}
// Disconnect nicely from the POP server.
// This method is called for normal termination and exceptions.
public static void shutdown() {
try {
if (out != null) {
send("QUIT");
out.close();
}
if (in != null) in.close();
if (s != null) s.close();
}
catch(IOException e) {}
}
}
|
Advertising
About
Store
Search
Archives
April 2008
March 2008 February 2008 January 2008 November 2007 October 2007 September 2007 August 2007 July 2007 June 2007 May 2007 April 2007 March 2007 February 2007 January 2007 December 2006 November 2006 October 2006 September 2006 August 2006 July 2006 June 2006 May 2006 April 2006 March 2006 January 2006 December 2005 November 2005 October 2005 September 2005 August 2005 July 2005 June 2005 April 2005 March 2005 February 2005 December 2004 October 2004 September 2004 July 2004 June 2004 May 2004 April 2004 March 2004 February 2004 January 2004 Syndicate
|