<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-16979882</id><updated>2011-11-18T19:21:27.284-08:00</updated><category term='linux'/><category term='ruby'/><category term='meizu'/><category term='emacs'/><category term='java'/><category term='python'/><category term='crap'/><category term='twitter'/><category term='maven'/><category term='music'/><category term='lisp'/><category term='beauty'/><category term='architecture'/><category term='georgia tech'/><category term='work'/><category term='oracle'/><title type='text'>M-x blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://escx.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>28</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-16979882.post-973137256214056813</id><published>2010-02-21T16:39:00.000-08:00</published><updated>2010-02-21T17:10:17.656-08:00</updated><title type='text'>Thanks, Sean!</title><content type='html'>Thanks to my friend, Sean, I noticed yesterday that my last blog entry was posted exactly one year ago today.  Well, I'm nothing if not punctual, and I'm committed to blogging at least once a year, whether I have anything to say or not.&lt;br /&gt;&lt;br /&gt;So on the off-chance you're wondering what I've been doing this past year...&lt;br /&gt;&lt;br /&gt;Well... I learned to juggle... a little... a few tricks, only 3 balls.&lt;br /&gt;&lt;br /&gt;It's a little depressing when I think about it.&lt;br /&gt;&lt;br /&gt;Thanks again, Sean.  ;-)&lt;br /&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-973137256214056813?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/973137256214056813/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=973137256214056813' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/973137256214056813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/973137256214056813'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2010/02/thanks-sean.html' title='Thanks, Sean!'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-8804390376401982098</id><published>2009-02-21T12:30:00.000-08:00</published><updated>2009-02-22T14:41:28.412-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>A little lithp</title><content type='html'>After almost 20 years of fairly hardcore &lt;a href="http://www.gnu.org/software/emacs/tour/"&gt;Emacs&lt;/a&gt; love, I finally wrote my first Lisp macro.  And now I want to smear my good fortune all over you, dear reader, by recounting my experience in more depth.&lt;br /&gt;&lt;br /&gt;How lucky for you!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;About Lisp...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Created in 1958, Lisp is the 2nd oldest (not to mention sexiest) programming language in widespread use today.  Its syntax is simple: lists of tokens contained within parentheses.  In Lisp, code and data are represented the same way: as lists of tokens, duh.  This deceptively powerful quality is what allows "Lispers" to create new syntax, i.e. &lt;span style="font-style: italic;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Domain-specific_language"&gt;Domain Specific Languages&lt;/a&gt;&lt;/span&gt;, within Lisp itself.&lt;br /&gt;&lt;br /&gt;Lisp is basically what the "executable XML geniuses" -- yes, I'm looking at you Ant, Spring, J2EE, VoiceXML, and countless others -- should have used!  XML is what you use when your chosen language for &lt;span style="font-style: italic;"&gt;manipulating&lt;/span&gt; data can't adequately &lt;span style="font-style: italic;"&gt;represent&lt;/span&gt; it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;My Macro&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ok, enough with the history.  Here's the macro:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;require&lt;/span&gt; '&lt;span class="constant"&gt;sqlplus&lt;/span&gt;)&lt;br /&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;def-oracle&lt;/span&gt; (name connect-string)&lt;br /&gt;  (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((buf-name (concat &lt;span class="string"&gt;"oracle-"&lt;/span&gt; (symbol-name name)))&lt;br /&gt;         (fun-name (intern buf-name)))&lt;br /&gt;    `(&lt;span class="keyword"&gt;defun&lt;/span&gt; ,fun-name ()&lt;br /&gt;       (interactive)&lt;br /&gt;       (sqlplus ,connect-string ,buf-name))))&lt;br /&gt;&lt;/pre&gt;Lisp macros are like functions, except that instead of computing a value, they compute another Lisp expression that will compute the value.  In this case, my macro creates a &lt;span style="font-style: italic;"&gt;defun&lt;/span&gt; expression, which defines a function.  Simple, huh?&lt;br /&gt;&lt;br /&gt;The trick to macros is the backquote character: &lt;tt&gt;`&lt;/tt&gt;.  Combined with the comma character, they allow you to selectively evaluate elements of a quoted list.  In my macro, the list whose first token is &lt;tt&gt;defun&lt;/tt&gt; is quoted (by a backquote) and my "selectively evaluated elements" are &lt;tt&gt;fun-name&lt;/tt&gt;, &lt;tt&gt;connect-string&lt;/tt&gt;, and &lt;tt&gt;buf-name&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;Here's how I invoke my macro:&lt;br /&gt;&lt;pre&gt;(def-oracle dev   &lt;span class="string"&gt;"user/password@dev.host:port/sid"&lt;/span&gt;)&lt;br /&gt;(def-oracle qa    &lt;span class="string"&gt;"user/password@qa.host:port/sid"&lt;/span&gt;)&lt;br /&gt;(def-oracle prod  &lt;span class="string"&gt;"user/password@prod.host:port/sid"&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;After those lines execute, three new functions are created:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;oracle-dev&lt;br /&gt;&lt;/li&gt;&lt;li&gt;oracle-qa&lt;br /&gt;&lt;/li&gt;&lt;li&gt;oracle-prod&lt;/li&gt;&lt;/ul&gt;Ta-da!&lt;br /&gt;&lt;br /&gt;Um, hello?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Your stolid reaction belies the awesomeness of my macro!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Maybe I should've started with my reasons for creating the macro in the first place.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create a handy function that "wraps" the Emacs &lt;tt&gt;&lt;a href="http://www.emacswiki.org/emacs/SqlPlus"&gt;sqlplus&lt;/a&gt;&lt;/tt&gt; function for each of the databases to which I must often connect&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Don't prompt me for the dang connection credentials every time I invoke the handy wrapper function&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Enforce a consistent naming convention for the various sqlplus buffers I have open so that I may, for example, easily switch between qa and dev&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Make it easy to add more of these handy wrapper functions&lt;/li&gt;&lt;/ul&gt;There.  Is that better?  I certainly think so.  :-)&lt;br /&gt;&lt;br /&gt;But perhaps it would be enlightening to see what I would've had to code to achieve the same result &lt;span style="font-style:italic;"&gt;without&lt;/span&gt; using a macro:&lt;br /&gt;    &lt;pre&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;oracle-dev&lt;/span&gt; () &lt;br /&gt;  (interactive)&lt;br /&gt;  (sqlplus &lt;span class="string"&gt;"user/password@dev.host:port/sid"&lt;/span&gt; &lt;span class="string"&gt;"oracle-dev"&lt;/span&gt;))&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;oracle-qa&lt;/span&gt; () &lt;br /&gt;  (interactive)&lt;br /&gt;  (sqlplus &lt;span class="string"&gt;"user/password@qa.host:port/sid"&lt;/span&gt; &lt;span class="string"&gt;"oracle-qa"&lt;/span&gt;))&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;oracle-prod&lt;/span&gt; () &lt;br /&gt;  (interactive)&lt;br /&gt;  (sqlplus &lt;span class="string"&gt;"user/password@prod.host:port/sid"&lt;/span&gt; &lt;span class="string"&gt;"oracle-prod"&lt;/span&gt;))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;See how verbose and redundant that is?  It's positively &lt;span style="font-weight:bold;"&gt;VERBOSIDUNDANT!!!&lt;/span&gt;  And my naming/calling conventions are sprinkled all over the place.  To change any one would require multiple redundant changes.  And that's not &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;DRY&lt;/a&gt;, and that's not good.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-8804390376401982098?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/8804390376401982098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=8804390376401982098' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8804390376401982098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8804390376401982098'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2009/01/little-lithp.html' title='A little lithp'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-6869175473161670501</id><published>2008-08-15T22:20:00.000-07:00</published><updated>2008-10-13T19:20:32.368-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='crap'/><title type='text'>PoopTag = Social Defecation</title><content type='html'>(It's not as gross as it sounds!)&lt;br /&gt;&lt;br /&gt;Everybody poops.  Every U.S. president.  Every Jonas Brother.  Every Olympic athlete.  Even Michael Phelps.  Actually, &lt;a href="http://blogs.wsj.com/health/2008/08/13/the-michael-phelps-diet-dont-try-it-at-home/" target="_blank"&gt;at 12,000 calories a day&lt;/a&gt;, he poops a LOT!  &lt;br /&gt;&lt;br /&gt;And so while you're pooping, have you ever wondered who else is pooping at exactly the same time?  Really?  Me, too!&lt;br /&gt;&lt;br /&gt;So with the help of some friends, I created &lt;a href="http://pooptag.com"&gt;PoopTag&lt;/a&gt;, a sort of game that strives to be both encouraging and enlightening.  It's easy to play:  you simply notify PoopTag whenever you poop.  You do this by sending a &lt;i&gt;direct message&lt;/i&gt; to the &lt;a href="http://twitter.com/pooptag"&gt;pooptag account on Twitter&lt;/a&gt; using your mobile device.  PoopTag will then determine whether anyone else is pooping at that time.  If they are, you will have successfully "tagged" them, and PoopTag will notify its followers of this happy event.&lt;br /&gt;&lt;br /&gt;And in response to your direct message, whether you successfully tag anyone or not, you will receive a random pithy reply.  That's the "encouraging and enlightening" part.&lt;br /&gt;&lt;br /&gt;There are two modes of play:  public and covert.  You decide which way to play each time you poop.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Public (send more than one character):  &lt;blockquote&gt;&lt;tt&gt;d pooptag This text will be broadcast to all PoopTaggers&lt;/tt&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Covert (send any single character):  &lt;blockquote&gt;&lt;tt&gt;d pooptag !&lt;/tt&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Obviously, the public notification makes it easy for someone to tag you, though they must be pooping in order to do so.&lt;br /&gt;&lt;br /&gt;Eventually, we'll publish statistics at &lt;a href="http://pooptag.com"&gt;pooptag.com&lt;/a&gt;, enabling services like &lt;i&gt;Poop Cycle Compatibility Matching&lt;/i&gt;, prospective home-buyer bathroom sizing tools, and life-saving medical history to present to your doctor, among other things.&lt;br /&gt;&lt;br /&gt;Come join the fun!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;UPDATE 9/20/08&lt;/b&gt;: Changed covert pooping method from an empty message to one containing only punctuation due to Twitter no longer delivering blank direct messages.&lt;br /&gt;&lt;b&gt;UPDATE 10/13/08&lt;/b&gt;: Covert pooping now triggered by a message containing only a single character (letter, number, punctuation, whatever)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-6869175473161670501?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/6869175473161670501/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=6869175473161670501' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/6869175473161670501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/6869175473161670501'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2008/08/pooptag-social-defecation.html' title='PoopTag = Social Defecation'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-2537190791173371867</id><published>2008-08-09T12:03:00.000-07:00</published><updated>2010-06-10T13:31:35.339-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='beauty'/><title type='text'>Create</title><content type='html'>I'm big on creativity.  You know, building stuff.  Putting things where nothing was before.  I think it's both the secret to happiness and the meaning of life.  I think it's what humans are SUPPOSED TO DO.  &lt;br /&gt;&lt;br /&gt;Create.  Re-create.  Recreation.  Play, especially at work.&lt;br /&gt;&lt;br /&gt;I follow &lt;a href="http://en.wikipedia.org/wiki/Why_the_lucky_stiff"&gt;why the lucky stiff&lt;/a&gt; on Twitter.  &lt;a href="http://twitter.com/_why"&gt;His tweets&lt;/a&gt; are usually funny and clever.  And once in a while, he'll write something like this:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;when you don't create things, you become defined by your tastes rather than ability. your tastes only narrow &amp; exclude people. so create.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Very cool.&lt;br /&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-2537190791173371867?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/2537190791173371867/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=2537190791173371867' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/2537190791173371867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/2537190791173371867'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2008/08/create.html' title='Create'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-6479433429202492526</id><published>2008-06-12T18:06:00.000-07:00</published><updated>2008-06-12T18:09:17.929-07:00</updated><title type='text'>Making Gardening Cool</title><content type='html'>As if we didn't have enough, here's &lt;a href="http://greenthumbr.com/members/bob/journal/2008/6/11/weed_flamer_makes_weeding_fun"&gt;yet another reason ya gotta love Bob!&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-6479433429202492526?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/6479433429202492526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=6479433429202492526' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/6479433429202492526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/6479433429202492526'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2008/06/making-gardening-cool.html' title='Making Gardening Cool'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-4482561971162018672</id><published>2008-05-18T08:30:00.000-07:00</published><updated>2008-05-18T18:15:07.268-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><category scheme='http://www.blogger.com/atom/ns#' term='georgia tech'/><title type='text'>Trying to Save the Crum &amp; Forster</title><content type='html'>My grandfather, &lt;a href="http://www.crossleys.org/buck" target="_blank"&gt;Lewis "Buck" Crook&lt;/a&gt;, was a prominent architect.  He and his partner, Ernest "Ed" Ivey, comprised the firm of &lt;span style="font-style: italic;"&gt;Ivey &amp;amp; Crook&lt;/span&gt;, responsible for almost 600 commissions over the course of 43 years, most of which were built in and around the city of Atlanta, Georgia.&lt;br /&gt;&lt;br /&gt;One of them, &lt;a href="http://www.crossleys.org/buck/images/commercial/job176a.jpg" target="_blank"&gt;the Crum &amp;amp; Forster building&lt;/a&gt;, a midtown landmark at 771 Spring Street since 1927, is in imminent danger of being torn down.  Ironically, the demolition is being proposed by Georgia Tech, who purchased the property in December of 2007, apparently with the intent to demolish it and expand the development of their &lt;a href="http://en.wikipedia.org/wiki/Technology_Square" target="_blank"&gt;Technology Square&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Georgia Tech is the alma mater of both my grandfather and his partner, Ed Ivey, who helped found the school of architecture there one hundred years ago this year.  Through their works, they made a substantial contribution to the esteemed academic reputation Georgia Tech enjoys today.&lt;br /&gt;&lt;br /&gt;Rather than destroy it, Georgia Tech has an opportunity to celebrate the work of &lt;span style="font-style: italic;"&gt;Ivey &amp;amp; Crook&lt;/span&gt;, just as world-renowned architect John Portman, another Tech graduate, did when he expanded Emory University's student center in the 1980's.  &lt;span style="font-style: italic;"&gt;Ivey &amp;amp; Crook&lt;/span&gt; designed and built &lt;a href="http://www.crossleys.org/buck/schools/emory.html" target="_blank"&gt;the original dining hall at Emory&lt;/a&gt;.  Portman could've torn it down, but instead he chose to incorporate it into his own vision for the Dobbs University Center.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;Georgia Tech has every reason to do the same for Technology Square!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;An &lt;a href="http://www.ipetitions.com/petition/savecrumforster/"&gt;online petition to save the Crum &amp;amp; Forster&lt;/a&gt; has been created.  Please add your name.&lt;br /&gt;&lt;br /&gt;The building has an &lt;a href="http://save771spring.blogspot.com/"&gt;official blog&lt;/a&gt;.  More pictures and info can be found &lt;a href="http://tomitron-sure.blogspot.com/2008/05/another-old-building.html"&gt;here&lt;/a&gt;, &lt;a href="http://save771spring.blogspot.com/2008/05/facts.html"&gt;here&lt;/a&gt;, and &lt;a href="http://surf303.com/crum/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-4482561971162018672?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/4482561971162018672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=4482561971162018672' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/4482561971162018672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/4482561971162018672'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2008/05/trying-to-save-crum-forster.html' title='Trying to Save the Crum &amp; Forster'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-1188466846208371210</id><published>2008-05-06T07:09:00.000-07:00</published><updated>2008-05-06T07:18:29.340-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='work'/><title type='text'>Bizzlr</title><content type='html'>Our &lt;a href="http://prwebpodcast.com/releases/pod906074.htm"&gt;first press release&lt;/a&gt;!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-1188466846208371210?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/1188466846208371210/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=1188466846208371210' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1188466846208371210'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1188466846208371210'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2008/05/bizzlr.html' title='Bizzlr'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-5204840778578542528</id><published>2007-11-03T11:18:00.000-07:00</published><updated>2007-11-03T17:51:01.525-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='beauty'/><category scheme='http://www.blogger.com/atom/ns#' term='crap'/><title type='text'>A Few Poems</title><content type='html'>While doing some digital housekeeping recently, I ran across some poems I wrote back in the mid 90's when I worked with a very creative group of people at a now-defunct company.  We were programmers, each responsible for one or more inter-related components of a large distributed system.  When someone released a new module, the others would have to recompile to incorporate the changes.  Often, a new-release email would be accompanied by a poem -- most often original -- or maybe song lyrics or even particularly elegant snippets of code.  &lt;br /&gt;&lt;br /&gt;My component was called the &lt;i&gt;Data Access Daemon&lt;/i&gt; or DAD, for short.  My first release email included this haiku:&lt;pre&gt;&lt;br /&gt;  Restless, like a child,&lt;br /&gt;  searching for its potential,&lt;br /&gt;  DAD, ever-changing.&lt;/pre&gt;&lt;br /&gt;Many of the poems I wrote were related to some feature of the DAD, i.e. stupid when taken out of context in a blog post, but a few of them I think stand on their own, or are at least "somewhat less stupid":&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Quicksort&lt;/span&gt;&lt;br /&gt;=========&lt;br /&gt;I happened across a big pile of stuff&lt;br /&gt;  And decided that it should be sorted.&lt;br /&gt;The time I could spend was hardly enough,&lt;br /&gt;  Any more time could not be afforded.&lt;br /&gt;&lt;br /&gt;I picked up a thing, at random, I'm sure,&lt;br /&gt;  And to each of the rest I compared it.&lt;br /&gt;I made two more piles of stuff, as it were:&lt;br /&gt;  The things found above and below it.&lt;br /&gt;&lt;br /&gt;To each of those piles, the same thing I done,&lt;br /&gt;  And to those thereafter created.&lt;br /&gt;And not 'til the size of each pile was one --&lt;br /&gt;  Were my efforts quite quickly abated.&lt;br /&gt;&lt;br /&gt;Hardly a minute of time had passed by,&lt;br /&gt;  When I realized something important.&lt;br /&gt;The stuff that had been on that very first pile,&lt;br /&gt;  After just a short while, was now sorted!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;A Little Number I Call "9"&lt;/span&gt;&lt;br /&gt;==========================&lt;br /&gt;Begin with twelve and four,&lt;br /&gt;  then take away seven.&lt;br /&gt;If you add two more,&lt;br /&gt;  You're left with eleven.&lt;br /&gt;&lt;br /&gt;Eleven, you could say,&lt;br /&gt;  Is two one's combined.&lt;br /&gt;If you take those away,&lt;br /&gt;  You end up with nine.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Stressed (Chill) Out&lt;/span&gt;&lt;br /&gt;====================&lt;br /&gt;Never settle (Be still) for less.&lt;br /&gt;Always strive (until) to be best.&lt;br /&gt;Never stop (your will) to rest.&lt;br /&gt;Everything (is nil.) is a test.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Work It&lt;/span&gt;&lt;br /&gt;=======&lt;br /&gt;"Work it, baby, work it!"&lt;br /&gt;  I heard some dancers say.&lt;br /&gt;"Toil it, baby, toil it!"&lt;br /&gt;  I joined in their refrain.&lt;br /&gt;&lt;br /&gt;At this, they hesitated,&lt;br /&gt;  And turned, and frowned at me.&lt;br /&gt;"What did you say?!" they shouted.&lt;br /&gt;  For dancers, they seemed mean.&lt;br /&gt;&lt;br /&gt;A misinterpretation&lt;br /&gt;  Was all that had occurred.&lt;br /&gt;Instead of inspiration,&lt;br /&gt;  It was "toilet" that they heard.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Men and Women&lt;/span&gt;&lt;br /&gt;=============&lt;br /&gt;"Men are from Mars," of course,&lt;br /&gt;  "And women are from Venus."&lt;br /&gt;As if those stupid metaphors,&lt;br /&gt;  Some wisdom they could gain us!&lt;br /&gt;&lt;br /&gt;Men and women are different,&lt;br /&gt;  But not because of Venus.&lt;br /&gt;Only women can get pregnant,&lt;br /&gt;  And only men can have a... prostate!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Kicking Squirrels&lt;/span&gt;&lt;br /&gt;=================&lt;br /&gt;A squirrel came hopping through my yard,&lt;br /&gt;  And got a little careless.&lt;br /&gt;The distance wasn't all that far --&lt;br /&gt;  From him to my Adidas.&lt;br /&gt;&lt;br /&gt;Ducking behind the nearest tree,&lt;br /&gt;  I lied in vengeful wait.&lt;br /&gt;And by the time he noticed me,&lt;br /&gt;  It was much too late.&lt;br /&gt;  &lt;br /&gt;In mid-jump I caught him square --&lt;br /&gt;  Just like that fella, Pele.&lt;br /&gt;Soaring higher into the air,&lt;br /&gt;  I watched him sail away.&lt;br /&gt;&lt;br /&gt;He landed atop my neighbor's house&lt;br /&gt;  A little shaken, but still alive.&lt;br /&gt;About that time I spied a mouse,&lt;br /&gt;  But my mom called me back inside.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Cubicles&lt;/span&gt;&lt;br /&gt;========&lt;br /&gt;The chains descended from the ceiling,&lt;br /&gt;  And fastened to the tops --&lt;br /&gt;  Of the cubicles.&lt;br /&gt;The motors began slowly turning,&lt;br /&gt;  Then rising were the walls --&lt;br /&gt;  Like theatre curtains.&lt;br /&gt;The workers stopped what they were doing,&lt;br /&gt;  And looked up from their desks --&lt;br /&gt;  To peer at themselves.&lt;br /&gt;Then came some men who owned the building,&lt;br /&gt;  Too late to warn the folks --&lt;br /&gt;  Of their new plans.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Nashville and Seattle&lt;/span&gt;&lt;br /&gt;=====================&lt;br /&gt;What if Nashville was Seattle&lt;br /&gt;  And therefore, vice versa?&lt;br /&gt;In the place of Mother Maybelle&lt;br /&gt;  Could be Ann and Nancy Wilson.&lt;br /&gt;&lt;br /&gt;Then Little Jimmy Dickens &lt;br /&gt;  And Minnie Pearl, yes ma'am,&lt;br /&gt;Would be Little Jimi Hendrix&lt;br /&gt;  And Minnie Pearl Jam.&lt;br /&gt;&lt;br /&gt;We could hear Kurt Cobain&lt;br /&gt;  Or even Courtney Love&lt;br /&gt;Sing "Blue Eyes Cryin' in the Rain"&lt;br /&gt;  Or "Wings of a Dove"&lt;br /&gt;&lt;br /&gt;Wouldn't it make you feel alive&lt;br /&gt;  To see Shania Twain&lt;br /&gt;Hockin' loogies between stage-dives&lt;br /&gt;  Apparently in pain?&lt;br /&gt;&lt;br /&gt;Well, maybe not "alive" per se,&lt;br /&gt;  But it would be pretty rad,&lt;br /&gt;Seeing Alice and Reba in chains&lt;br /&gt;  Switching cities, switching fads.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Fonts&lt;/span&gt;&lt;br /&gt;=====&lt;br /&gt;Arial is fairly boring,&lt;br /&gt;  And Courier's way too plain.&lt;br /&gt;Modern is the same ol' story,&lt;br /&gt;  And frankly, Script is a pain.&lt;br /&gt;&lt;br /&gt;Garamond comes off as haughty.&lt;br /&gt;  Sans Serif a tad too smooth.&lt;br /&gt;Lucida strikes me as naughty,&lt;br /&gt;  And Roman a bit long in the tooth.&lt;br /&gt;&lt;br /&gt;Symbol is symbolic of nothing.&lt;br /&gt;  Terminal should be put to rest.&lt;br /&gt;Hardly better is Times New Roman,&lt;br /&gt;  And Wingdings is simply a mess!&lt;br /&gt;&lt;br /&gt;In the end, the font doesn't matter.&lt;br /&gt;  You may choose whichever you like.&lt;br /&gt;Whether one or another is better --&lt;br /&gt;  Counts far less than how well you write.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Long-Eared, Pickle-Toed Pirate&lt;/span&gt;&lt;br /&gt;==============================&lt;br /&gt;Wouldn't it be funny --&lt;br /&gt;  If my ears were long, like a bunny?&lt;br /&gt;I'd tuck 'em up under my arm pits.&lt;br /&gt;  That wouldn't be gross, would it?&lt;br /&gt;&lt;br /&gt;Wouldn't you be tickled --&lt;br /&gt;  If instead of toes I had pickles?&lt;br /&gt;I'd clip off the nails and make relish.&lt;br /&gt;  Everyone would be so jealous.&lt;br /&gt;&lt;br /&gt;Wouldn't it be a riot --&lt;br /&gt;  If my leg was a peg, like a pirate?&lt;br /&gt;I'd buckle swashes, say "Aye" and grin,&lt;br /&gt;  And eat relish, and... um... listen?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Tony Gwynn&lt;/span&gt;&lt;br /&gt;==========&lt;br /&gt;Tony Gwynn's amazing,&lt;br /&gt;  But a little overweight.&lt;br /&gt;It won't be getting past him&lt;br /&gt;  If it makes it 'cross his plate.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Rooms&lt;/span&gt;&lt;br /&gt;=====&lt;br /&gt;There are rooms to do your sleeping,&lt;br /&gt;And rooms to take your bath,&lt;br /&gt;Rooms to do your eating,&lt;br /&gt;And rooms in which you laugh.&lt;br /&gt;&lt;br /&gt;Most realtors show you common rooms&lt;br /&gt;In the houses that they sell.&lt;br /&gt;But an agent took me on a tour&lt;br /&gt;I'll long remember well.&lt;br /&gt;&lt;br /&gt;"Here's a room for your improvement,&lt;br /&gt;And one for you to grow.&lt;br /&gt;These rooms are not the kind you rent&lt;br /&gt;They're rooms you come to know."&lt;br /&gt;&lt;br /&gt;That's what the agent sang to me.&lt;br /&gt;That's right, my realtor sings -- &lt;br /&gt;'Bout rooms that ain't so roomy,&lt;br /&gt;But let you spread your wings!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Childbirth&lt;/span&gt;&lt;br /&gt;==========&lt;br /&gt;It won't be long for her.&lt;br /&gt;It will all be over soon.&lt;br /&gt;What now seems like November -&lt;br /&gt;Will tomorrow seem like June.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;What Is Is&lt;/span&gt;&lt;br /&gt;==========&lt;br /&gt;By the definition&lt;br /&gt;  In my deposition,&lt;br /&gt;I have no recollection&lt;br /&gt;  Of a sexual relation.&lt;br /&gt;&lt;br /&gt;I mean to be truthful&lt;br /&gt;  Without being helpful.&lt;br /&gt;To admit that I'm a mouthful&lt;br /&gt;  Would be very, very harmful.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;A Crappy Poem&lt;/span&gt;&lt;br /&gt;=============&lt;br /&gt;"Roses are blue,&lt;br /&gt;  Violets are red."&lt;br /&gt;Er...no... that's not true.&lt;br /&gt;  Nevermind what I said.&lt;br /&gt;&lt;br /&gt;"Stars in the morning,&lt;br /&gt;  Sunshine at night."&lt;br /&gt;Now wait just a minute!&lt;br /&gt;  That can't be right.&lt;br /&gt;&lt;br /&gt;"Ships on the prairie&lt;br /&gt;  Planes in the sea."&lt;br /&gt;What am I saying?&lt;br /&gt;  What's happening to me?&lt;br /&gt;&lt;br /&gt;"My eyes won't steady --&lt;br /&gt;  I feel like a nap."&lt;br /&gt;Shut up, already.&lt;br /&gt;  Enough of this crap!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Seven Fools&lt;/span&gt;&lt;br /&gt;===========&lt;br /&gt;Seven separate fools considered their lot,&lt;br /&gt;  And in turn, each of them ruled,&lt;br /&gt;"I know well enough to know I know not,"&lt;br /&gt;  "I'm content", as fools are, "to be fooled".&lt;br /&gt;&lt;br /&gt;"Be wary of too-happy people", one claimed,&lt;br /&gt;  "And also avoid the too-sad",&lt;br /&gt;"Try to eat well!"  "Make beautiful things!"&lt;br /&gt;  "And be a good mother or dad."&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Mr. Stroustrup&lt;/span&gt;&lt;br /&gt;==============&lt;br /&gt;An email, from Bjarne,&lt;br /&gt;  today I received.&lt;br /&gt;"Thanks" was all that --&lt;br /&gt;  it said.&lt;br /&gt;&lt;br /&gt;Ironic that he &lt;br /&gt;  should be thanking me.&lt;br /&gt;I should thank him --&lt;br /&gt;  instead.&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-5204840778578542528?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/5204840778578542528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=5204840778578542528' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/5204840778578542528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/5204840778578542528'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/11/few-poems.html' title='A Few Poems'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-2439408914145633256</id><published>2007-10-13T15:35:00.000-07:00</published><updated>2007-10-13T15:41:47.425-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='crap'/><title type='text'>New Job, New Spin on Frank</title><content type='html'>Well, it's been a while since I've blogged about anything.  Since then, I've signed on with &lt;a href="http://websingularity.com"&gt;WebSingularity&lt;/a&gt;.  We're attempting to build a social networking site that doesn't suck.  More on that later, but for now, in honor of Frank Zappa, here's this:&lt;br /&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/wyN5FhcU-tU"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/wyN5FhcU-tU" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-2439408914145633256?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/2439408914145633256/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=2439408914145633256' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/2439408914145633256'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/2439408914145633256'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/10/well-its-been-while-since-ive-blogged.html' title='New Job, New Spin on Frank'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-8670365382849698448</id><published>2007-07-06T11:53:00.000-07:00</published><updated>2007-08-24T11:55:26.498-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Java shell tips</title><content type='html'>In which jar file does the class, CLASSNAME, reside?&lt;br /&gt;&lt;pre&gt;for i in lib/*.jar; do jar tf $i | grep CLASSNAME &gt;/dev/null &amp;&amp; echo $i; done&lt;/pre&gt;&lt;br /&gt;To recursively search through all jars in the current directory...&lt;br /&gt;&lt;pre&gt;find . -name "*.jar" | while read i; do jar tf $i | grep CLASSNAME &gt;/dev/null &amp;&amp;amp; echo $i; done&lt;/pre&gt;&lt;br /&gt;And here's a handy little Python script that recursively "explodes" J2EE artifacts into their constituent components.  If they're already exploded, it'll "implode" them.  This is a great tool for quickly updating deployment descriptors at the command line.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span class="comment"&gt;#!/usr/bin/env python&lt;br /&gt;#&lt;br /&gt;# Each file passed on the command line that is a valid j2ee archive&lt;br /&gt;# (war, jar, ear, sar) will be either exploded if archived or archived&lt;br /&gt;# if exploded.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; sys,os&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; zipfile &lt;span class="keyword"&gt;import&lt;/span&gt; is_zipfile,ZipFile&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; os.path &lt;span class="keyword"&gt;import&lt;/span&gt; isdir,exists,realpath,dirname,basename,join&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; shutil &lt;span class="keyword"&gt;import&lt;/span&gt; rmtree&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# The result of this try block is a function named 'mktempdir' that&lt;br /&gt;# makes a temporary directory and returns its path.  Python 2.3&lt;br /&gt;# provides tempfile.mkdtemp, which does exactly what we need, but we&lt;br /&gt;# must implement it ourself in older versions.&lt;br /&gt;&lt;/span&gt;&lt;span class="keyword"&gt;try&lt;/span&gt;:&lt;br /&gt;    &lt;span class="keyword"&gt;from&lt;/span&gt; tempfile &lt;span class="keyword"&gt;import&lt;/span&gt; mkdtemp &lt;span class="keyword"&gt;as&lt;/span&gt; mktempdir&lt;br /&gt;&lt;span class="keyword"&gt;except&lt;/span&gt; ImportError:&lt;br /&gt;    &lt;span class="keyword"&gt;from&lt;/span&gt; tempfile &lt;span class="keyword"&gt;import&lt;/span&gt; mktemp&lt;br /&gt;    &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;mktempdir&lt;/span&gt;():&lt;br /&gt;        path = mktemp()&lt;br /&gt;        os.mkdir(path)&lt;br /&gt;        &lt;span class="keyword"&gt;return&lt;/span&gt; path&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# The result of this try block is a function called 'move', which&lt;br /&gt;# moves a file or directory, possibly across filesystems.  Python 2.3&lt;br /&gt;# provides shutil.move, but older versions do not, so we revert to a&lt;br /&gt;# system call.  The os.rename function will not work across&lt;br /&gt;# filesystems.&lt;br /&gt;&lt;/span&gt;&lt;span class="keyword"&gt;try&lt;/span&gt;:&lt;br /&gt;    &lt;span class="keyword"&gt;from&lt;/span&gt; shutil &lt;span class="keyword"&gt;import&lt;/span&gt; move&lt;br /&gt;&lt;span class="keyword"&gt;except&lt;/span&gt; ImportError:&lt;br /&gt;    &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;move&lt;/span&gt;(src,dest):&lt;br /&gt;        os.system(&lt;span class="string"&gt;"mv "&lt;/span&gt;+src+&lt;span class="string"&gt;" "&lt;/span&gt;+dest)&lt;br /&gt;        &lt;br /&gt;&lt;span class="comment"&gt;# The presence of one of these indicates a J2EE artifact.&lt;br /&gt;&lt;/span&gt;&lt;span class="variable-name"&gt;DEPLOYMENT_DESCRIPTORS&lt;/span&gt; = (&lt;span class="string"&gt;"META-INF/application.xml"&lt;/span&gt;,     &lt;span class="comment"&gt;# ear&lt;br /&gt;&lt;/span&gt;                          &lt;span class="string"&gt;"WEB-INF/web.xml"&lt;/span&gt;,              &lt;span class="comment"&gt;# war&lt;br /&gt;&lt;/span&gt;                          &lt;span class="string"&gt;"META-INF/ejb-jar.xml"&lt;/span&gt;,         &lt;span class="comment"&gt;# jar&lt;br /&gt;&lt;/span&gt;                          &lt;span class="string"&gt;"META-INF/jboss-service.xml"&lt;/span&gt;)   &lt;span class="comment"&gt;# sar&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;explode&lt;/span&gt;(file):&lt;br /&gt;    &lt;span class="string"&gt;'''Recursively unpack the contents of a J2EE file into a directory of same name.'''&lt;/span&gt;&lt;br /&gt;    file = realpath(file)&lt;br /&gt;    tmp = mktempdir()&lt;br /&gt;    os.system(&lt;span class="string"&gt;"cd "&lt;/span&gt;+tmp+&lt;span class="string"&gt;"; jar xMf "&lt;/span&gt;+file)&lt;br /&gt;    [explode(join(tmp,f)) &lt;span class="keyword"&gt;for&lt;/span&gt; f &lt;span class="keyword"&gt;in&lt;/span&gt; os.listdir(tmp) &lt;span class="keyword"&gt;if&lt;/span&gt; is_j2ee_zip(join(tmp,f))]&lt;br /&gt;    os.remove(file)&lt;br /&gt;    move(tmp,file)&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt; &lt;span class="string"&gt;"Exploded "&lt;/span&gt;+file&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;implode&lt;/span&gt;(dir):&lt;br /&gt;    &lt;span class="string"&gt;'''Recursively package the contents of a directory into a J2EE file with same name.'''&lt;/span&gt;&lt;br /&gt;    dir = realpath(dir)&lt;br /&gt;    [implode(join(dir,f)) &lt;span class="keyword"&gt;for&lt;/span&gt; f &lt;span class="keyword"&gt;in&lt;/span&gt; os.listdir(dir) &lt;span class="keyword"&gt;if&lt;/span&gt; is_j2ee_dir(join(dir,f))]&lt;br /&gt;    tmp = mktempdir()+&lt;span class="string"&gt;"/"&lt;/span&gt;+basename(dir)&lt;br /&gt;    os.system(&lt;span class="string"&gt;"cd "&lt;/span&gt;+dir+&lt;span class="string"&gt;"; jar cMf "&lt;/span&gt;+tmp+&lt;span class="string"&gt;" *"&lt;/span&gt;)&lt;br /&gt;    rmtree(dir)&lt;br /&gt;    move(tmp,dir)&lt;br /&gt;    os.rmdir(dirname(tmp))&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt; &lt;span class="string"&gt;"Imploded "&lt;/span&gt;+dir&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;is_j2ee_dir&lt;/span&gt;(path):&lt;br /&gt;    &lt;span class="string"&gt;'''Return True if directory contents are structured as a valid J2EE artifact.'''&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; isdir(path):&lt;br /&gt;        &lt;span class="keyword"&gt;for&lt;/span&gt; dd &lt;span class="keyword"&gt;in&lt;/span&gt; DEPLOYMENT_DESCRIPTORS:&lt;br /&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; exists(join(path,dd)): &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="keyword"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;is_j2ee_zip&lt;/span&gt;(path):&lt;br /&gt;    &lt;span class="string"&gt;'''Return True if path refers to a valid J2EE artifact.'''&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; is_zipfile(path):&lt;br /&gt;        files = ZipFile(path).namelist()&lt;br /&gt;        &lt;span class="keyword"&gt;for&lt;/span&gt; dd &lt;span class="keyword"&gt;in&lt;/span&gt; DEPLOYMENT_DESCRIPTORS:&lt;br /&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; dd &lt;span class="keyword"&gt;in&lt;/span&gt; files: &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="keyword"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;for&lt;/span&gt; path &lt;span class="keyword"&gt;in&lt;/span&gt; sys.argv[1:]:&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; is_j2ee_zip(path):&lt;br /&gt;        explode(path)&lt;br /&gt;    &lt;span class="keyword"&gt;elif&lt;/span&gt; is_j2ee_dir(path):&lt;br /&gt;        implode(path)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-8670365382849698448?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/8670365382849698448/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=8670365382849698448' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8670365382849698448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8670365382849698448'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/07/java-shell-tips.html' title='Java shell tips'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-5779523076173233907</id><published>2007-06-18T19:20:00.000-07:00</published><updated>2007-06-18T20:00:25.537-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Stupidest Guitar Chord: D# minor</title><content type='html'>I love the Beatles' song, "I'm Only Sleeping", especially the very first chord, played with a beautiful, exaggerated "up-stroke" for emphasis.  Strictly speaking, listening to the recording, the chord is a D# minor, but a guitar just ain't made to play that!  For a guitarist, a D#m is a pretty stupid chord.&lt;br /&gt;&lt;br /&gt;Why?  Because E minor sounds so much better!  The open strings allow it to ring long and full, using the lowest note possible in standard tuning.  And it's easier to play!  And it's only a half-step away!  A mere semi-tone!&lt;br /&gt;&lt;br /&gt;So either George Martin altered the speed of the recording, or maybe the guitars were tuned down a half step.  But either way, nobody was playing a D#m on their guitar that day.  The Beatles weren't stupid.&lt;br /&gt;&lt;br /&gt;So you shouldn't be, either.  But it's a pain to detune your guitar just to play along, right?  Thankfully, there's an easier way:  &lt;a href="http://audacity.sourceforge.net/"&gt;Audacity&lt;/a&gt;, a free audio editor for Linux, can do magic for the budding guitarist.  It can change the pitch of a song without changing its tempo.  Conversely, it can slow the tempo without changing the pitch, which is really handy for figuring out solos.  What was impossible with analog recordings is now amazingly simple with digital audio.&lt;br /&gt;&lt;br /&gt;Nobody should be forced to play D# minor.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-5779523076173233907?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/5779523076173233907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=5779523076173233907' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/5779523076173233907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/5779523076173233907'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/06/stupidest-guitar-chord-dm.html' title='Stupidest Guitar Chord: D# minor'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-8119094712241947676</id><published>2007-05-15T04:17:00.000-07:00</published><updated>2007-05-15T04:20:33.151-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='crap'/><title type='text'>Tease</title><content type='html'>Tanya Thompson, the third tallest tenured teaching typist, told Timmy Timmington to try to take the touch-typing test tomorrow.&lt;br /&gt;&lt;br /&gt;Ta-da!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-8119094712241947676?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/8119094712241947676/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=8119094712241947676' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8119094712241947676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8119094712241947676'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/05/tease.html' title='Tease'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-8446558209151247246</id><published>2007-05-11T05:05:00.000-07:00</published><updated>2007-05-11T05:51:20.976-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Remote Monitoring with Ruby and sshfs</title><content type='html'>At work I've been doing some performance analysis as of late.  Specifically, I've been combing JBoss server.log files recording the time it takes various transactions to complete.  Admittedly, I'm fighting a little with &lt;a href="http://en.wikipedia.org/wiki/Werner_Heisenberg"&gt;Werner Heisenberg&lt;/a&gt; because configuring the app to produce the log messages on which I depend will affect performance, but my interest is in &lt;i&gt;relative&lt;/i&gt; rather than overall performance, i.e. I'm looking for potential bottlenecks.&lt;br /&gt;&lt;br /&gt;Still, I don't want to take up any more resources from the app server's CPU than absolutely necessary.  The Ruby script that processes the log files obviously makes much use of regular expressions.  Here's an example of a Request object from my script.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  Request.new("Typical request") { |request|&lt;br /&gt;    request.expect /Processing Request/&lt;br /&gt;    request.expect /No existing Session found/&lt;br /&gt;    request.reject /ERROR/&lt;br /&gt;    request.reject /reaping required/&lt;br /&gt;    request.expect /Processing message took \d+ millis/&lt;br /&gt;  } &lt;/pre&gt;&lt;br /&gt;Here's an example of a line from the server.log:&lt;br /&gt;&lt;pre&gt;2007-05-09 16:28:51,511 INFO  [UDPListener] (Thread-16) UDPListener thread running&lt;/pre&gt;&lt;br /&gt;The Ruby script organizes the log messages by thread id.  If it finds matches for each expected regular expression in the correct order without finding any it should reject, it records the total time for the Request.  When finished, it reports various statistics (average, mean, stddev, etc) for each type of Request it found in the log.&lt;br /&gt;&lt;br /&gt;Ideally, I would run that script on a log &lt;i&gt;after&lt;/i&gt; a particular load test was performed, but hey that's no fun!  I'd rather see how the server is doing in realtime, monitoring its performance while it's running.  Here comes &lt;a href="http://en.wikipedia.org/wiki/SSH_Filesystem"&gt;sshfs&lt;/a&gt; to the rescue!&lt;br /&gt;&lt;br /&gt;With sshfs, I can mount the file system of the app server on a remote machine running my Ruby script.  The app server doesn't need Samba or NFS installed, only ssh, and what respectable app server wouldn't have sshd running?&lt;br /&gt;&lt;br /&gt;With the server.log mounted via sshfs, far fewer CPU resources are taken up by the analysis script, no more than if you were "tailing the log" yourself while the app server was running.&lt;br /&gt;&lt;br /&gt;Installing sshfs is very simple on modern Debian-based distros:&lt;br /&gt;&lt;pre&gt;  $ sudo apt-get install sshfs&lt;/pre&gt;&lt;br /&gt;Depending on the distro, you may need to add your user to the 'fuse' group or modprobe fuse, or chmod /dev/fuse, or some other such minor detail, but once installed, remote, realtime log file analysis is a breeze:&lt;pre&gt;&lt;br /&gt;  $ sshfs appsrvr:/ /mnt/appsrvr&lt;br /&gt;  $ cd /mnt/appsrvr/usr/local/jboss/server/default/log&lt;br /&gt;  $ tail -f server.log | statistics.rb&lt;br /&gt;&lt;/pre&gt;So you use local CPU resources to parse a remote log file.  Cool, huh?&lt;br /&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-8446558209151247246?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/8446558209151247246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=8446558209151247246' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8446558209151247246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8446558209151247246'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/05/remote-monitoring-with-ruby-and-sshfs.html' title='Remote Monitoring with Ruby and sshfs'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-8719245807779085080</id><published>2007-04-08T17:06:00.000-07:00</published><updated>2007-04-08T17:10:34.547-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='beauty'/><category scheme='http://www.blogger.com/atom/ns#' term='crap'/><title type='text'>My Mother's Blog</title><content type='html'>I've been meaning to create a blog for my mother for some time now.  I think she's a pretty good writer.  I have to.  She's my mother.&lt;br /&gt;&lt;br /&gt;So here is &lt;a href="http://mymothers.blogspot.com"&gt;My Mother's Blog&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-8719245807779085080?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/8719245807779085080/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=8719245807779085080' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8719245807779085080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8719245807779085080'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/04/my-mothers-blog.html' title='My Mother&apos;s Blog'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-1424505971927944701</id><published>2007-03-11T18:13:00.000-07:00</published><updated>2007-03-11T19:58:01.912-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>1952 Vincent Black Lightning</title><content type='html'>The theme for this weekend: &lt;span style="font-style:italic;"&gt;1952 Vincent Black Lightning&lt;/span&gt;.  That's because I heard Richard Thompson's song for the first time.  It immediately appealed to me, what with its protagonist sharing my name and his girl being red-headed, not to mention some pretty tasty finger picking throughout... but who's this cat, Vincent?&lt;br /&gt;  &lt;br /&gt;And so began my journey of not only learning what the heck a Vincent is -- it's a &lt;a href="http://en.wikipedia.org/wiki/Vincent_Motorcycles"&gt;famous motorcycle&lt;/a&gt; -- but also about &lt;a href="http://www.richardthompson-music.com/"&gt;Richard Thompson&lt;/a&gt;, his technique, and a new open tuning I'd never tried before.  &lt;br /&gt;&lt;br /&gt;I also learned about &lt;a href="http://en.wikipedia.org/wiki/Rollie_Free"&gt;Rollie Free&lt;/a&gt; and decided that being famous for &lt;a href="http://en.wikipedia.org/wiki/Image:Rollie_Free%2C_record_run.jpg"&gt;this photo&lt;/a&gt; is darn cool.&lt;br /&gt;&lt;br /&gt;And even though I've previously &lt;a href="http://escx.blogspot.com/2007/01/learning-to-hear-instrument.html"&gt;cautioned against it&lt;/a&gt;, here is why YouTube is so freaking great:&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/AxKTzwaEa2o"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/AxKTzwaEa2o" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;So I ignored all the stuff I had wanted to get done this weekend and attempted to learn to play the tune.  My youngest daughter thinks I'm a bit obsessed with it.  Maybe so, but it's fun to sing, and especially funny to hear &lt;span style="font-style:italic;"&gt;her&lt;/span&gt; sing it.  &lt;br /&gt;&lt;br /&gt;I ended up with a decent approximation... and a newfound respect for both Richard Thompson and Phil Vincent.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-1424505971927944701?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/1424505971927944701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=1424505971927944701' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1424505971927944701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1424505971927944701'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/03/1952-vincent-black-lightning.html' title='1952 Vincent Black Lightning'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-745462503232161149</id><published>2007-03-04T23:13:00.000-08:00</published><updated>2007-03-06T12:08:19.720-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>Testing EJB3 (JPA) with Maven2</title><content type='html'>This tutorial describes how to test EJB3 beans as a part of a Maven2 build, i.e. without an EJB container but with an in-memory, transactional database.  I make the following assumptions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;JDK 1.5&lt;br /&gt;&lt;/li&gt;&lt;li&gt;EJB 3.0, i.e. Java Persistence API (JPA) 1.0&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Maven 2 (2.0.5)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;I don't assume much more than that.  Our application contains no explicit references to Spring or Hibernate as EJB3 itself borrows much of their goodness. We avoid all the XML config usually associated with Spring/Hibernate by using the JPA persistence annotations in our entity beans.  To facilitate testing, any injected dependency references, e.g. &lt;tt&gt;@PersistenceContext&lt;/tt&gt; or &lt;tt&gt;@EJB&lt;/tt&gt;, are declared package access so that the JUnit test classes can set them appropriately.&lt;br /&gt;&lt;br /&gt;As with many enterprise applications, much of our business logic is embedded within database queries, most in &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/QueryLanguage.html"&gt;JPQL&lt;/a&gt;, and some can be quite complex.  It's vital that we're able to test them outside of a container as a part of our normal, continuously-integrated build.  For that, we use the &lt;a href="http://hsqldb.org/"&gt;Hypersonic (hsqldb)&lt;/a&gt; in-memory database.  We'll need a JPA implementation, of course, and because we deploy to JBoss 4.0 in production, which uses Hibernate's JPA, we choose the same.  Of course, any certified JPA implementation should work just as well.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;b&gt;&lt;tt&gt;pom.xml&lt;/tt&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So our first step is to declare our testing dependencies in our &lt;a href="http://maven.apache.org/guides/introduction/introduction-to-the-pom.html"&gt;POM&lt;/a&gt;:&lt;pre&gt;&lt;br /&gt;  &amp;lt;!-- For in-memory sql testing --&amp;gt;                                                                                 &lt;br /&gt;  &amp;lt;dependency&amp;gt;                                                                                                       &lt;br /&gt;    &amp;lt;groupId&amp;gt;org.hibernate&amp;lt;/groupId&amp;gt;                                                                           &lt;br /&gt;    &amp;lt;artifactId&amp;gt;hibernate-entitymanager&amp;lt;/artifactId&amp;gt;                                                           &lt;br /&gt;    &amp;lt;version&amp;gt;3.2.0.ga&amp;lt;/version&amp;gt;                                                                                &lt;br /&gt;    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;                                                                                        &lt;br /&gt;  &amp;lt;/dependency&amp;gt;                                                                                                      &lt;br /&gt;  &amp;lt;dependency&amp;gt;                                                                                                       &lt;br /&gt;    &amp;lt;groupId&amp;gt;hsqldb&amp;lt;/groupId&amp;gt;                                                                                  &lt;br /&gt;    &amp;lt;artifactId&amp;gt;hsqldb&amp;lt;/artifactId&amp;gt;                                                                            &lt;br /&gt;    &amp;lt;version&amp;gt;1.8.0.7&amp;lt;/version&amp;gt;                                                                                 &lt;br /&gt;    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;                                                                                        &lt;br /&gt;  &amp;lt;/dependency&amp;gt;                                                                                                      &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;Aside:&lt;/i&gt; I toyed with using the new &lt;a href="http://www.h2database.com/"&gt;H2 database&lt;/a&gt; instead of Hypersonic, but the version I tested, 1.0.20061217, included only table locks, causing my app to deadlock.  Hypersonic supports row locking.&lt;br /&gt;&lt;br /&gt;Although not strictly necessary, I recommend adding this to your POM, too:&lt;pre&gt;&lt;br /&gt;  &amp;lt;testResources&amp;gt;                                                                                                     &lt;br /&gt;    &amp;lt;testResource&amp;gt;                                                                                                    &lt;br /&gt;      &amp;lt;directory&amp;gt;src/test/resources&amp;lt;/directory&amp;gt;                                                                 &lt;br /&gt;      &amp;lt;filtering&amp;gt;true&amp;lt;/filtering&amp;gt;                                                                               &lt;br /&gt;    &amp;lt;/testResource&amp;gt;                                                                                                   &lt;br /&gt;  &amp;lt;/testResources&amp;gt;                                                                                                    &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You'll see why in the next section. &lt;br /&gt;&lt;br /&gt;Note that we didn't need to change the POM much.  In particular, no special configuration of the Surefire plugin (the testcase invoker) is required.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;b&gt;&lt;tt&gt;src/test/resources/META-INF/persistence.xml&lt;/tt&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;All JPA apps need a &lt;tt&gt;persistence.xml&lt;/tt&gt; file.  Your app will likely already have one beneath &lt;tt&gt;src/main/resources&lt;/tt&gt;.  Both will be in your CLASSPATH when the tests are run.  The JPA will aggregate them, so you need to make sure all your persistence-units, both real and test, are uniquely named.  How you organize your persistence-units is up to you.  It's probably easiest to have just one for testing, or you might, for example, prefer to create one per test class.&lt;br /&gt;&lt;br /&gt;Here's what I recommend:&lt;pre&gt;&lt;br /&gt; &amp;lt;persistence&amp;gt;                                                                                                         &lt;br /&gt;   &amp;lt;persistence-unit name="hibernate-hsqldb"&amp;gt;                                                                          &lt;br /&gt;     &amp;lt;provider&amp;gt;org.hibernate.ejb.HibernatePersistence&amp;lt;/provider&amp;gt;                                                 &lt;br /&gt;     &amp;lt;jar-file&amp;gt;${project.build.directory}/classes&amp;lt;/jar-file&amp;gt;                                                     &lt;br /&gt;     &amp;lt;properties&amp;gt;                                                                                                      &lt;br /&gt;       &amp;lt;property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/&amp;gt;                                  &lt;br /&gt;       &amp;lt;property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/&amp;gt;                              &lt;br /&gt;       &amp;lt;property name="hibernate.connection.url" value="jdbc:hsqldb:."/&amp;gt;                                               &lt;br /&gt;       &amp;lt;property name="hibernate.connection.username" value="sa"/&amp;gt;                                                     &lt;br /&gt;       &amp;lt;property name="hibernate.connection.password" value=""/&amp;gt;                                                       &lt;br /&gt;       &amp;lt;property name="hibernate.hbm2ddl.auto" value="create-drop"/&amp;gt;                                                   &lt;br /&gt;     &amp;lt;/properties&amp;gt;                                                                                                     &lt;br /&gt;   &amp;lt;/persistence-unit&amp;gt;                                                                                                 &lt;br /&gt; &amp;lt;/persistence&amp;gt;                                                                                                        &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note the &lt;tt&gt;jar-file&lt;/tt&gt; element.  The ${...} expansion only works when filtering is turned on in the &lt;tt&gt;testResources&lt;/tt&gt; element of the POM (see above).  By setting the &lt;tt&gt;jar-file&lt;/tt&gt; this way, we're telling the persistence provider to find our entities by searching for persistence annotations in our classes.  From these, the required DDL is generated and the schema  is created.  Alternatively, you could use one or more &lt;tt&gt;class&lt;/tt&gt; elements instead.  This would allow you finer-grained control over which classes and/or packages are involved in a test.  This strategy would probably also lead to multiple &lt;tt&gt;persistence-unit&lt;/tt&gt; elements defined.&lt;br /&gt;&lt;br /&gt;One thing to keep in mind about Hibernate.  There's a known bug in which it won't create a schema before creating tables for your entities, so you're in trouble if you've set the &lt;tt&gt;schema&lt;/tt&gt; attribute in your &lt;tt&gt;@Table&lt;/tt&gt; annotations.  I recommend removing this attribute from your entities anyway:  it's a violation of the &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;DRY principle&lt;/a&gt;.  A better solution is to set the &lt;tt&gt;hibernate.default_schema&lt;/tt&gt; property in your persistence-unit.&lt;br /&gt;&lt;br /&gt;One debugging tip:  you may want to include the following property in your &lt;tt&gt;persistence-unit&lt;/tt&gt;:&lt;pre&gt;&lt;br /&gt;       &amp;lt;property name="hibernate.show_sql" value="${hibernate.show_sql}"/&amp;gt;                                             &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That way, you could add something along these lines in your POM:&lt;pre&gt;&lt;br /&gt; &amp;lt;properties&amp;gt;                                                                                                          &lt;br /&gt;   &amp;lt;hibernate.show_sql&amp;gt;false&amp;lt;/hibernate.show_sql&amp;gt;                                                                &lt;br /&gt; &amp;lt;/properties&amp;gt;                                                                                                         &lt;br /&gt; &amp;lt;profiles&amp;gt;                                                                                                            &lt;br /&gt;   &amp;lt;profile&amp;gt;                                                                                                           &lt;br /&gt;     &amp;lt;id&amp;gt;debug&amp;lt;/id&amp;gt;                                                                                           &lt;br /&gt;     &amp;lt;properties&amp;gt;                                                                                                      &lt;br /&gt;       &amp;lt;hibernate.show_sql&amp;gt;true&amp;lt;/hibernate.show_sql&amp;gt;                                                             &lt;br /&gt;     &amp;lt;/properties&amp;gt;                                                                                                     &lt;br /&gt;   &amp;lt;/profile&amp;gt;                                                                                                          &lt;br /&gt; &amp;lt;/profiles&amp;gt;                                                                                                           &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So when you want to see the SQL produced by your unit tests...&lt;pre&gt;&lt;br /&gt; $ mvn -Pdebug test&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;b&gt;&lt;tt&gt;src/test/resources/import.sql&lt;/tt&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Some apps make use of Hibernate's ability to run some SQL after a schema is created.  If it finds a file named &lt;tt&gt;import.sql&lt;/tt&gt; on the classpath, it'll run the SQL within it.  This can be a problem if, for example, your import.sql is exploiting Oracle PL/SQL commands that wouldn't make any sense to Hypersonic.  Fortunately, you can easily solve the problem by simply putting a different -- possibly empty -- &lt;tt&gt;import.sql&lt;/tt&gt; file beneath &lt;tt&gt;src/test/resources&lt;/tt&gt;, because Hibernate will find that one first in the CLASSPATH.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Aside:&lt;/i&gt;  I experienced some problems with a test-specific import.sql file, strange stack traces generated by Hibernate's SchemaExport class dumped to stdout without actually failing the tests.  I ended up truncating &lt;tt&gt;src/test/resources/import.sql&lt;/tt&gt; completely.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;b&gt;&lt;tt&gt;src/test/java/org/yours/SomeTest.java&lt;/tt&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Speaking of unit tests, here's an obviously-contrived example:&lt;pre&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;package&lt;/span&gt; &lt;span class="jde-java-font-lock-package"&gt;org&lt;/span&gt;.&lt;span class="jde-java-font-lock-package"&gt;yours&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;  &lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="jde-java-font-lock-package"&gt;javax&lt;/span&gt;.&lt;span class="jde-java-font-lock-package"&gt;persistence&lt;/span&gt;.&lt;span class="jde-java-font-lock-number"&gt;*&lt;/span&gt;;&lt;br /&gt;  &lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="jde-java-font-lock-package"&gt;junit&lt;/span&gt;.&lt;span class="jde-java-font-lock-package"&gt;framework&lt;/span&gt;.&lt;span class="jde-java-font-lock-number"&gt;*&lt;/span&gt;;&lt;br /&gt;  &lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="jde-java-font-lock-package"&gt;org&lt;/span&gt;.&lt;span class="jde-java-font-lock-package"&gt;apache&lt;/span&gt;.&lt;span class="jde-java-font-lock-package"&gt;log4j&lt;/span&gt;.&lt;span class="type"&gt;Logger&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;  &lt;span class="jde-java-font-lock-modifier"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;SomeTest&lt;/span&gt; &lt;span class="keyword"&gt;extends&lt;/span&gt; &lt;span class="type"&gt;TestCase&lt;/span&gt;&lt;br /&gt;  {&lt;br /&gt;      &lt;span class="jde-java-font-lock-modifier"&gt;public&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;testService&lt;/span&gt;() &lt;span class="keyword"&gt;throws&lt;/span&gt; &lt;span class="type"&gt;Exception&lt;/span&gt;&lt;br /&gt;      {&lt;br /&gt;          log.info (&lt;span class="string"&gt;"testService"&lt;/span&gt;);&lt;br /&gt;          &lt;span class="type"&gt;EntityManager&lt;/span&gt; &lt;span class="variable-name"&gt;em&lt;/span&gt; = emf.createEntityManager();&lt;br /&gt;          TestData.build (em);&lt;br /&gt;          &lt;span class="type"&gt;ServiceBean&lt;/span&gt; &lt;span class="variable-name"&gt;slsb&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;ServiceBean&lt;/span&gt;();&lt;br /&gt;          slsb.em = em;&lt;br /&gt;          assertTrue (slsb.something());&lt;br /&gt;      }&lt;br /&gt;      &lt;span class="jde-java-font-lock-modifier"&gt;protected&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;setUp&lt;/span&gt;() &lt;span class="keyword"&gt;throws&lt;/span&gt; &lt;span class="type"&gt;Exception&lt;/span&gt;&lt;br /&gt;      {&lt;br /&gt;          log.debug (&lt;span class="string"&gt;"setUp"&lt;/span&gt;);&lt;br /&gt;          emf = Persistence.createEntityManagerFactory (&lt;span class="string"&gt;"hibernate-hsqldb"&lt;/span&gt;);&lt;br /&gt;      }&lt;br /&gt;      &lt;span class="jde-java-font-lock-modifier"&gt;protected&lt;/span&gt; &lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;tearDown&lt;/span&gt;() &lt;span class="keyword"&gt;throws&lt;/span&gt; &lt;span class="type"&gt;Exception&lt;/span&gt;&lt;br /&gt;      {&lt;br /&gt;          log.debug (&lt;span class="string"&gt;"tearDown"&lt;/span&gt;);&lt;br /&gt;          emf.close();&lt;br /&gt;      }&lt;br /&gt;      &lt;span class="jde-java-font-lock-modifier"&gt;private&lt;/span&gt; &lt;span class="type"&gt;Logger&lt;/span&gt; &lt;span class="variable-name"&gt;log&lt;/span&gt; = Logger.getLogger(getClass());&lt;br /&gt;      &lt;span class="jde-java-font-lock-modifier"&gt;private&lt;/span&gt; &lt;span class="type"&gt;EntityManagerFactory&lt;/span&gt; &lt;span class="variable-name"&gt;emf&lt;/span&gt;;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There is a lot of room for artistic freedom within this structure, but the essential point is the create/close of the EntityManagerFactory in the setUp/tearDown methods.  This provides a clean database to each testXXX method.  &lt;br /&gt;&lt;br /&gt;If your transactional requirements are minimal, you may also want to create/close an EntityManager member variable in setUp/tearDown, too.&lt;br /&gt;&lt;br /&gt;If your seed data requirements are complex, you may want to look into something like &lt;a href="http://dbunit.sourceforge.net/"&gt;DBUnit&lt;/a&gt;, but in my experience, it's often easier to construct "builder" objects that can model various situations for your tests.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;b&gt;Transactions&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For a lot of test cases, you can probably safely ignore transactions.  Within one test, letting each of your beans use the same EntityManager without ever beginning or committing its transaction may go a long way toward testing your app sufficiently, especially if all of your beans are simply propagating the transaction anyway.&lt;br /&gt;&lt;br /&gt;But transactions can get icky quickly, especially when you have multiple cooperating session beans with their transaction attribute set to REQUIRES_NEW.  I'm going to show you one way of solving the problem using Java's dynamic proxies, but there's probably &lt;a href="http://blog.interface21.com/main/2006/08/07/using-jpa-in-spring-without-referencing-spring/"&gt;a more elegant solution&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here's something I call a TransactionProxy.  Others might call it "a good argument for AOP".  It assumes your target bean class has an EntityManager member named &lt;tt&gt;em&lt;/tt&gt;.&lt;pre&gt;&lt;br /&gt;&lt;span class="jde-java-font-lock-modifier"&gt;public&lt;/span&gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="type"&gt;TransactionProxy&lt;/span&gt; &lt;span class="keyword"&gt;implements&lt;/span&gt; &lt;span class="type"&gt;InvocationHandler&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;    &lt;span class="jde-java-font-lock-modifier"&gt;private&lt;/span&gt; &lt;span class="type"&gt;EntityManagerFactory&lt;/span&gt; &lt;span class="variable-name"&gt;emf&lt;/span&gt;;&lt;br /&gt;    &lt;span class="jde-java-font-lock-modifier"&gt;private&lt;/span&gt; &lt;span class="type"&gt;Object&lt;/span&gt; &lt;span class="variable-name"&gt;target&lt;/span&gt;;&lt;br /&gt;    &lt;span class="jde-java-font-lock-modifier"&gt;private&lt;/span&gt; &lt;span class="type"&gt;Field&lt;/span&gt; &lt;span class="variable-name"&gt;field&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span class="jde-java-font-lock-modifier"&gt;private&lt;/span&gt; &lt;span class="type"&gt;TransactionProxy&lt;/span&gt; (&lt;span class="type"&gt;Object&lt;/span&gt; &lt;span class="variable-name"&gt;target&lt;/span&gt;, &lt;span class="type"&gt;EntityManagerFactory&lt;/span&gt; &lt;span class="variable-name"&gt;emf&lt;/span&gt;) &lt;br /&gt;    {&lt;br /&gt;        &lt;span class="keyword"&gt;this&lt;/span&gt;.emf = emf;&lt;br /&gt;        &lt;span class="keyword"&gt;this&lt;/span&gt;.target = target;&lt;br /&gt;        &lt;span class="keyword"&gt;try&lt;/span&gt; {&lt;br /&gt;            &lt;span class="keyword"&gt;this&lt;/span&gt;.field = target.getClass().getDeclaredField (&lt;span class="string"&gt;"em"&lt;/span&gt;);&lt;br /&gt;            &lt;span class="keyword"&gt;this&lt;/span&gt;.field.setAccessible (&lt;span class="constant"&gt;true&lt;/span&gt;);&lt;br /&gt;        } &lt;span class="keyword"&gt;catch&lt;/span&gt; (&lt;span class="type"&gt;Exception&lt;/span&gt; &lt;span class="variable-name"&gt;e&lt;/span&gt;) {&lt;br /&gt;            &lt;span class="keyword"&gt;throw&lt;/span&gt; &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;RuntimeException&lt;/span&gt; (e);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="jde-java-font-lock-modifier"&gt;public&lt;/span&gt; &lt;span class="jde-java-font-lock-modifier"&gt;static&lt;/span&gt; &lt;span class="type"&gt;Object&lt;/span&gt; &lt;span class="function-name"&gt;newInstance&lt;/span&gt; (&lt;span class="type"&gt;Object&lt;/span&gt; &lt;span class="variable-name"&gt;target&lt;/span&gt;, &lt;span class="type"&gt;EntityManagerFactory&lt;/span&gt; &lt;span class="variable-name"&gt;emf&lt;/span&gt;) &lt;br /&gt;    {&lt;br /&gt;        &lt;span class="keyword"&gt;return&lt;/span&gt; Proxy.newProxyInstance (target.getClass().getClassLoader(),&lt;br /&gt;                                       target.getClass().getInterfaces(),&lt;br /&gt;                                       &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;TransactionProxy&lt;/span&gt; (target, emf));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="jde-java-font-lock-modifier"&gt;public&lt;/span&gt; &lt;span class="type"&gt;Object&lt;/span&gt; &lt;span class="function-name"&gt;invoke&lt;/span&gt; (&lt;span class="type"&gt;Object&lt;/span&gt; &lt;span class="variable-name"&gt;proxy&lt;/span&gt;, &lt;span class="type"&gt;Method&lt;/span&gt; &lt;span class="variable-name"&gt;m&lt;/span&gt;, &lt;span class="type"&gt;Object&lt;/span&gt;[] &lt;span class="variable-name"&gt;args&lt;/span&gt;)&lt;br /&gt;        &lt;span class="keyword"&gt;throws&lt;/span&gt; &lt;span class="type"&gt;Throwable&lt;/span&gt;&lt;br /&gt;    {&lt;br /&gt;        &lt;span class="type"&gt;EntityManager&lt;/span&gt; &lt;span class="variable-name"&gt;em&lt;/span&gt; = emf.createEntityManager();&lt;br /&gt;        &lt;span class="keyword"&gt;try&lt;/span&gt; {&lt;br /&gt;            field.set (target, em);&lt;br /&gt;            em.getTransaction().begin();&lt;br /&gt;            &lt;span class="type"&gt;Object&lt;/span&gt; &lt;span class="variable-name"&gt;result&lt;/span&gt; = m.invoke (target, args);&lt;br /&gt;            em.getTransaction().commit();&lt;br /&gt;            &lt;span class="keyword"&gt;return&lt;/span&gt; result;&lt;br /&gt;        } &lt;span class="keyword"&gt;catch&lt;/span&gt; (&lt;span class="type"&gt;InvocationTargetException&lt;/span&gt; &lt;span class="variable-name"&gt;e&lt;/span&gt;) {&lt;br /&gt;            em.getTransaction().rollback();&lt;br /&gt;            &lt;span class="keyword"&gt;throw&lt;/span&gt; e.getTargetException();&lt;br /&gt;        } &lt;span class="keyword"&gt;catch&lt;/span&gt; (&lt;span class="type"&gt;Exception&lt;/span&gt; &lt;span class="variable-name"&gt;e&lt;/span&gt;) {&lt;br /&gt;            &lt;span class="keyword"&gt;throw&lt;/span&gt; &lt;span class="keyword"&gt;new&lt;/span&gt; &lt;span class="type"&gt;RuntimeException&lt;/span&gt; (e);&lt;br /&gt;        } &lt;span class="keyword"&gt;finally&lt;/span&gt; {&lt;br /&gt;            em.close();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The idea is that you create it with an instance of say, a stateless session bean (SLSB) and an EntityManagerFactory, and subsequent invocations on the proxy will wrap calls to the real bean inside a transaction obtained from the EntityManagerFactory.  For example:&lt;pre&gt;&lt;br /&gt;    Service service = (Service) TransactionProxy.newInstance (new ServiceBean(), emf);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This allows you to combine your beans in any number of transactional contexts.  But it's not optimal since it's not actually using the &lt;tt&gt;@TransactionAttribute&lt;/tt&gt; annotation of the classes under test.  Hopefully, that's what the more elegant solution mentioned above is doing.&lt;br /&gt;&lt;br /&gt;I'll update this tutorial after I've confirmed that.  Until then, happy hacking!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-745462503232161149?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/745462503232161149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=745462503232161149' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/745462503232161149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/745462503232161149'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/03/testing-ejb3-jpa-with-maven2.html' title='Testing EJB3 (JPA) with Maven2'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-916634486236272194</id><published>2007-03-04T06:57:00.000-08:00</published><updated>2007-03-04T11:14:03.223-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>From Gentoo+Postfix+Courier to Debian+Exim+Dovecot</title><content type='html'>Ok, let's say -- hypothetically, of course -- you chose Gentoo for your home email server a few years ago.  You found one of Gentoo's great docs on setting up Postfix, Courier and SpamAssassin.  You accepted all the reasonable default options during the install, and things seemed to work great.&lt;br /&gt;&lt;br /&gt;Over time, you began to take the little email server for granted.  Oh sure, you'd occasionally login and "emerge" some security updates, but the dang thing just worked.  Why bother it?&lt;br /&gt;&lt;br /&gt;Of course, &lt;a href="http://en.wikipedia.org/wiki/Entropy"&gt;entropy&lt;/a&gt; is a cold bitch, and "for granted taking" turned to neglect.  After a while, you began to forget all the arcane portage commands, having to use rpms at work and happily experimenting with Ubuntu's adopted dpkg on your desktop.  In the beginning, spam was under control, but it's a constant battle, and forgetting your distro's packaging commands won't exactly help you in that fight.&lt;br /&gt;&lt;br /&gt;And then terror struck:  the Gentoo maintainers decided to release a portage update that was incompatible with the version running on your little email server.  And at that precise version did your server remain for the rest of its life.  The prospect of recompiling your entire system was just too much to bear.  You decided it was a good time to look for another distro, maybe in a couple weeks or so, when you're not so busy...&lt;br /&gt;&lt;br /&gt;Well, a couple of weeks turned into a couple of years.  And over that time, you came to realize the benefits of the &lt;a href="http://www.qmail.org/man/man5/maildir.html"&gt;maildir&lt;/a&gt; storage format over the traditional &lt;a href="http://www.qmail.org/man/man5/mbox.html"&gt;mbox&lt;/a&gt; one, the latter being one of those reasonable defaults mentioned above.  More importantly, you began to appreciate apt/dpkg as "portage without all the compiling", and you became aware of Debian's reputation for server stability.&lt;br /&gt;&lt;br /&gt;One day when the spam/ham ratio was particularly high, you decided the time had finally come to switch the ol' home server from Gentoo to Debian.  You decided you'd build a new box, transfer the family email over and convert it to the Maildir format.  Just because you're a total dumbass-glutton-for-punishment, you also decided to switch MTA's (Postfix to Exim) and IMAP daemons (Courier to Dovecot).  "Why the hell not?" you wondered.&lt;br /&gt;&lt;br /&gt;Um... I'm not sure I can keep up the hypothetical perspective any longer.  I'm thinking you were seeing right through it, anyway.  Sorry.&lt;br /&gt;&lt;br /&gt;So you -- I mean, I -- found a cheap Dell P3 on Ebay for $33+s/h, installed Debian Etch, and then fired up aptitude...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Exim&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;By default, Debian installs &lt;tt&gt;exim4-daemon-light&lt;/tt&gt;, which is fine for most purposes, but &lt;tt&gt;exim4-daemon-heavy&lt;/tt&gt; is required for integration with most spam/virus detection packages and, despite its name, it's really not that heavy.&lt;pre&gt;&lt;br /&gt;  # aptitude install exim4-daemon-heavy&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Configuring Exim is easy enough, though it took me a while to grok the split vs non-split formats.  I wasted a lot of time googling for docs/howtos:  I wish I had just started with /usr/share/doc/exim4/README.Debian.gz like the maintainers suggest.  I don't know why Google results seem so much more glamorous than the packaged docs.&lt;br /&gt;&lt;br /&gt;I went through the normal routine:&lt;pre&gt;&lt;br /&gt;  # dpkg-reconfigure exim4-config&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I chose non-split, mail sent by smarthost, received via SMTP, and accepted mail for &lt;tt&gt;*.internal.domain;internal.domain;external.domain&lt;/tt&gt;.  Your -- my? -- domain names will differ, of course.  I chose to relay mail for my local network, set the name of the outgoing smarthost, hid the local mail names making only my external domain visible, and made sure to set Maildir as my delivery format. &lt;br /&gt;&lt;br /&gt;I then created user accounts for all my family members, and sent them some test mail from a box configured to relay outgoing mail to my new server.  In addition to testing the above config, it also caused the Maildir structures to be created in the users' home directories.  Easy-peazy-lemon-squeazy.&lt;br /&gt;&lt;br /&gt;Something else cool:  I can add entries to /etc/aliases and exim will pick them up immediately without restarting or rerunning newaliases or some such.  I do that for extended relatives so I don't have to remember their real email addresses, only their alias, e.g. unclebob@crossleys.org.  Let me know if you want one.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Dovecot&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;So delivery is working.  Now I need an IMAP server for retrieval.&lt;pre&gt;&lt;br /&gt;  # aptitude install dovecot-imapd&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;Configuring Dovecot was simple:   I just needed to enable plaintext logins (I'm not exposed to the bad ol' Internet) and the &lt;tt&gt;imap&lt;/tt&gt; protocol.  Here's a diff of my changes to /etc/dovecot/dovecot.conf:&lt;pre&gt;&lt;br /&gt;  21c21&lt;br /&gt;  &amp;lt; protocols =&lt;br /&gt;  ---&lt;br /&gt;  &amp;gt; protocols = imap&lt;br /&gt;  46c46&lt;br /&gt;  &amp;lt; #disable_plaintext_auth = yes&lt;br /&gt;  ---&lt;br /&gt;  &amp;gt; disable_plaintext_auth = no&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After saving those changes, a restart is required:&lt;pre&gt;&lt;br /&gt;  # /etc/init.d/dovecot restart&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;imapsync&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Now comes the hard part:  how to move my email from the old server to the new one?  After googling a bit, I came across a utility called &lt;a href="http://batleth.sapienti-sat.org/projects/mb2md/"&gt;&lt;tt&gt;mb2md&lt;/tt&gt;&lt;/a&gt;, but its interface is a little weird, seemed to expect files to be in certain places, and seemed more geared toward a single user rather than a group.  Somewhat discouraged, I restarted my search, and came across &lt;a href="http://directory.fsf.org/imapsync.html"&gt;&lt;tt&gt;imapsync&lt;/tt&gt;&lt;/a&gt;.  This discovery simplified my approach:  instead of copying mbox files (both spooled and saved) from the old server to the new and *then* converting them, I could simply encapsulate the format differences behind the IMAP interface, i.e. imapsync allows me to copy email from one IMAP server to another, irrespective of the underlying formats of each!  Neat-Oh!&lt;br /&gt;&lt;br /&gt;Installing imapsync is easy:&lt;pre&gt;&lt;br /&gt;  # aptitude install imapsync&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using it takes some practice.  Fortunately it provides a --dry option for just that purpose.  Here's an example of my Courier-to-Dovecot imapsync incantations for a particular user.  I needed to invoke it twice:  once for the main inbox, and again for the categories of saved mail I kept beneath the ~/mail directory on the old server:&lt;pre&gt;&lt;br /&gt;  imapsync --host1 oldbox --user1 jim --password1 oldpass \&lt;br /&gt;           --host2 newbox --user2 jim --password2 newpass \&lt;br /&gt;           --folder INBOX&lt;br /&gt;  imapsync --host1 oldbox --user1 jim --password1 oldpass \&lt;br /&gt;           --host2 newbox --user2 jim --password2 newpass \&lt;br /&gt;           --include "^mail/.+"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The nice thing about imapsync is that it's a true sync.  You can run it multiple times and it only copies over what's changed.  This allowed me to be pretty sloppy about when I actually updated my firewall to send SMTP requests to the new server.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ClamAV&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Ok, so we've (we, as in me, not you) accomplished a lot.  The only thing remaining is to reject viruses and spam.  &lt;a href="http://www.clamav.net/"&gt;ClamAV&lt;/a&gt; will handle the viruses:&lt;pre&gt;&lt;br /&gt;  # aptitude install clamav-daemon&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To integrate with Exim, two changes to &lt;tt&gt;/etc/exim4/exim4.conf.template&lt;/tt&gt; were required.  I set &lt;tt&gt;av_scanner&lt;/tt&gt; to this:&lt;pre&gt;&lt;br /&gt;  av_scanner = clamd:/var/run/clamav/clamd.ctl&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And uncommented these lines (presumably put there by &lt;tt&gt;exim4-daemon-heavy&lt;/tt&gt;):&lt;pre&gt;&lt;br /&gt;  deny&lt;br /&gt;    malware = *&lt;br /&gt;    message = This message was detected as possible malware ($malware_name).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After running &lt;tt&gt;update-exim4.conf&lt;/tt&gt;, restarting exim and sending a fake virus (as described &lt;a href="http://www.debian-administration.org/articles/141"&gt;here&lt;/a&gt;), I noticed some "lstat() failed" errors in the mainlog.  Smells like a perms problem.  To fix that, I did this:&lt;pre&gt;&lt;br /&gt;  # adduser clamav Debian-exim&lt;br /&gt;  # /etc/init.d/clamav-daemon restart&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;SpamAssassin, er... greylistd&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;At this point in our story, I expected to tell you how easy it was to integrate SpamAssassin with our (my) new setup, but frankly I haven't done it yet.&lt;br /&gt;&lt;br /&gt;Instead, I discovered &lt;a href="http://en.wikipedia.org/wiki/Greylisting"&gt;greylisting&lt;/a&gt;.  This has eliminated 95% of my spam.  For this reason, I'm holding off on SpamAssassin integration for the time being.&lt;br /&gt;&lt;br /&gt;The &lt;tt&gt;greylistd&lt;/tt&gt; package provides a convenient script that completely idiot-proofs exim4 integration:&lt;pre&gt;&lt;br /&gt;  # aptitude install greylistd&lt;br /&gt;  # greylistd-setup-exim4 add&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Simple, huh?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;That's it.  We're done.  Whew!&lt;br /&gt;&lt;br /&gt;Now I have a stable home email server that I can actually update and try to secure.  Sweet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-916634486236272194?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/916634486236272194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=916634486236272194' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/916634486236272194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/916634486236272194'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/03/from-gentoopostfixcourier-to.html' title='From Gentoo+Postfix+Courier to Debian+Exim+Dovecot'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-3144013972758618742</id><published>2007-02-18T14:40:00.000-08:00</published><updated>2007-02-18T12:40:23.816-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>Hating Maven less than Ant</title><content type='html'>I've been writing Java code since 1996 and struggling with build tools at least as long.  &lt;a href="http://ant.apache.org/"&gt;Ant&lt;/a&gt; is the most comprehensive and &lt;a href="http://maven.apache.org/"&gt;Maven&lt;/a&gt; is the most ambitious.  I hate both of them, mostly because they each require me to describe my project's build requirements using XML, which &lt;a href="http://weblog.jamisbuck.org/2004/6/10/jelly-scripting-for-the-soulless"&gt;should never be used as a programming language&lt;/a&gt;, though often it's used as a configuration file format, but because &lt;a href="http://weblogs.java.net/blog/arnold/archive/2003/06/duncan_davidson.html"&gt;all configuration files ultimately will evolve into programming languages&lt;/a&gt;... I digress.&lt;br /&gt;&lt;br /&gt;For different reasons, Ant and Maven are both terrific and terrible at once.  Ant allows you to describe your build however you please, but you must effectively do it from scratch for each project.  Maven encourages many best practices by providing a lot of build features out-of-the-box, adopting a sort of "convention over configuration" philosophy popularized by the Ruby on Rails project, among others.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://research.unc.edu/endeavors/spr2005/images/turtle.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 200px;" src="http://research.unc.edu/endeavors/spr2005/images/turtle.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;So for my money, Maven is the logical choice for a typical Java project.  Unfortunately, few Java projects are typical, especially few enterprise Java projects.  And for those projects, Maven can be frustrating because, like a child or a turtle, it's difficult to make it do something it doesn't want to do.&lt;br /&gt;&lt;br /&gt;For example, let's say I'm debugging a build and I want to echo the value of a property to the console during a build.  With Ant, I could add a target to &lt;tt&gt;build.xml&lt;/tt&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;project&amp;gt;&lt;br /&gt;  &amp;lt;target name="debug"&amp;gt;&lt;br /&gt;    &amp;lt;echo&amp;gt;The value is ${some.property}&amp;lt;/echo&amp;gt;&lt;br /&gt;  &amp;lt;/target&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;With Maven 1, I can invoke Ant tasks within a goal defined in &lt;tt&gt;maven.xml&lt;/tt&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;project&amp;gt;&lt;br /&gt;  &amp;lt;goal name="debug"&amp;gt;&lt;br /&gt;    &amp;lt;echo&amp;gt;The value is ${some.property}&amp;lt;/echo&amp;gt;&lt;br /&gt;  &amp;lt;/goal&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Maven 1 provides easy access to Ant tasks, as you can see.  Unfortunately, doing anything non-trivial with Maven 1 involves Jelly, which is pure "executable XML hell".  Maven 2 did away with Jelly, but...&lt;br /&gt;&lt;br /&gt;With Maven 2, it's bewilderingly complicated to echo a simple property to the console.  You must include the "antrun" &lt;span style="font-style: italic;"&gt;plugin&lt;/span&gt; in your &lt;tt&gt;pom.xml&lt;/tt&gt;, &lt;span style="font-style: italic;"&gt;configure&lt;/span&gt; an &lt;span style="font-style: italic;"&gt;execution&lt;/span&gt; to run Ant's echo task, and bind that execution to a particular &lt;span style="font-style: italic;"&gt;phase&lt;/span&gt; of your build (but see comments for other options).  Ready?  Here we go:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;project&amp;gt;&lt;br /&gt;  &amp;lt;build&amp;gt;&lt;br /&gt;    &amp;lt;plugins&amp;gt;&lt;br /&gt;      &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;maven-antrun-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;executions&amp;gt;&lt;br /&gt;          &amp;lt;execution&amp;gt;&lt;br /&gt;            &amp;lt;id&amp;gt;debug&amp;lt;/id&amp;gt;&lt;br /&gt;            &amp;lt;phase&amp;gt;validate&amp;lt;/phase&amp;gt;&lt;br /&gt;            &amp;lt;configuration&amp;gt;&lt;br /&gt;              &amp;lt;tasks&amp;gt;&lt;br /&gt;                &amp;lt;echo&amp;gt;The value is ${some.property}&amp;lt;/echo&amp;gt;&lt;br /&gt;              &amp;lt;/tasks&amp;gt;&lt;br /&gt;            &amp;lt;/configuration&amp;gt;&lt;br /&gt;            &amp;lt;goals&amp;gt;&lt;br /&gt;              &amp;lt;goal&amp;gt;run&amp;lt;/goal&amp;gt;&lt;br /&gt;            &amp;lt;/goals&amp;gt;&lt;br /&gt;          &amp;lt;/execution&amp;gt;&lt;br /&gt;        &amp;lt;/executions&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;    &amp;lt;/plugins&amp;gt;&lt;br /&gt;  &amp;lt;/build&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that our choice of the "validate" phase is completely arbitrary, but in order for our task to execute, it must be bound to &lt;span style="font-style: italic;"&gt;some&lt;/span&gt; phase.  Unlike our Ant and Maven 1 examples above, Maven 2 provides no way to invoke a single task &lt;span style="font-weight: bold;"&gt;without&lt;/span&gt; invoking all other tasks bound to the same phase along with all those before it.  Maven 2 introduced the concept of &lt;span style="font-style: italic;"&gt;profiles&lt;/span&gt; to address this perceived limitation, but I'll save that discussion for a future blog post.&lt;br /&gt;&lt;br /&gt;Clearly, the Maven designers have violated a fundamental tenet:  &lt;span style="font-style: italic;"&gt;"the solution cannot be more abstract than the problem!"&lt;/span&gt;  In trying to define and encourage best practices, Maven unapologetically sacrifices simplicity and flexibility, arguably two of the most important qualities of any software build tool, even for those projects exhibiting all accepted best practices.&lt;br /&gt;&lt;br /&gt;Somewhat surprisingly, I generally still think it's worthwhile to convert your Ant projects to Maven, no matter how difficult.  Maven's pain is concentrated, sort of like filling a cavity early in the project, resulting in a relatively pain-free existence once your project leaves the dentist's office.  &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.premierdentalcenter.com/images/bna/dentures_implants/p-dentures11.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px;" src="http://www.premierdentalcenter.com/images/bna/dentures_implants/p-dentures11.jpg" border="0" alt="" /&gt;&lt;/a&gt;Ant is more like a minor toothache annoying you for the entire duration of your project, and then spreading to the other projects in your mouth as each begins its life with a copy of some other's &lt;tt&gt;build.xml&lt;/tt&gt; file.  Yes, of course it's simpler to do simple things with Ant, but adding enough of those simple things to projects over time can (and will, in my experience) lead to a pretty darn complex, brittle, and incomprehensible build.&lt;br /&gt;&lt;br /&gt;In the long run, I think you'll end up hating Maven less than Ant, too.  But if you insist on using Ant, use &lt;a href="http://incubator.apache.org/projects/ivy.html"&gt;Ivy&lt;/a&gt; to manage your dependencies.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UPDATED&lt;/span&gt; Maven 2 example, removing unnecessary &amp;lt;property&amp;gt; element to reflect Eric's comment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-3144013972758618742?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/3144013972758618742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=3144013972758618742' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/3144013972758618742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/3144013972758618742'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/02/hating-maven-less-than-ant.html' title='Hating Maven less than Ant'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-1634149938757248073</id><published>2007-02-17T16:32:00.000-08:00</published><updated>2007-02-17T13:32:04.396-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='meizu'/><title type='text'>Linux Podcasts</title><content type='html'>Since I got &lt;a href="http://escx.blogspot.com/2007/01/my-new-meizu.html"&gt;my new Meizu&lt;/a&gt;, I've been listening to a lot of podcasts, especially those dedicated to Linux.  There are &lt;a href="http://www.thelinuxlink.net/"&gt;a surprising number of them out there&lt;/a&gt;, and I've listened to quite a few, but I wanted to share my favorites.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://feeds.feedburner.com/linuxreality-ogg"&gt;Linux Reality&lt;/a&gt; - Chess Griffin's generous, informative and practical podcast, presumably for newbies but really for anyone interested in Linux&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.lugradio.org/episodes.ogg.rss"&gt;LugRadio&lt;/a&gt; - can't understand them half the time, but usually very funny&lt;/li&gt;&lt;li&gt;&lt;a href="http://feeds.feedburner.com/TheJakAttackOGG"&gt;JaK Attack&lt;/a&gt; - kind of hit and miss, but nice to hear a female voice&lt;/li&gt;&lt;li&gt;&lt;a href="http://rss.gigavox.com/series/programming.xml"&gt;ITConversations Programming series&lt;/a&gt; - not active any longer, and focused on software development rather than linux, but great interviews with industry heavyweights like Paul Graham, Kent Beck, etc&lt;/li&gt;&lt;li&gt;&lt;a href="http://feeds.feedburner.com/TheLinuxActionShowOGG"&gt;Linux Action Show&lt;/a&gt; - often silly and self-conscious, but not boring and usually informative&lt;/li&gt;&lt;li&gt;&lt;a href="http://feeds.runyourownserver.org/runyourownserver"&gt;Run Your Own Server&lt;/a&gt; - smart cats, more geared toward BSD than Linux, though&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-1634149938757248073?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/1634149938757248073/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=1634149938757248073' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1634149938757248073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1634149938757248073'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/02/linux-podcasts.html' title='Linux Podcasts'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-1408189843552061218</id><published>2007-02-03T09:08:00.000-08:00</published><updated>2007-02-03T10:18:21.116-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Charter DNS still sucks</title><content type='html'>I set up &lt;tt&gt;dnsmasq&lt;/tt&gt; on the Debian box &lt;a href="http://escx.blogspot.com/2007/01/charter-dns-sucks.html"&gt;I had been running &lt;tt&gt;bind9&lt;/tt&gt; on&lt;/a&gt;.  It was infinitely easier to configure than a "real" DNS server -- just edit /etc/hosts -- and it perfectly suited the host-naming requirements for my admittedly tiny home network.&lt;br /&gt;&lt;br /&gt;As with all things computer, of course, I still had hurdles to overcome, and they all involved difficulties with the commercial operating systems in my network, specifically Win XP and Mac OS X.  All the Linux boxes were blissfully easy to setup and predictably functional and even adaptable to the weirdness required by the commercial ones.&lt;br /&gt;&lt;br /&gt;As far as the Mac goes, it wouldn't resolve my local domain name, apparently because it ended in &lt;tt&gt;.local&lt;/tt&gt;.  I used that suffix because I read somewhere that it was an illegal TLD (top level domain, e.g. .com, .org, .edu, etc).  Apparently, Mac's lookupd process eats those names because of the file &lt;tt&gt;/etc/resolver/local&lt;/tt&gt;.  I could fix the problem by adding a file for my domain to /etc/resolver, but I decided it was easier to use the suffix &lt;tt&gt;.home&lt;/tt&gt; instead.  I'm pretty sure it's an illegal TLD, too.&lt;br /&gt;&lt;br /&gt;The WinXP box was sad for another reason.  I had initially tried configuring &lt;tt&gt;dnsmasq&lt;/tt&gt; as a DHCP daemon, which was cool, because then I could control all my static IP's in &lt;tt&gt;/etc/dnsmasq.conf&lt;/tt&gt; instead of having to configure each host on the network.  But alas, that broke my work VPN client on the WinXP box.  I didn't spend any time trying to figure out why, though.&lt;br /&gt;&lt;br /&gt;So now my router still acts as the DHCP server, even though the work laptop is the only thing that uses it.  The other hosts are configured with static IP's that are replicated in the /etc/hosts file on the host running dnsmasq.  Suboptimal, but functional.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-1408189843552061218?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/1408189843552061218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=1408189843552061218' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1408189843552061218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1408189843552061218'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/01/charter-dns-still-sucks.html' title='Charter DNS still sucks'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-8510679597572407847</id><published>2007-01-27T12:50:00.000-08:00</published><updated>2007-01-29T07:25:23.176-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='beauty'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Musical Moments</title><content type='html'>When I was in high school, a friend described me as "the type of person who listens to guitar solos".  I didn't realize some (most?) people don't.&lt;br /&gt;&lt;br /&gt;I don't really hear lyrics when I listen to music.  I'm aware of the words being sung, of course, but I rarely have any emotional response to them, other than the &lt;span style="font-style: italic;"&gt;way&lt;/span&gt; they're being sung.  For example, I only have a vague notion of &lt;span style="font-style: italic;"&gt;what&lt;/span&gt; Joe Strummer or Sly Stone sing, but I love &lt;span style="font-style: italic;"&gt;how&lt;/span&gt; they sing it.&lt;br /&gt;&lt;br /&gt;I'm not into lyrics or "story songs", but I dig other aspects of music:  a groovy bass line, a soulful vocalist, tight harmonies, unusual instrumentation, exceptional tone, original technique, surprising syncopation, or maybe an incredibly poignant note in a solo -- one that fits the context of the song perfectly!  In short, I'm more inspired by the &lt;span style="font-style: italic;"&gt;delivery&lt;/span&gt; of the message than the message itself.&lt;br /&gt;&lt;br /&gt;Also, rather than entire songs, there are specific moments in certain songs that always excite me; always send multiple chills down my spine.  So instead of favorite songs, I have favorite "musical moments".  Here are a few...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"Machine Gun"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Jimi Hendrix&lt;/span&gt; -- It's difficult for me to describe my relationship to this song.  So much power from three people.  Amazing vocals, both Jimi and Buddy.  Forceful, soulful drumming.  It's the tone of the guitar I love the most, of course.  It's angry.  At (3:11) singer and guitar are one.  At (4:00) the player finally relents to the guitar's obvious desire and lets it scream.  One angry, long sustained scream to introduce a solo.  Not overplaying.  Just one fantastic note.  Gives me chills every time.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"Gimme Shelter"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;The Rolling Stones&lt;span style="font-style: italic;"&gt; --&lt;/span&gt;&lt;/span&gt;&lt;span&gt; The intro always blows me away; dark and gently descending, culminating in that note at (0:50) Richards holds on the verge of feeding back with beautiful vibrato, one of those notes on a hollow-body that resonates to the point of shaking furniture if left unchecked.&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;"Hey"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Pixies&lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt; -- &lt;/span&gt;&lt;/span&gt;&lt;span&gt;I love how he sings &lt;span style="font-style: italic;"&gt;"If you go, I will surely die"&lt;/span&gt; at &lt;/span&gt;&lt;span&gt;(0:36).  I also love the guitars throughout, both the bass/rhythm interplay and the lead.  The chorus is beautifully unusual, the word "chained" alternately and unapologetically broken.&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;The entire second verse is a masterpiece, &lt;span style="font-style: italic;"&gt;"Said the man to the lady... the sound that the mother makes when the baby breaks"&lt;/span&gt;.  I even like the lyrics on that one, but loved the way he sings that last line long before I realized what he was saying!  ;-)&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"There There"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Radiohead&lt;/span&gt; -- These guys are like musical crack.  And this song is exceptionally potent.  One of my favorite bike-riding tunes.  I always get chills when the background vocals come in at (2:01).  I have no idea what they're singing.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"My Funny Valentine"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Miles Davis&lt;/span&gt; -- When he flattens that note at (1:06) , he turns the whole song much darker and mysterious.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"Georgia on My Mind"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Jerry Reed -- &lt;/span&gt;&lt;span&gt;A study in timing.  Both the intro and the outro are amazing, especially the harmonics at the very end.  I love when he says "Yeah" at (2:21), himself seemingly amazed at what his own fingers are doing.  He indeed has amazing fingers, but not all of his recordings reflect this.  Fortunately, every track on "Explores Guitar Country", beginning with this one, does.  Did I mention how amazing he is?  ;-)&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"Advance Romance"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Frank Zappa -- &lt;/span&gt;&lt;span&gt;Ok, I admit the opening lyric to this song initially hooked me.  How can you not pay attention to a song that opens with &lt;span style="font-style: italic;"&gt;"No more credit at the liquor store"&lt;/span&gt;? Wow!  Love the drumming and slide guitar on this one, too.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"Bohemian Rhapsody"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Queen -- &lt;/span&gt;&lt;span&gt;Everyone loves this song, and for good reason.  And what's the image in your head at (4:08)?  The "Pacer scene" in the movie, &lt;span style="font-style: italic;"&gt;Wayne's World&lt;/span&gt;, right?  :-)&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"I'll Take You There"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Staple Singers&lt;/span&gt; (1:15) The solo begins with the piano trills and ends with the forever-familiar bass breakdown, the rim shots, kick drum accents and Mavis' vamping throughout.  Love that groove.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"Statesboro Blues"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Allman Brothers Band&lt;/span&gt; -- For my money, Duane achieved perfection on this recording.  His slide has such a vocal quality, as if it's translating Greg's lyrics for all the &lt;span style="font-style: italic;"&gt;guitars&lt;/span&gt; in the audience.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;"Reelin' in the Years"&lt;/span&gt; -- &lt;span style="font-style: italic;"&gt;Steely Dan -- &lt;/span&gt;&lt;span&gt;M&lt;/span&gt;&lt;span&gt;ost people have no clue who &lt;/span&gt;&lt;span&gt;&lt;a href="http://www.elliott-randall.com/"&gt;Elliot Randall&lt;/a&gt; &lt;/span&gt;&lt;span&gt;is.  He's known mainly for one thing:  he's the guitarist on this song.  It was recorded in one take.  The lick he plays around (4:02) in the outro is one of my all-time favorites in all of guitar-lick-dom.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span&gt;Those are but a few of many.  What are yours?&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-8510679597572407847?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/8510679597572407847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=8510679597572407847' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8510679597572407847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8510679597572407847'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/01/musical-moments.html' title='Musical Moments'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-5482245145706036248</id><published>2007-01-19T00:11:00.000-08:00</published><updated>2007-01-21T10:19:30.650-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Learning (to hear) an Instrument</title><content type='html'>I'm a pretty good guitar player.  Been playing since I was maybe 12 or so, and I'm self-taught, which is why it took me so long to get any good.  A great teacher can make you better faster.&lt;br /&gt;&lt;br /&gt;But there aren't a lot of great teachers.  If you really want to learn an instrument, you're going to have to teach yourself, and to do that you need to do two things:&lt;ul&gt;&lt;li&gt;Tap your foot&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Listen&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Always tap your foot when you play anything.  Always, always, always.  If not your foot, some other limb or digit.  You must develop your rhythm.  Timing is everything.  Beginning musicians neglect rhythm/meter/tempo.  They're too focused on pitch/melody/harmony.  The tune means nothing without the rhythm.  Don't tell me you can play something unless I can dance to it and/or accompany you, and your sense of rhythm is what enables me to do that.  Tap your foot when you practice, always.&lt;br /&gt;&lt;br /&gt;The Internet has produced two great tools for budding instrumentalists:  tablature and YouTube.  But without a good ear, they're dangerous, because quite frankly, most tablature available on the Internet is wrong and most YouTube performers suck.  There are, of course, plenty of valuable exceptions, but even bad tablature and sucky performers can teach you something...&lt;br /&gt;&lt;br /&gt;I'm not telling you anything new, of course.  You already know the tabs/videos are wrong, because they don't &lt;span style="font-weight: bold;"&gt;sound&lt;/span&gt; quite right.  Recognizing this is a big step!  Unfortunately, your mind can trick you into thinking what you're playing is correct.  If you practice it enough, you'll begin to think it's right, simply because through practice you've become better at playing it, even though it's not exactly what the artist you're trying to emulate is playing.&lt;br /&gt;&lt;br /&gt;You would know this if you simply listened or -- even better -- played along with the song you're trying to learn.  (which also improves your rhythm)&lt;br /&gt;&lt;br /&gt;As your ear develops, you'll start to hear subtleties you missed before.  There are many ways to play an F# chord on the guitar, for example.  Any chord played around the 1st fret will have a completely different tonal quality around the 7th fret, even though both are the same chord.  An open string sounds much different than a fretted one, even if it's the same note. &lt;br /&gt;&lt;br /&gt;When I first started playing, I became very frustrated trying to play rock/blues solos, until I discovered string bending.  My ear was telling me Hendrix was playing an E.  I could hear the note, and I could play it:  1st string, 12th fret.  Same note, exactly.  But his sounded &lt;span style="font-weight: bold;"&gt;so much better&lt;/span&gt; than mine.  Why?  He was fretting the 1st string at the 10th fret (D), and then bending it up to the pitch it would be at the 12th fret (E).  Or he'd bend the 2nd string from the 15th fret (D) up to (E), simultaneously playing the (E) at the 12th fret on the 1st string.  Same note I was playing, but sounded &lt;span style="font-weight: bold;"&gt;WAY&lt;/span&gt; cooler.&lt;br /&gt;&lt;br /&gt;Always trust your ear.  I can remember struggling to "pick out" George Harrison's "Here Comes the Sun".  When I thought I had a reasonable approximation -- though I knew it was wrong -- I went looking for some tab and experienced a huge "Doh! moment" when I discovered he was using a capo!  That made it MUCH easier!&lt;br /&gt;&lt;br /&gt;But even more important, because I had tried to play the song myself first BY EAR, and then by tab, the subtle effects a capo can have on a guitar's tone creeped into my subconscious.  Over time, it became easier for me to recognize when a guitarist is using a capo, or bending a string, or pulling-off, or hammering-on, etc... without having to watch a video or read tablature.&lt;br /&gt;&lt;br /&gt;I could &lt;span style="font-weight: bold;"&gt;hear&lt;/span&gt; it, and eventually, I could &lt;span style="font-weight: bold;"&gt;play&lt;/span&gt; it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-5482245145706036248?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/5482245145706036248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=5482245145706036248' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/5482245145706036248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/5482245145706036248'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/01/learning-to-hear-instrument.html' title='Learning (to hear) an Instrument'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-4682604785725231533</id><published>2007-01-14T11:32:00.000-08:00</published><updated>2007-01-14T12:37:36.812-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Charter DNS sucks</title><content type='html'>For the most part, I've been happy with my Charter cable service, with two glaring exceptions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;They won't provide the NFL Network&lt;/li&gt;&lt;li&gt;Their DNS servers are unreliable, often down, and slow when they're up&lt;/li&gt;&lt;/ul&gt;I can't do much about the first issue -- other than relocate -- because the cable industry is rife with un-American anti-competition policies, but I stumbled across a VERY EASY, i.e. lazy, solution to the second:  install a "caching forwarding nameserver".  The performance improvement is ridiculous.&lt;br /&gt;&lt;br /&gt;It so happens that Debian's default configuration of DNS (bind9) is to behave just that way:  &lt;span style="font-style: italic;"&gt;cache-ily&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;foward-ily&lt;/span&gt;.  I realize there are other lighter-weight solutions, e.g. &lt;tt&gt;nscd&lt;/tt&gt;, &lt;tt&gt;dnsmasq&lt;/tt&gt;, or &lt;tt&gt;pdnsd&lt;/tt&gt;, but it was just so bone-stupid-simple to install Debian on a spare PC (or VM, of course) and check the "DNS Server" option on the installation's package selection screen.  And what better use for the $33 PC I just bought off ebay, right?  Incidentally, the shipping cost was only 15 cents less than the bid price, which technically makes it a $65.85 PC, but hey, whenever server TCO is less than $100, I'm happy.&lt;br /&gt;&lt;br /&gt;Of course, I now must configure the other computers in my home network to use my new nameserver.  For my Ubuntu laptop, I edit &lt;tt&gt;/etc/dhcp3/dhclient.conf&lt;/tt&gt;, and add (uncomment) this line: &lt;pre&gt;&lt;br /&gt;  prepend domain-name-servers THE_IP_ADDRESS&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course, you'll substitute THE_IP_ADDRESS with the real IP address of your $65.85 PC.  This results in THE_IP_ADDRESS of my nameserver showing up ahead of Charter's nameservers in &lt;tt&gt;/etc/resolv.conf&lt;/tt&gt; when the laptop obtains its DHCP address at boot.  Alternatively, I could use my $65.85 PC as my DHCP server so that no per-client config is necessary at all.  If only I wasn't so lazy...&lt;br /&gt;&lt;br /&gt;Did I mention how ridiculously fast "the Internet" seems now?  All the perceived slowness was due to Charter's name servers, which suck, btw.  I mentioned that, right?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-4682604785725231533?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/4682604785725231533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=4682604785725231533' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/4682604785725231533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/4682604785725231533'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/01/charter-dns-sucks.html' title='Charter DNS sucks'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-1031011253753716663</id><published>2007-01-13T08:17:00.000-08:00</published><updated>2007-01-13T09:26:13.221-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Maxtor OneTouch, HFS+ to ext3</title><content type='html'>I bought a Maxtor OneTouch 300GB external USB/Firewire drive a couple of years ago, mostly to save my iMovie experiments on my wife's Mac, but also to serve as a general backup device for my other Linux boxen.  Because I had no box other than the Mac with a firewire port, I hooked it up there and -- due to the normal fear and ignorance computer manufacturers have come to expect from their users -- I ran the supplied installation CD.&lt;br /&gt;&lt;br /&gt;That turned out to be a mistake.&lt;br /&gt;&lt;br /&gt;The Mac formatted the file system on the drive as HFS+, of course.  This was fine, as long as the drive was connected to the Mac.  But when I plugged it into a Linux box, it was none-too-happy with HFS+ and would only mount the thing read-only.&lt;br /&gt;&lt;br /&gt;So today, I finally got around to reformatting the drive as ext3.  This turned out to be way easier than I thought it would.  Actually, the hardest part was finding places to temporarily store the files that were on there during the reformatting.  Once that was done, I simply plugged the thing into my Ubuntu Edgy laptop, fired up gparted, deleted the old partitions -- for some reason, there were three of them -- and then I created a single ext3 partition.&lt;br /&gt;&lt;br /&gt;Using, I assume, some combination of "udev/hotplug/hal/gnome" magic, Ubuntu recognized the drive when I power-cycled it and automounted it (/dev/sdb) to /media/usbdisk.  Not liking that name, I realized gparted hadn't prompted me for a volume label, so I then umounted it and ran:  &lt;pre&gt;&lt;br /&gt;  $ e2label /dev/sdb1 maxtor&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I guess I'm in the right group to allow me to do that.  Yanking and reinserting the USB cable now caused the ghosts in the machine to mount the drive at /media/maxtor.  Perfect!&lt;br /&gt;&lt;br /&gt;But alas, I couldn't write to it.  :-(&lt;pre&gt;&lt;br /&gt;  $ sudo chmod 777 /media/maxtor&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There, all better.  Now I'm off to move all my files back to it, and hook it up to a "real" file server.  In doing so, I'll no longer benefit from all the udev automounting hocus pocus, because I'm pretty sure that's a gnome-ish feature.  If I'm not logged into gnome, nothing shows up in /media when I connect the drive, though /var/log/messages does show the device being detected as /dev/sdb.  I'm pretty sure I'll need to put something like the following in /etc/fstab:&lt;pre&gt;&lt;br /&gt;  LABEL=maxtor  /mnt/maxtor  auto  defaults  0 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I'll let you know if that doesn't work out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-1031011253753716663?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/1031011253753716663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=1031011253753716663' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1031011253753716663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/1031011253753716663'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/01/maxtor-onetouch-hfs-to-ext3.html' title='Maxtor OneTouch, HFS+ to ext3'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-7521673093920018063</id><published>2007-01-08T19:44:00.000-08:00</published><updated>2007-01-08T16:46:47.075-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='meizu'/><title type='text'>My New Meizu</title><content type='html'>My wife bought me a MiniPlayer for Christmas, a portable media player made by Meizu, a Chinese manufacturer.  I love the darn thing.&lt;br /&gt;&lt;br /&gt;My wife has owned two iPods, an original and a Nano.  I hate the darn things.  I think they're unreliable and flaky.  Oh, and overpriced.&lt;br /&gt;&lt;br /&gt;But I have a large music library, and my new job has a long commute, plenty enough time to listen to some cool, geeky podcasts.  She asked if I wanted one for Christmas.  I said sure, but only if it works with Linux and plays ogg files.  So she found the Meizu, which does that and much more, including videos, which I wasn't expecting.  So far, I've been very impressed.  There are plenty of &lt;a href="http://www.google.com/search?q=meizu+miniplayer+review"&gt;reviews on the web&lt;/a&gt;, and YouTube has some &lt;a href="http://youtube.com/results?search_query=meizu"&gt;demonstration videos&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you're looking for a great DAP for Linux, I heartily recommend it.  Amarok works well with it, and you can use mencoder to format your videos to play on it.  It ships with the Windows-only VirtualDub, but the following mencoder incantation has worked great for me.  SRC is the filename to be converted, and TGT is the name of the output file, sans extension.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CMD="mencoder $SRC \&lt;br /&gt;-quiet \&lt;br /&gt;-vc ffmpeg12,mpeg12, \&lt;br /&gt;-ofps 20 \&lt;br /&gt;-vf scale=320:240:0:0:0.00:0.75,expand=320:240,rotate=1 \&lt;br /&gt;-ovc xvid \&lt;br /&gt;-xvidencopts profile=dxnhtntsc:cartoon:zones=0,w,1.0:bitrate=384:pass=1 \&lt;br /&gt;-oac mp3lame \&lt;br /&gt;-lameopts cbr:br=128 \&lt;br /&gt;-info name=$TGT \&lt;br /&gt;-o $TGT.avi"&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-7521673093920018063?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/7521673093920018063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=7521673093920018063' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/7521673093920018063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/7521673093920018063'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/01/my-new-meizu.html' title='My New Meizu'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-8919404353565200362</id><published>2007-01-06T12:47:00.000-08:00</published><updated>2007-02-01T11:25:40.037-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='beauty'/><category scheme='http://www.blogger.com/atom/ns#' term='meizu'/><title type='text'>Beauty and the Bike</title><content type='html'>It's the middle of January, and I just returned from a lovely bike ride on the  &lt;a href="http://georgiatrails.com/trails/bigcreek.html"&gt;Alpharetta Big Creek Greenway&lt;/a&gt;.  There was a slight chill, but 'twas really downright balmy for January.  A few clouds, but mostly bright and blue.  Definitely could drive with the windows down.&lt;br /&gt;&lt;br /&gt;I don't get outside often enough.  My job keeps me indoors, and frankly I don't mind all that much, but when I get the chance to ride my bike on a good-weather day, I like to take it.  And I usually like to drag parts of my family along, too.&lt;br /&gt;&lt;span class="down" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;&lt;/span&gt;&lt;br /&gt;I couldn't persuade any of them to go today, but I really wasn't trying that hard.  I wanted to bring my new &lt;a href="http://www.miniplayer.info/"&gt;Meizu MiniPlayer&lt;/a&gt; with me, and that's a solitary endeavor, right?  You can't very well listen to the &lt;a href="http://en.wikipedia.org/wiki/Digital_audio_player"&gt;DAP&lt;/a&gt; on a ride with other folks, especially family.&lt;br /&gt;&lt;br /&gt;For me, few things are more enjoyable than riding a bike on a beautiful, i.e. cool but sunny, day listening to great tunes on a portable music player. DAP's avoid the &lt;a href="http://en.wikipedia.org/wiki/Doppler_effect"&gt;Doppler effect&lt;/a&gt;.  The music stays right with you as you try to move away from it.  Plenty of  songs sound great on a bike, but today I embraced the 70's.  Steve Miller, ELO, Parliament, Al Green, Joni Mitchell, and the Staples Singers, among others.&lt;br /&gt;&lt;br /&gt;In particular, Al Green and Mavis Staples made me so happy on the bike trail that I guess I was smiling a lot, because I received smiles from plenty of other folks on the trail, too, all of whom I thought I recognized, none of whom I actually did.&lt;br /&gt;&lt;br /&gt;Is it any wonder Prince tried to revive Mavis Staples' career?&lt;br /&gt;&lt;br /&gt;I love the Alpharetta bike trail.  There's a creek -- a Big one, apparently -- that runs alongside it.  In the summer, snakes are plentiful.  It's always exciting to see one, and we always see at least one on any given trip.  Most of the six-mile trail is paved but there's a certain swamp-like section comprised of twisty, wooden slats that are fun to navigate.  The trail runs behind Northpoint Mall, various Alpharetta business parks and the usual  McMansion subdivisions.&lt;br /&gt;&lt;br /&gt;I have a touch of the flu on this day, so even though the entire ride was mostly perfect, the snot would tend to build up periodically.  Finding an unpopulated stretch and snorting it out provided enormous relief and the occasional burst of energy that puts my butt in the air, my weight forward, the handlebars seemingly touching the ground with each pump of the pedal, finally reaching the pinnacle of one of the many bridges over that winding creek, and leaning back on the seat, straightening the spine, the wind on my face, coasting down with hands on hips while Al Green sings of Love and Happiness...&lt;br /&gt;&lt;br /&gt;Life is beautiful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-8919404353565200362?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/8919404353565200362/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=8919404353565200362' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8919404353565200362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/8919404353565200362'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2007/01/beauty-and-bike.html' title='Beauty and the Bike'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-116658770745055433</id><published>2006-12-19T20:01:00.000-08:00</published><updated>2007-01-06T12:44:46.718-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='crap'/><title type='text'>New Year's Resolution</title><content type='html'>My new year's resolution is to blog more.&lt;br /&gt;&lt;br /&gt;So I'm getting a jump on 2007.  I created this blog in September of 2004.  I added my first post a year later.  This is my second entry... in December of 2006.  Clearly, I don't have a handle on this whole blogging concept yet.&lt;br /&gt;&lt;br /&gt;So I'm going to blog more in 2007.  More self-indulgence and opinions... woo-hoo!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-116658770745055433?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/116658770745055433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=116658770745055433' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/116658770745055433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/116658770745055433'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2006/12/my-new-years-resolution-is-to-blog.html' title='New Year&apos;s Resolution'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-16979882.post-112733567087844446</id><published>2005-09-21T12:44:00.000-07:00</published><updated>2007-01-06T12:15:43.738-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='crap'/><title type='text'>First</title><content type='html'>I'm a software developer.&lt;br /&gt;&lt;br /&gt;At any given time, I know about 17 minutes worth of crap.  After 17 minutes, I'll start to repeat myself.  My job requires me to know more than 17 MWOC, so obviously I need to write stuff down.  I've tried to do that &lt;a href="http://www.crossleys.org/%7Ejim/cms"&gt;here,&lt;/a&gt; but that's becoming unwieldy, so I'm going to try my hand at blogging.  It's mostly for me, but I hope it might be useful to others, too.&lt;br /&gt;&lt;br /&gt;17 minutes later...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/16979882-112733567087844446?l=escx.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://escx.blogspot.com/feeds/112733567087844446/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=16979882&amp;postID=112733567087844446' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/112733567087844446'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/16979882/posts/default/112733567087844446'/><link rel='alternate' type='text/html' href='http://escx.blogspot.com/2005/09/im-software-developer.html' title='First'/><author><name>Jim Crossley</name><uri>http://www.blogger.com/profile/01021808246199010203</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
