Sunday, May 11, 2008

[OT] Out of the Mouths of Babes...

I promise I'm not going to turn into one of those bloggers who turns his blog into a blog about blogging every precious blogging word that comes out of his three-year-old son's mouth. But I'm submitting this to the Interbits in the hopes that some day when I'm old and grey and he's graduating from high school I'll be to retrieve it and *really* get some peoples' eyes rolling.

So we're celebrating Mother's Day and my wife gets to the card that "he" gave her. It's got a Winnie the Pooh theme, with a typical "you're the best mommy ever" verse. Inside is a picture of Kanga (also a mommy) giving Roo (also a little boy) an affectionate hug. So Angela points to the picture and says, "Look, Alex, it's me and you!". And he looks at her with his eyes wide, then he looks at the picture again, and says in a tone that implies that he's a little concerned about how she's not keeping current with her medication, "Yeeeaaahhhh........and we've turned into kangaroos!"

Friday, May 09, 2008

The Java Process That Ate My MacBook Pro

Gotta love the Phenomenal Cosmic Power of virtual memory (the next-to-last column in the following ActivityMonitor screen capture):

Thursday, May 08, 2008

Debugging a Remote Java VM with Eclipse

I (heart) the internet. Why? Well, among other reasons, I ran across this blog post today, which totally saved me a good several hours of frustration and swearing whilst tracking down a thorny problem.

You see, at my day job, I help to develop this webapp that points to a number of Java VMs, running in various combinations of in-Eclipse/not-in-Eclipse. The not-in-Eclipse bits are not run in Eclipse for a reason (read: "I don't care to jump through the hoops necessary to force them to."); however, lamentably, sometimes I get to (read: "have no choice but to") debug those bits too. And so, on the advice of the aforementioned blog post, I added these options to my startup scripts for the to-be-remotely-debugged VMs:
-Xdebug -Xrunjdwp:transport=dt_socket,address=8001,server=y,suspend=n
did some Eclipse magic ('Run | Open Debug Dialog... | (right-click) Remote Java Application | New | (fill in the details)') to point my debugger at the appropriate host and port, and I was off to the races.

The moral of the story: some days you just can get rid of a bomb.

(Author's Note: Bonus points to any commenters who can identify that seriously obscure reference!)

Tuesday, April 29, 2008

In Which I Reconnect With Ruby Just in Time to Finish My Master's Degree

The fates have conspired to force me to end my graduate school career with a bang. Fortunately, the bang is not the sound of my head exploding, as I feared it would be at the beginning of this semester. I'm taking two classes -- Theory of Complexity (about P vs. NP, BPP, #P, complexity of quantum computing...you know the drill; if you don't know the drill, try The Complexity Petting Zoo for an introduction) and Applied Graph Theory. Each is fascinating in its own right, but the graph theory class has really made an impression on me. So much so that I think if I'd taken it earlier on, I probably would have been tempted to do a thesis in that area. As it is, I'm stuck with my lame default choice of the "coursework-only" option for the Master's degree I'll be finishing come mid-May (the 17th, just in case anyone wants to send a present).

However, to my credit, I did manage to complete a significant semester-long project in graph theory, using Ruby as the implementation language. Why Ruby, and not Java (which I use professionally right now) or Scala, or any of the other possibilities I blather about on this blog?

Well, if you're a seasoned Ruby user you'll know why -- there's not much else (that I'm accustomed to) that's suited to the kind of rapid development/iterability I needed to get this thing done on time. My primary concerns throughout were a) to be able to complete the implementation with enough time left over to write up my results and prepare my in-class presentation, and 2) see concern a) above. Sure, Java's static typing or Scala's functional orientation might have given me some (nominally) safer code, but here, speed of development was of the essence. Advantage: Ruby.

The actual project? It's an implementation of an algorithm due to Hermann Stamm-Wilbrandt of the University of Bonn (Google is your friend...) to draw a maximal planar graph in linear time. Actually, the intent of the core algorithm is to "embed", not "draw" the graph; although I also implemented an optional enhancement to assign x-y coordinates to the vertices of the embedded graph for drawing purposes. Using Ruby, I was able to implement the entire project in less than 500 lines -- including blank lines, comments, debug, and other cruft -- of gloriously unoptimized, un-statically-typed, un-refactored, un-IDE'd-to-within-an-inch-of-its-life Ruby code.

I shudder to think what the LOC count would have been for Java.

Even in Ruby, the bulk of the code was either utilitarian in nature (e.g., a Line class and a Triangle class to help with the geometry of deciding where vertices should lie in the plane; some vector math for seeing if two points are on the same side of a line), or was wrapped up in simple one-to-two-liner convenience methods to support the main objective. This stuff accounted for about 120 lines. And the <500 lines of code also includes my from-scratch graph data structure (typically of the adjacency list variety), along with about 35 lines of code to emit the output in displayable format. The guts of the actual implementation of Stamm-Wilbrandt's embedding algorithm took about 130 lines of Ruby code (including blanks and comments), plus another 70 or so to handle a complicated special case of the optional x-y coordinate assignment.

My only regret is that I don't know Python well enough to have done the entire implementation in that language. I say that primarily because I used the totally awesome NodeBox as my drawing solution. NodeBox is written using PyObjC (Mac only -- sorry Windows/*n[i|u]x users!), and uses Python as its scripting language. My Ruby implementation actually emits a Python script which I then open with NodeBox to draw the final graph. If it were implemented in Python, then it could just be a NodeBox library.

The Good News: Now I have an excellent excuse to learn Python...

Thursday, February 07, 2008

IE Misbehaving? Fix It With Prototype!

Disclaimer: Since I specifically make reference to where I work in this post, I thought I should also specifically make reference to the fact that on this blog in general, and in this post in particular, I do not speak for my company in any way shape or form. My opinions are mine. Mine, mine, mine! If my company wants to express its opinions, it can get its own damn blog!

So at my day job we're converting the front end of our product from a Java thick client to a Struts 2 / JSP / several-other-buzzwords webapp. We officially support three browsers (in no particular order other than my blatant preference): Firefox, Safari, and Internet Explorer. We're using a fair amount of Ajax via Prototype and Scriptaculous. Again, no big surprise.

One of the problems we've encountered with our Ajax stuff is that anything we return from a normal Ajax call is subject to caching by the browser. Now I know the standard tricks to defeat caching are to a) submit the request as a POST; and 2) submit as a GET, but append a random string (e.g., a timestamp) to the request string to force the browser to fail on the cache lookup.

Unfortunately, being the curmudgeonly programmer and language maven that I am, I object to both of these approaches. Sometimes (most times) GET is the proper verb to use for my request; it's not changing state on the server, just retrieving it. POST should be used for changing state. The random string hack is just that -- a hack. If the browser is going to cache the results of a single request, it's probably going to cache the result of each "unique" request we create by tacking on this otherwise-irrelevant snippet of "data". I don't want all that unnecessary cruft in my customers' caches if I can help it.

There must be a better way.

Fortunately, there is a better way as of HTTP 1.1, in the form of the Cache-Control response header. See Section 14.9 of the HTTP 1.1 spec for all the gory details. Suffice it to say, including a response header of Cache-Control: no-cache with all of our Ajax responses worked to defeat caching of our Ajax-GET snippets.

Except on IE.

Are you surprised? I was, but only because Cache-Control: no-cache actually works on IE, to a point. So it wasn't obvious at first that IE was screwing up. Fortunately, we have the world's best quality engineering team, so they caught the problem when we lowly developers weren't seeing it.

Long story short: it turns out that (in our application at least; YMMV) the Cache-Control: no-cache response header actually works to prohibit caching in IE. Until the response is > ~8K in size. At which point IE caches it anyway. Which leads to all kinds of fun "but I saved my edits, I know I did" debates between QE and development when the browser returns the cached results instead of the latest and greatest.

This is where Prototype comes in. You see, Prototype already has a set of headers it adds to every Ajax request submitted through it. For example, it sets the header X-Requested-With to XMLHttpRequest. We use this in our app to distinguish between Ajax GETs and non-Ajax (NAjax?) GETs. Comes in handy sometimes. (Note: See Prototype's source file prototype.js, specifically the Ajax.Request.setRequestHeaders() function for the details on this; ~ line 1241 in Prototype 1.6.0.2.)

So the trick is to get Prototype to include another header with its Ajax requests: If-Modified-Since. Set If-Modified-Since to some datetime in the past, and the browser should always go to the server instead of the (expired) cache. I could do this by hacking my prototype.js file to include this new header in Ajax.Request.setRequestHeaders(), but then I'd have to remember to re-hack every time I upgrade Prototype. Not fun. I could also submit it as a patch, I suppose, but I don't know that everyone needs this behavior by default.

Enter The Next Best Thing: Functional Programming! Prototype has a wonderful FP-ish function called wrap(), which is defined on Element.Methods (prototype.js, ~ line 1652 in 1.6.0.2). wrap() lets me extend the existing setRequestHeaders() function from the outside thusly:
Ajax.Request.prototype.setRequestHeaders =
Ajax.Request.prototype.setRequestHeaders.wrap(
function(original) {
// do my stuff first; e.g., set 'If-Modified-Since'
original() // then call the original version
})
In case this isn't clear, I'm replacing the original definition of the setRequestHeaders() function with a new (anonymous) function that does my stuff first, then does whatever the original was defined to do. To Java programmers circa 2004, this looks kind of like "before advice" in Aspect-Oriented Programming. Without the new-language-compiler-tools-runtime-stack baggage. To functional programming types, it looks like function composition. Without the lambdas and general my-thesis-is-bigger-than-yours snootiness.

To me it looks like a pretty nifty hack, one that plays by everyone's rules.

Well. Except IE's.

But I'm okay with that.

Friday, February 01, 2008

From the Bonehead File: Pragmatic Programmers Books And "Save As PDF"

I (heart) my MacBook Pro.

One of the main reasons I (heart) my MacBook Pro is Mac OS X's built-in "Print... | PDF | Save as PDF..." feature. I've been reading a lot of papers from various online sources recently for my schoolwork, and I love being able to "print" them to a file on my local filesystem for offline reading. HTML, postscript, whatever format you care to publish your paper in, if I can browse it, I can PDF it.

Unfortunately, though, I've gotten so much in the habit of printing to PDF that I've unwittingly given away some of the functionality of some of my documents.

You see, I buy a lot of PDF versions of books from the excellent Pragmatic Programmers catalog. Their PDFs are, without serious exception, among the most informative resources I have at my disposal. But because my browser renders them, and because I'm so used to "printing" my PDFs, I've gotten into the habit of browsing to them and saving them as PDF from the browser. Which, of course, totally wrecks the internal (and external) hyperlinks they have built into the books.

D'oh!

The good news is, the Prags allow one to regenerate purchased PDFs at will (Note: sorry for the spike in regeneration requests this morning, guys! Give the gerbils an extra pellet or two for me.), so I'm back in business, pragmatic-hyperlink-wise.

Saturday, January 19, 2008

Fox/FXRuby/Leopard, Part the Second

As a follow-on to my previous post, I discovered that there's a little more work to be done to actually be able to use my freshly installed FXRuby. I tried using the listexample.rb program from the aforementioned Pragmatic Programmers book, and promptly found that the 'fox16' library couldn't be required. Grumble.

Fortunately, this post on the fxruby-users mailing list (again, courtesy of Lyle Johnson, lead programmer of FXRuby and author of the PragBook) was able to get me going:
a workaround is to use the "gem env" command to identify your Gems installation directory (usually something like /usr/local/lib/ruby/gems/1.8) and switch to the "gems/fxruby-[version]/ext/fox16" directory underneath that:
cd /usr/local/lib/ruby/gems/1.8/gems/fxruby-1.6.0/ext/fox16
and then type:
make
Note: Since RubyGems comes installed with Leopard, your gem installation directory is more likely to be something like "/Library/Ruby/Gems/1.8", at least if you did a fresh install as I did. YMMV.

But here I encountered the same architecture problem I referenced in my previous post. This time I ended up editing the Makefile and deleting all references to "-arch ppc"; setting the ARCHFLAGS environment variable before running make didn't cut it for me.

Once you've re-made the gem, you should be able to verify your installation in irb:
>> require 'fox16'
=> true
Two final caveats:
  • I had to modify the PragExample with the line "require 'rubygems'" at the top of the file, right before the "require 'fox16'" line. This was because I haven't yet set my RUBYOPT environment variable in Leopard to require rubygems automatically for me.
  • The example program worked fine for me after all this (try holding the shift button to do multiple selections), until I quit, at which point I got:
    X Fatal Error.
    Abort trap
So this FXRuby thing is not an exact science yet. Sigh.