Monday, June 09, 2008

WWDC Keynote 2008: Are You Kidding Me?

iPhone and the .Mac replacement: About two full hours.

Next-gen Mac OS (10.6 -- "Snow Leopard"): About 10 seconds (a brief announcement that it's being discussed in an afternoon session with Bertrand Serlet).

New Mac hardware (e.g., the rumored Mac tablet): 0 seconds.

Mac hardware updates (e.g., the rumored laptop redesign/s): 0 seconds.

"One-More-Thing": 0 seconds.

I'm just waiting for this announcement from Cupertino:
Apple Inc. (formerly-and-clearly-no-longer Apple Computer Inc.) proudly announces the obliteration of any computer-related product from its catalog. Going forward, the laptop computer formerly known as the "MacBook Air" will be known as the "iPhone Maxi"*. And the segment-dominating laptop computers formerly known as the "MacBook" and "MacBook Pro" will be renamed the "iPhone Big-n-Bulky"* and "iPhone Bigger-n-Bulkier"*.

*iPhone cell phone functionality not included.

Wednesday, May 21, 2008

[Mac] pbcopy == Brilliant

For Mac users only (as far as I know):
cat filename | pbcopy
This bit of magic, as I just discovered, will copy the contents of the cat-ed file to the clipboard, for easy pasting wherever you need to paste it.

Macs. Rule.

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.