<?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-12172455</id><updated>2012-01-05T17:38:27.455-05:00</updated><category term='jquery'/><category term='gwt'/><category term='pdf adobe tools'/><category term='fun'/><category term='ibm bpm css3 flexbox jquery'/><category term='web4j'/><category term='java'/><category term='glassbox oracle jdbc'/><category term='orm'/><category term='db'/><category term='programming'/><title type='text'>JeoFTP</title><subtitle type='html'>Putting up with Enterprise Java programming. And loving every millisecond of it.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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>35</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-12172455.post-5879605259212825486</id><published>2012-01-05T17:37:00.000-05:00</published><updated>2012-01-05T17:37:20.845-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ibm bpm css3 flexbox jquery'/><title type='text'>Web Development for the fun of it</title><content type='html'>Right now I'm co-leading Product Design for &lt;a href="http://www-01.ibm.com/software/info/bpm/"&gt;IBM BPM&lt;/a&gt;. That means I create all the detailed specs for how a product feature is going to work, but I don't get the luxury of building it myself. An army of software engineers does that part.&lt;br /&gt;
&lt;br /&gt;
Although I don't get to write production code these days, I still like to do prototypes to prove the feasibility of my ideas. This has opened my eyes to an array of incredible new libraries for web developers.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://github.com/thoughtbot/bourbon#readme"&gt;Bourbon&lt;/a&gt; makes it reeeeally easy to write modern CSS3, hiding all the convoluted vendor-specific&amp;nbsp;repetitive&amp;nbsp;ugliness from you.&amp;nbsp;It's built on&amp;nbsp;&lt;a href="http://sass-lang.com/"&gt;sass&lt;/a&gt;, whose mission was basically to fix CSS's stuttering problem. Finally, writing CSS is actually fun again!&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;"But Jeoff, what about IE, I thought it can't do CSS3?"&lt;/b&gt; No problem, even Internet Elephant (IE) can be made to dance with CSS3 gradients, rounded corners, and shadows: it's as easy as &lt;a href="http://css3pie.com/"&gt;PIE&lt;/a&gt;!&amp;nbsp;Also, check out&amp;nbsp;&lt;a href="http://flexiejs.com/"&gt;flexie&lt;/a&gt;,&amp;nbsp;which brings the CSS3 flexible box model to IE and other browsers. Create some layouts using flexbox and style some stuff using CSS3 fx and you'll never go back again. I guarantee it.&lt;br /&gt;
&lt;br /&gt;
Now that you're ready to write some CSS3, &lt;a href="http://graphicpeel.com/cssiosicons"&gt;check out what's possible&lt;/a&gt;!&lt;br /&gt;
&lt;br /&gt;
p.s. Yes, I'm &lt;a href="http://jeoftp.blogspot.com/2010/01/why-i-left-gwt-and-came-crawling-back.html"&gt;still using jquery&lt;/a&gt;. I agree with the SproutCore folks that &lt;a href="http://blog.sproutcore.com/giving-back-to-jquery/"&gt;jQuery has become the standard library of the web&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-5879605259212825486?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/5879605259212825486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=5879605259212825486' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/5879605259212825486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/5879605259212825486'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2012/01/web-development-for-fun-of-it.html' title='Web Development for the fun of it'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-6048287650523704686</id><published>2010-10-20T17:00:00.000-04:00</published><updated>2010-10-20T17:00:11.372-04:00</updated><title type='text'>Long live the quirks-mode box model!</title><content type='html'>There are lots of things to hate about Internet Explorer, but I've always preferred their "broken" box model. Well sometimes bugs turn out to be loveable, soft, furry critters, and so now you can have it even in standards mode, thanks to CSS3:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="css" name="code"&gt;* {
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}
&lt;/pre&gt;&lt;br /&gt;
&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#Support_for_Internet_Explorer.27s_box_model"&gt;From wikipedia&lt;/a&gt;:&lt;br /&gt;
&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: sans-serif; font-size: 13px; line-height: 19px;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div style="line-height: 1.5em; margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.4em;"&gt;&lt;span class="Apple-style-span" style="font-family: sans-serif; font-size: 13px; line-height: 19px;"&gt;Web designer Doug Bowman has said that the original Internet Explorer box model represents a better, more logical approach.&lt;sup class="reference" id="cite_ref-17" style="font-style: normal; font-weight: normal; line-height: 1em;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#cite_note-17" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none; white-space: nowrap;"&gt;[18]&lt;/a&gt;&lt;/sup&gt;&amp;nbsp;Peter-Paul Koch gives the example of a physical box, whose dimensions always refer to the box itself, including potential padding, but never its content.&lt;sup class="reference" id="cite_ref-CSS2_-_Box_model_tweaking_9-1" style="font-style: normal; font-weight: normal; line-height: 1em;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#cite_note-CSS2_-_Box_model_tweaking-9" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none; white-space: nowrap;"&gt;[10]&lt;/a&gt;&lt;/sup&gt;&amp;nbsp;He says that this box model is more useful for graphic designers, who create designs based on the visible width of boxes rather than the width of their content.&lt;sup class="reference" id="cite_ref-18" style="font-style: normal; font-weight: normal; line-height: 1em;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#cite_note-18" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none; white-space: nowrap;"&gt;[19]&lt;/a&gt;&lt;/sup&gt;&amp;nbsp;Bernie Zimmermann says that the Internet Explorer box model is closer to the definition of cell dimensions and padding used in the HTML table model.&lt;sup class="reference" id="cite_ref-19" style="font-style: normal; font-weight: normal; line-height: 1em;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#cite_note-19" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none; white-space: nowrap;"&gt;[20]&lt;/a&gt;&lt;/sup&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="line-height: 1.5em; margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.4em;"&gt;&lt;span class="Apple-style-span" style="font-family: sans-serif; font-size: 13px; line-height: 19px;"&gt;The W3C has included a "box-sizing" property in CSS3. When&amp;nbsp;&lt;code style="background-color: #f9f9f9; font-family: monospace, sans-serif;"&gt;box-sizing: border-box;&lt;/code&gt;&amp;nbsp;is specified for an element, any padding or border of the element is drawn&amp;nbsp;&lt;i&gt;inside&lt;/i&gt;&amp;nbsp;the specified width and height, "as commonly implemented by legacy HTML user agents".&lt;sup class="reference" id="cite_ref-20" style="font-style: normal; font-weight: normal; line-height: 1em;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#cite_note-20" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none; white-space: nowrap;"&gt;[21]&lt;/a&gt;&lt;/sup&gt;&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_8" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="Internet Explorer 8"&gt;Internet Explorer 8&lt;/a&gt;,&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Opera_(web_browser)" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="Opera (web browser)"&gt;Opera&lt;/a&gt;&amp;nbsp;7.0 and later, and&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Konqueror" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="Konqueror"&gt;Konqueror&lt;/a&gt;&amp;nbsp;3.3.2 and later support the CSS3 box-sizing property.&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Gecko_(layout_engine)" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="Gecko (layout engine)"&gt;Gecko-based&lt;/a&gt;&amp;nbsp;browsers such as&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Mozilla_Firefox" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="Mozilla Firefox"&gt;Mozilla Firefox&lt;/a&gt;&amp;nbsp;support the same functionality using a&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Proprietary_software" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="Proprietary software"&gt;proprietary&lt;/a&gt;&amp;nbsp;"-moz-box-sizing" property,&lt;sup class="reference" id="cite_ref-21" style="font-style: normal; font-weight: normal; line-height: 1em;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#cite_note-21" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none; white-space: nowrap;"&gt;[22]&lt;/a&gt;&lt;/sup&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/WebKit" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="WebKit"&gt;WebKit&lt;/a&gt;&amp;nbsp;browsers such as&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Safari_(web_browser)" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="Safari (web browser)"&gt;Apple Safari&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Google_Chrome" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none;" title="Google Chrome"&gt;Google Chrome&lt;/a&gt;&amp;nbsp;support it as a proprietary "-webkit-box-sizing" property.&lt;sup class="reference" id="cite_ref-22" style="font-style: normal; font-weight: normal; line-height: 1em;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#cite_note-22" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #0645ad; text-decoration: none; white-space: nowrap;"&gt;[23]&lt;/a&gt;&lt;/sup&gt;&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-6048287650523704686?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/6048287650523704686/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=6048287650523704686' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6048287650523704686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6048287650523704686'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2010/10/long-live-quirks-mode-box-model.html' title='Long live the quirks-mode box model!'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-1144466618531687668</id><published>2010-01-15T12:00:00.009-05:00</published><updated>2010-01-15T12:47:11.914-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gwt'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>jquery poised to gain on GWT's compiler, thanks to, um ... Google</title><content type='html'>&lt;p&gt;Buried in the &lt;a href="http://jquery14.com/day-01/jquery-14"&gt;jquery 1.4 release announcement&lt;/a&gt;, under the Miscellaneous section, is the following tidbit:

&lt;blockquote&gt;Now use Closure Compiler instead of YUI Min (&lt;a href="http://github.com/jquery/jquery/commit/3fd62eae9df3159fc238a515bb748140a942313d"&gt;commit&lt;/a&gt;)&lt;/blockquote&gt;

&lt;p&gt;So what is Closure Compiler? According to &lt;a href="http://code.google.com/closure/compiler/"&gt;the project page at Google Labs&lt;/a&gt;,

&lt;blockquote&gt;"The Closure Compiler is a tool for making JavaScript download and run faster. It is a true compiler for JavaScript. Instead of compiling from a source language to machine code, it compiles from JavaScript to better JavaScript. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls."&lt;/blockquote&gt;

&lt;p&gt;According to the commit message, switching from YUI Compressor to Google Closure Compiler reduced the filesize by 13% with no other changes.

&lt;h3&gt;But wait, there's more!&lt;/h3&gt;

&lt;p&gt;A 13% bump is not bad, and certainly no one is surprised these days when Google one-ups Yahoo. But it gets better: jquery is so far only using Closure Compiler in Standard Optimizations mode (the default setting). It is not yet using the more aggressive &lt;a href="http://code.google.com/closure/compiler/docs/api-tutorial3.html"&gt;Advanced Optimizations&lt;/a&gt;. With advanced optimizations enabled, Closure Compiler also:

&lt;p&gt;&lt;ol&gt;&lt;li&gt;renames global variables and function names to make them shorter, &lt;/li&gt;&lt;li&gt;removes unused functions, and &lt;/li&gt;&lt;li&gt;inlines references where possible, including function calls, constants, and variables.&lt;/li&gt;&lt;/ol&gt;

&lt;h3&gt;The catch is...&lt;/h3&gt;

&lt;p&gt;In order to use Advanced Optimizations safely, you need to tell Closure Compiler which symbols are actually calls to external code (externs) and which symbols need to be preserved as public API so that other libraries can reference them (exports). In the case of exports, it still shortens the symbol names everywhere they are used, but it sets up a single alias with the public name.

&lt;p&gt;Setting up externs and exports takes time, effort, and testing, and it's probably the reason why jquery isn't using advanced optimizations just yet. Still, the source code is out there in plain sight and anyone can do it. So it's only a matter of time before jquery gains some of the same compiler optimizations that lend GWT its performance advantage.

&lt;p&gt;Of course, for the code you write on top of jquery to also leverage Closure Compiler, you will have to add it to your build step and use it on your own javascript code as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-1144466618531687668?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/1144466618531687668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=1144466618531687668' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/1144466618531687668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/1144466618531687668'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2010/01/jquery-poised-to-gain-some-of-same.html' title='jquery poised to gain on GWT&apos;s compiler, thanks to, um ... Google'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-2579215648394289888</id><published>2010-01-05T17:04:00.007-05:00</published><updated>2010-01-05T17:15:43.264-05:00</updated><title type='text'>The best Javascript toolkit is...</title><content type='html'>The contenders are dojo, yui, jquery, mootools, gwt, scriptaculous. The votes are in. The people have spoken. The best Javascript toolkit, by popularity, is....

&lt;a style="text-decoration:none" href="http://www.google.com/trends?q=jquery%2C+extjs%2C+gwt%2C+yui%2C+dojo%2C+mootools%2C+scriptaculous&amp;ctab=0&amp;geo=all&amp;date=all&amp;sort=1"&gt;

&lt;img src="http://www.google.com/trends/viz?q=jquery,+extjs,+gwt,+yui,+dojo,+mootools,+scriptaculous&amp;date=all&amp;geo=all&amp;graph=weekly_img&amp;sort=1&amp;sa=N"&gt;

&lt;p&gt;&lt;p&gt;jquery.&lt;p&gt;&lt;p&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-2579215648394289888?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/2579215648394289888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=2579215648394289888' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/2579215648394289888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/2579215648394289888'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2010/01/best-javascript-toolkit-is.html' title='The best Javascript toolkit is...'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-1626857690105112714</id><published>2010-01-05T15:11:00.009-05:00</published><updated>2010-01-05T16:31:36.244-05:00</updated><title type='text'>Why I left GWT and came crawling back to jquery</title><content type='html'>&lt;p&gt;&lt;a href="http://jquery.com/"&gt;jquery&lt;/a&gt;, I'm sorry I said those &lt;a href="http://jeoftp.blogspot.com/2009/02/jquery-rocks-now-back-to-gwt.html"&gt;mean things&lt;/a&gt; about you. After all the good times we had together, I walked away the moment GWT batted its eyes at me. 

&lt;p&gt;We got along okay at first, GWT and me. But then I started running into &lt;a href="http://groups.google.com/group/google-web-toolkit/browse_thread/thread/6ad355abb12505f6/fd7078e216c66ac9?hl=en&amp;ie=UTF-8&amp;oe=utf-8&amp;q=gwt+jeoffwilks#fd7078e216c66ac9"&gt;problems with the simplest things&lt;/a&gt; due to GWT's convoluted overuse of container markup for things like text and links. Trudging on, I encountered &lt;a href="http://code.google.com/p/google-web-toolkit/issues/detail?id=2802"&gt;vexing UI layout bugs&lt;/a&gt; that could not easily be solved without dissecting that very-same convoluted container markup. When I cracked open the Firebug Inspector, I was shocked to see just &lt;a href="http://development.lombardi.com/?p=253#comment-1472"&gt;how many containers&lt;/a&gt; were being created for even the simplest layouts, and that same complexity made it mind-numbingly tedious to comprehend the effects of the CSS on all that markup.

&lt;p&gt;Of course, using Eclipse's superior source-code navigation capabilities, I was able to find the offending source code. It was one of those weird decisions made in a private method 4 superclasses up. (To fork, or not to fork? That is the question.) With the offending code safely barricaded, er, encapsulated, I turned back to hacking the CSS rules on that seemingly unending hierarchy of containers. That was when I first doubted my decision to leave you, jquery... But I trudged on, in search of that beautiful, statically-typed, generic, polymorphic sunrise.

&lt;h4&gt;The two faces of GWT-RPC&lt;/h4&gt;

&lt;p&gt;Okay, so the approach to UI layout is needlessly complex and tedious, but at least there is the beauty of GWT-RPC, right? That's what I thought, until I learned firsthand of the &lt;a href"http://groups.google.com/group/google-web-toolkit-contributors/browse_thread/thread/3c768d8d33bfb1dc/ee0a3e809813e24a?hl=en&amp;ie=UTF-8&amp;oe=utf-8&amp;q=gwt+gae+dto"&gt;nasty RPC problems&lt;/a&gt; that arise when you make assumptions about what objects you can transfer. There are JavaScriptObjects and pojos and a bloodbath of DTOs trying to hold things together. 

&lt;p&gt;There was the time I naively assumed that I could read a JavaScriptObject from the "wild" and then pass it upstream in a GWT-RPC call. BZZZTT! Please convert to a pojo and try again.

&lt;p&gt;Then there was the dreaded RPC Types Whitelist thingie that couldn't deal with my clever use of generics. I couldn't find this mythical whitelist file, so I just added dummy methods to my RPC interface referencing all the different concrete types I would need to be able to handle. (Bleh.)

&lt;h4&gt;Closures&lt;/h4&gt;

&lt;p&gt;There was also the misery of recreating in tedious boilerplate Java what is so succint and beautiful in Javascript. Closures become anonymous callback classes with several lines of boilerplate for the interface instantiation and the method declaration. I would agonize about whether to convert to a top-level class, which would then require me to pass in all my closure variables to a constructor and hold them in member variables, and pretty soon the once-elegant javascript closure has exploded into a 100-line .java file of its very own...

&lt;p&gt;Java has a degree of elegance when compared to C++, but not next to Javascript: closures save time. Hip companies like Apple &lt;a href="http://developer.apple.com/mac/articles/cocoa/introblocksgcd.html"&gt;know this&lt;/a&gt;. Maybe someday &lt;a href="http://blogs.sun.com/mr/entry/closures_qa"&gt;Sun will catch up&lt;/a&gt;.

&lt;h4&gt;Here's your host.... (loading)&lt;/h4&gt;

&lt;p&gt;Then there's GWT hosted mode with its slow launch time and its generated code that I can't follow in Firebug because, you know, some-really-smart-guy-that's-probably-smarter-than-me wrote it, and so what right do I have to debug code generated by said-smart-guy's compiler? But then there's this little problem called, I have no idea why my RPC is failing, and why does it have to be this hard? and maybe the GWT-RPC mechanism is just flawed, and maybe I should just dump it and rewrite my own RPC mechanism?

&lt;p&gt;Dump GWT-RPC and rewrite my own RPC mechanism? Not a bad idea....

&lt;p&gt;And so I did.

&lt;p&gt;Using jquery.

&lt;h4&gt;JSON, JSOFF&lt;/h4&gt;

&lt;p&gt;The funny thing is, it was so easy. All I needed was some JSON conversion on the server-side and then your getJSON() method took care of everything else for me. And then finally, I had my Firebug back. And I had my closures back. And no more private methods 4 superclasses up; I had my easy mixins back. And suddenly event-handling was easy again.

&lt;p&gt;I miss a few things from that fling with GWT. The CSS sprites were good (ImageBundle, in GWT parlance). The idea of deferred binding is nice. The focus on performance in general, will be missed. The new native hosted mode is definitely a step up from GWT 1.x. I will miss refactoring, sort of. (It's absolutely essential when dealing with the verbosity of Java, but many of the types of code changes that make refactoring necessary in Java just aren't even necessary in Javascript.)

&lt;p&gt;Still, it's good to be back. It's amazing what you can do in just a few lines with a javascript closure. And I don't mind writing "throwaway javascript code" when it feels good every time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-1626857690105112714?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/1626857690105112714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=1626857690105112714' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/1626857690105112714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/1626857690105112714'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2010/01/why-i-left-gwt-and-came-crawling-back.html' title='Why I left GWT and came crawling back to jquery'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-3996298236384825964</id><published>2009-12-16T16:28:00.001-05:00</published><updated>2009-12-16T16:28:36.218-05:00</updated><title type='text'>IBM Acquires Lombardi</title><content type='html'>Lombardi, my employer, announced this morning that they have been &lt;a href="http://www.lombardisoftware.com/ibm_to_acquire_lombardi.php"&gt;acquired by IBM&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-3996298236384825964?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/3996298236384825964/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=3996298236384825964' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/3996298236384825964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/3996298236384825964'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/12/ibm-acquires-lombardi.html' title='IBM Acquires Lombardi'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-3076801993241209367</id><published>2009-07-06T15:47:00.004-04:00</published><updated>2009-07-06T15:52:02.203-04:00</updated><title type='text'>Tweaks to Zach's humane Date script</title><content type='html'>I like &lt;a href="http://www.zachleat.com/web/2008/03/23/yet-another-pretty-date-javascript/"&gt;Zach's humane Date script&lt;/a&gt; (based on work by Dean Landolt, John Resig, and henrah), but I wanted to be able to pass in an actual Date object in addition to just an ISO8601 date string. I also wanted it to be able to handle future dates (e.g. "10 minutes from now"). Here's the result:

&lt;pre name="code" class="js"&gt; 
/*
 * Javascript Humane Dates
 * Copyright (c) 2008 Dean Landolt (deanlandolt.com)
 * Re-write by Zach Leatherman (zachleat.com)
 * 
 * Adopted from the John Resig's pretty.js
 * at http://ejohn.org/blog/javascript-pretty-date
 * and henrah's proposed modification 
 * at http://ejohn.org/blog/javascript-pretty-date/#comment-297458
 * 
 * Licensed under the MIT license.
 */

 /**
  * @param {String or Date} date_str either an ISO8601 date string or a Date object.
  *          Note: ISO8601 dates are always formatted using UTC/GMT timezone.
  *          Example: 2009-06-03T20:06:44Z
  */
function humane_date(date_str){
 var time_formats = [
  [1, '1 second'],
  [60, 'seconds', 1],
  [90, '1 Minute'], // 60*1.5
  [3600, 'Minutes', 60], // 60*60, 60
  [5400, '1 Hour'], // 60*60*1.5
  [86400, 'Hours', 3600], // 60*60*24, 60*60
  [129600, '1 Day'], // 60*60*24*1.5
  [604800, 'Days', 86400], // 60*60*24*7, 60*60*24
  [907200, '1 Week'], // 60*60*24*7*1.5
  [2628000, 'Weeks', 604800], // 60*60*24*(365/12), 60*60*24*7
  [3942000, '1 Month'], // 60*60*24*(365/12)*1.5
  [31536000, 'Months', 2628000], // 60*60*24*365, 60*60*24*(365/12)
  [47304000, '1 Year'], // 60*60*24*365*1.5
  [3153600000, 'Years', 31536000], // 60*60*24*365*100, 60*60*24*365
  [4730400000, '1 Century'], // 60*60*24*365*100*1.5
 ];

 var time = ('' + date_str).replace(/-/g,"/").replace(/[TZ]/g," "),
  dt = new Date,
  seconds = (date_str instanceof Date ? (dt - date_str)
              : (dt - new Date(time) + (dt.getTimezoneOffset() * 60000))) / 1000,
  token = ' ago',
  i = 0,
  format;

 if (seconds &lt; 0) {
  seconds = Math.abs(seconds);
  token = ' from now';
 }

 while (format = time_formats[i++]) {
  if (seconds &lt; format[0]) {
   if (format.length == 2) {
    return format[1] + token
   } else {
    return Math.round(seconds / format[2]) + ' ' + format[1] + token;
   }
  }
 }

 // overflow for centuries
 if(seconds &gt; 4730400000)
  return Math.round(seconds / 4730400000) + ' Centuries' + token;

 return date_str;
};

if(typeof jQuery != 'undefined') {
 jQuery.fn.humane_dates = function(){
  return this.each(function(){
   var date = humane_date(this.title);
   if(date &amp;&amp; jQuery(this).text() != date) // don't modify the dom if we don't have to
    jQuery(this).text(date);
  });
 };
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-3076801993241209367?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/3076801993241209367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=3076801993241209367' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/3076801993241209367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/3076801993241209367'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/07/tweaks-to-zachs-humane-date-script.html' title='Tweaks to Zach&apos;s humane Date script'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-2861027649706213466</id><published>2009-04-17T21:44:00.007-04:00</published><updated>2009-04-17T22:17:57.379-04:00</updated><title type='text'>JSON Annotations</title><content type='html'>&lt;p&gt;Suppose you want a generic parser that will turn &lt;a href="http://json.org"&gt;JSON&lt;/a&gt; into a strongly-typed Java object graph. For certain kinds of object graphs you either have to parse into generic objects and then convert, or hint the parser at every step -- a tedious process. &lt;b&gt;What if those type hints were embedded within the JSON itself?&lt;/b&gt;

&lt;h4&gt;Option 1. Processing Instructions&lt;/h4&gt;
&lt;p&gt;This approach specifies a reserved prefix for meta-attributes, of which @type is one. Borrowing an idea from sed's variable syntax for find-and-replace separators, you can wrap the entire payload in one outer object that specifies what the metadata prefix is. That way if a given payload would clash with the default metadata prefix, it can be changed easily.

&lt;pre name="code" class="js"&gt;
{ "meta": "@"
  "data": {
    "@type": "java.util.HashMap",
    "Joe" : {
      "@type": "jeoftp.Person"
      "firstName": "Joseph",
      "lastName": "Robinson",
      "age": 45,
      "birthday": new Date(23493822934)
    },
    "Sue" : {
      "@type": "jeoftp.Person"
      ...
    }
  }
}
&lt;/pre&gt;

&lt;h4&gt;Option 2. Wrap Typed Objects with Annotations&lt;/h4&gt;
&lt;p&gt;Another way to annotate objects with their types is to wrap each object that needs specific type information within a meta-object that tells us its type. The parser would need to know how to recognize a meta-object vice a regular one.

&lt;pre name="code" class="js"&gt;
{
  "@class" : "java.util.HashMap",
  "@data" : {
    "Joe" : {
      "@class" : "jeoftp.Person",
      "@data" : {
        "firstName": "Joseph",
        "lastName": "Robinson",
        "age": 45,
        "birthday": new Date(23493822934)
      }
    },
    "Sue" : {
      "@class" : "jeoftp.Person",
      "@data" : { ... }
    }
  }
}
&lt;/pre&gt;

&lt;h4&gt;Option 3. Embed Annotations in Comments&lt;/h4&gt;
&lt;p&gt;You could also embed type information in comments. This approach borrows from Javadoc, Python docstrings, etc. This would certainly break some JSON parsers, but the advantage is that it strictly demarcates metadata from data.

&lt;pre name="code" class="js"&gt;
{
  /* @type java.util.HashMap */
  "Joe" : {
    /* @type jeoftp.Person */
    "firstName": "Joseph",
    "lastName": "Robinson",
    "age": 45,
    "birthday": new Date(23493822934)
    },
  "Sue" : {
    /* @type jeoftp.Person */
    ...
    }
}
&lt;/pre&gt;

&lt;p&gt;If your type parser was smart enough, perhaps you could even handle generics:

&lt;pre name="code" class="js"&gt;
{
  /* @type java.util.HashMap&lt;java.lang.String,org.jeoftp.Person&gt; */
  "Joe" : {
    "firstName": "Joseph",
    "lastName": "Robinson",
    "age": 45,
    "birthday": new Date(23493822934)
    },
  "Sue" : {
    ...
    }
}
&lt;/pre&gt;

&lt;p&gt;I haven't settled on one approach yet, but after reading this I'm leaning toward option 1 -- specifying a metadata prefix -- because it is valid in curent JSON syntax, easy to parse, and relatively compact.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-2861027649706213466?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/2861027649706213466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=2861027649706213466' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/2861027649706213466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/2861027649706213466'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/04/json-annotations.html' title='JSON Annotations'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-3499712492182141861</id><published>2009-03-23T11:10:00.008-04:00</published><updated>2009-04-08T13:00:16.406-04:00</updated><title type='text'>Determine the Java TimeZone of your web visitors using client-side javascript</title><content type='html'>&lt;p&gt;You want your Java web application to know what &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/TimeZone.html"&gt;TimeZone&lt;/a&gt; your visitors are in. You can use client-side javascript code like the following to produce a timezone string that Java can understand:

&lt;pre name="code" class="js"&gt;
function getTimezone() {
  var tzo = new Date().getTimezoneOffset();  //returns timezone offset in minutes
  function pad(num, digits) {
    num = String(num); while (num.length &lt; digits) { num="0"+num; }; return num;
  }
  return "GMT" + (tzo &gt; 0 ? "-" : "+") + pad(Math.floor(tzo/60), 2) + ":" + pad(tzo%60, 2);
}
&lt;/pre&gt;

&lt;p&gt;This will produce strings like &lt;b&gt;GMT-04:00&lt;/b&gt; and &lt;b&gt;GMT-07:00&lt;/b&gt; which you can then pass up to the server and into &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)"&gt;TimeZone.getTimeZone()&lt;/a&gt; to produce a TimeZone object.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-3499712492182141861?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/3499712492182141861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=3499712492182141861' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/3499712492182141861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/3499712492182141861'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/03/determine-java-timezone-of-your-web.html' title='Determine the Java TimeZone of your web visitors using client-side javascript'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-6602023750811834935</id><published>2009-03-19T23:04:00.012-04:00</published><updated>2009-09-19T08:18:55.839-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='glassbox oracle jdbc'/><title type='text'>Timing Oracle SQL queries with Glassbox</title><content type='html'>&lt;p&gt;&lt;a href="http://www.glassbox.com/"&gt;Glassbox&lt;/a&gt;, the slick AOP tool for automatically diagnosing your Java webapp's performance bottlenecks, first caught my eye nearly a year ago. In consulting activities, where off-the-shelf products and custom solution code mix, it can be very difficult to determine whose code is at fault when web pages load slowly.

&lt;/p&gt;&lt;p&gt;Glassbox comes to the rescue by tracking each web request through every layer of the J2EE stack. It identifies slow requests and groups them by URL, then identifies the controllers, methods, database queries, network operations, etc. that are causing the operation to be slow. It comes packaged in a war file: just drop it into the app server, hit the webapp root from a browser, and let it autoconfigure itself.

&lt;/p&gt;&lt;p&gt;The hitch I ran into was that Glassbox had trouble instrumenting our Oracle JDBC driver, so it couldn't identify the precise SQL queries that were causing slowness. I went to the forums initially, then forgot about it for a while when an answer wasn't readily available.

&lt;/p&gt;&lt;p&gt;I became interested again over the past couple days, so I contacted &lt;a href="http://rbodkin.blogs.com/"&gt;Ron Bodkin&lt;/a&gt; directly: he suggested the AspectJ load-time weaving is probably at issue. He gave me some quick pointers on how to use the ajc tool to create an instrumented version of the Oracle JDBC driver so that it wouldn't need to be instrumented on the fly at load-time. I gave it a shot and it worked!

&lt;/p&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_x5PtJrE-t4M/ScMLd0Qn4LI/AAAAAAAAAw4/r55FapiCoko/s1600-h/glassbox_slow_database.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 155px;" src="http://4.bp.blogspot.com/_x5PtJrE-t4M/ScMLd0Qn4LI/AAAAAAAAAw4/r55FapiCoko/s400/glassbox_slow_database.png" alt="" id="BLOGGER_PHOTO_ID_5315104592099795122" border="0" /&gt;&lt;/a&gt;

&lt;/p&gt;&lt;p&gt;In testing, I purposely crafted a slow query by using a ridiculous number of pointless joins, to verify whether Glassbox would call me out on it.&lt;/p&gt;

&lt;h3&gt;The cookbook&lt;/h3&gt;

&lt;p&gt;First, grab &lt;a href="http://downloads.sourceforge.net/glassbox/glassbox.war"&gt;Glassbox 2.0&lt;/a&gt; and &lt;a href="http://www.eclipse.org/downloads/download.php?file=/tools/aspectj/aspectj-1.5.3.jar"&gt;AspectJ 1.5.3&lt;/a&gt;. (To do offline weaving you'll need aspectjtools.jar, which doesn't ship with Glassbox). &lt;/p&gt;

&lt;p&gt;Create a project directory and drop in the jar file you want to weave. Then create a lib/ subdirectory. Add aspectjtools.jar from the AspectJ download, and then explode glassbox.war into it. Find ./lib/glassbox.war/install/glassboxMonitor.jar and extract the file META-INF/Xlint.properties from it; place it in the root directory of the project.&lt;/p&gt;

&lt;p&gt;Finally, run the following ant script:&lt;/p&gt;

&lt;textarea name="code" style="width:100%" rows="10" class="xml"&gt;
&lt;project name="glassbox-instrument-oracle" default="compile"&gt;
  &lt;taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties"&gt;
    &lt;classpath&gt;
      &lt;pathelement location="./lib/aspectjtools.jar" /&gt;
    &lt;/classpath&gt;
  &lt;/taskdef&gt;

  &lt;target name="compile"&gt;
    &lt;iajc inpath="./ojdbc14.jar" outjar="./odjbc14-glassbox-woven.jar"
      aspectpath="./lib/glassbox.war/install/glassboxMonitor.jar"
      xlintfile="./Xlint.properties"
      classpath="./lib/glassbox.war/WEB-INF/lib/aspectjrt.jar" /&gt;
  &lt;/target&gt;
&lt;/project&gt;

&lt;/textarea&gt;

&lt;p&gt;Note: Adjust your path to AspectJ as needed.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-6602023750811834935?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/6602023750811834935/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=6602023750811834935' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6602023750811834935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6602023750811834935'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/03/timing-oracle-sql-queries-with-glassbox.html' title='Timing Oracle SQL queries with Glassbox'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_x5PtJrE-t4M/ScMLd0Qn4LI/AAAAAAAAAw4/r55FapiCoko/s72-c/glassbox_slow_database.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-4623769320420707256</id><published>2009-03-19T21:02:00.003-04:00</published><updated>2009-03-19T21:20:17.590-04:00</updated><title type='text'>Things to study when I get a minute</title><content type='html'>Some of the things I'd like to spend more time with:
&lt;ul&gt;&lt;li&gt; &lt;a href="http://www.eclipse.org/aspectj/"&gt;AspectJ&lt;/a&gt;, &lt;a href="http://www.eclipse.org/ajdt/"&gt;AJDT&lt;/a&gt;, &lt;a href="http://www.glassbox.com/"&gt;Glassbox&lt;/a&gt; - AOP supertools
&lt;/li&gt;&lt;li&gt; &lt;a href="http://www.aptana.com/jaxer"&gt;Jaxer&lt;/a&gt;, &lt;a href="http://www.axiomstack.com/"&gt;Axiom Stack&lt;/a&gt;, &lt;a href="http://helma.org/"&gt;Helma&lt;/a&gt;, &lt;a href="http://appjet.com/"&gt;AppJet&lt;/a&gt; - web platforms that leverage the symmetry of using javascript on both the server and client side
&lt;/li&gt;&lt;li&gt; &lt;a href="http://lucene.apache.org/java/docs/"&gt;Lucene&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Berkeley_DB"&gt;Berkeley DB&lt;/a&gt; - faster than a database, given the right use case&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.terracotta.org/"&gt;Terracotta&lt;/a&gt; - enforces java memory model across a grid of servers&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/12172455-4623769320420707256?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/4623769320420707256/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=4623769320420707256' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/4623769320420707256'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/4623769320420707256'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/03/things-to-study-when-i-get-minute.html' title='Things to study when I get a minute'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-6545698071113612759</id><published>2009-03-17T15:34:00.006-04:00</published><updated>2009-04-08T12:58:25.596-04:00</updated><title type='text'>Reason #82734 why I hate Oracle JDBC drivers</title><content type='html'>&lt;p&gt;Are you wondering why the date/time values you read from Oracle 10g's jdbc driver are &lt;a href="http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.html#08_03"&gt;off by several hours?&lt;/a&gt; It's because the 9i/10g drivers don't properly account for the jdbc client's time zone when writing the date value.

&lt;h3&gt;So what's the fix?&lt;/h3&gt;
&lt;p&gt;Suppose you know your server date/time values are stored in GMT. You can call getTimestamp() with a GMT calendar, and then you'll get the right values.

&lt;pre name="code" class="java"&gt;
public class OracleJdbcDriverSucks {
  public static Calendar GMT_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

  public static Date getGMTDate(ResultSet rs, String column) throws SQLException {
    return new Date(rs.getTimestamp(column, GMT_CALENDAR).getTime());
  }
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-6545698071113612759?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/6545698071113612759/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=6545698071113612759' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6545698071113612759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6545698071113612759'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/03/reason-82734-why-i-hate-oracle-jdbc.html' title='Reason #82734 why I hate Oracle JDBC drivers'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-6346514891031904426</id><published>2009-02-12T09:28:00.005-05:00</published><updated>2010-01-05T16:34:20.131-05:00</updated><title type='text'>jquery rocks! now back to GWT</title><content type='html'>&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; read the followup to this post, &lt;a href="http://jeoftp.blogspot.com/2010/01/why-i-left-gwt-and-came-crawling-back.html"&gt;Why I left GWT and came crawling back to jquery&lt;/a&gt;.

&lt;p&gt;I just finished a project that includes a rich, complex web interface built on &lt;a href="http://jquery.com/"&gt;jquery&lt;/a&gt;, and now it's time to report the good, the bad, and the ugly.

&lt;h4&gt;The Good&lt;/h4&gt;
&lt;p&gt;Certain things can be expressed with incredible succinctness in jquery. In a finished project there are snippets of code sprinkled throughout that really evoke a smile. Certainly other projects have a lot to learn from the expressive power of jquery.

&lt;h4&gt;The Bad&lt;/h4&gt;
&lt;p&gt;One core principle behind jquery is the idea of progressive enhancement; that is, you start with HTML markup that would render reasonably well in a browser even with no javascript enabled. Then you add in some well placed jquery "enhancements" via selectors, plugins, etc. and the page literally comes alive.

&lt;p&gt; The problem is, projects have deadlines. When a business person funds a project, they don't really care if the site runs without javascript enabled. So, in the push to launch the project, the ideals of progressive enhancement quickly fall by the wayside.

&lt;h4&gt;The Ugly&lt;/h4&gt;
&lt;p&gt;In reaching for the ideal of progressive enhancement, jquery performs weakly in one important feature of traditional software engineering: &lt;b&gt;encapsulation&lt;/b&gt;. Certainly the code within plugins is encapsulated, but the markup on which those plugins operate is completely exposed. The very power of selectors and plugins for progressively enhancing existing markup are also their downfall: when the markup needs to change for some reason, you must carefully review/debug/bang-your-head-over all the selectors and plugins and the assumptions they made about the markup that are no longer true.

&lt;h4&gt;Back to GWT&lt;/h4&gt;
&lt;p&gt;I'm starting a new project now, and I'll be doing this one on &lt;a href="http://code.google.com/webtoolkit/"&gt;GWT&lt;/a&gt;. Is it because GWT is better than jquery? No, that is certainly not true in the general case. To breathe new life into an existing web site, there is perhaps nothing better than jquery. No, I'll be doing this project on GWT because progressive enhancement is not relevant. The ability to encapsulate DOM structures and behavior together, and then refactor wildly (with no fear of breaking carefully crafted selectors and plugins) will be essential to this project. And so it's back to GWT.

&lt;p&gt;Fear not, jquery, my friend. We have built great things together, and there &lt;em&gt;will&lt;/em&gt; be other times.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-6346514891031904426?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/6346514891031904426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=6346514891031904426' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6346514891031904426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6346514891031904426'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/02/jquery-rocks-now-back-to-gwt.html' title='jquery rocks! now back to GWT'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-2918920706382362165</id><published>2009-02-12T09:21:00.004-05:00</published><updated>2009-02-12T09:28:01.570-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pdf adobe tools'/><title type='text'>Review and annotate PDF files without Adobe messiness</title><content type='html'>&lt;p&gt;A technical writer at work sent me a document for review, and told me to use Adobe's commenting tools to annotate it. I open it up in Reader and -- surprise -- there aren't any commenting tools!

&lt;p&gt;I expected this to happen, because it wasn't the first time this had happened to me. Adobe Reader can only annotate PDF documents when the author has enabled specific usage rights. Apparently most technical writers either don't understand this, or they all have the full Adobe Acrobat and simply have never encountered the problem.

&lt;p&gt;The quick solution: grab &lt;a href="http://www.docu-track.com/downloads/users/"&gt;PDF-XChange Viewer&lt;/a&gt; (which allows mere mortals to annotate any PDF document), and leave the Adobe mess for someone else.

&lt;p&gt;p.s. the small "portable" version includes all the commenting tools (second Download button in the link above, as of this writing) ... not sure why the full version is so much bigger.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-2918920706382362165?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/2918920706382362165/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=2918920706382362165' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/2918920706382362165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/2918920706382362165'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/02/review-and-annotate-pdf-files-without.html' title='Review and annotate PDF files without Adobe messiness'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-675195728965628798</id><published>2009-01-22T14:43:00.004-05:00</published><updated>2009-01-22T15:13:52.990-05:00</updated><title type='text'>Beware jquery's :nth-child selector</title><content type='html'>&lt;p&gt;The past day or two I've been using selectors like this to manipulate tabular data:
&lt;ul&gt;&lt;li&gt; $("table#summary tr:nth-child("+k+")")&lt;/ul&gt;

&lt;p&gt;I was using the selectors to pump a table of audit data into a summary table, and the whole process was 5-6 seconds for under 50 rows. There were lots of ":contains" statements in there, so naturally I attacked those first, caching the results in .data() fields. Yet most of the lag time remained.

&lt;p&gt;So I cranked on the &lt;a href="http://www.getfirebug.com"&gt;Firebug&lt;/a&gt; profiler and profiled the function that does the heavy lifting. Calls to nth-child were taking up 4700ms out of about 5000ms total!

&lt;p&gt;After staring at it for a minute, I recalled that &lt;a href="http://docs.jquery.com/Selectors/nthChild#index"&gt;:nth-child&lt;/a&gt; looks for all elements that are the nth child of their parent -- that's what I want, right? -- but it &lt;em&gt;does not assume that they are direct descendants of the preceding element in the selection chain&lt;/em&gt;.

&lt;blockquote&gt;"Matches all elements that are the nth-child of their parent ... While :eq(index) matches only a single element, this matches more than one ..."&lt;/blockquote&gt;

&lt;p&gt;This is certainly what you need in some cases, but if your jquery consists of a set of siblings and you just want to grab a particular element by index, it's far quicker to do one of the following:
&lt;ul&gt;&lt;li&gt; $("table#summary tr:&lt;b&gt;eq&lt;/b&gt;("+k+")")
    &lt;li&gt; $("table#summary tr")&lt;b&gt;.eq&lt;/b&gt;(k)&lt;/ul&gt;

&lt;p&gt;Once I made that change, Firebug's Profiler timing went from 5000ms+ down to the 300-400ms range. Lesson learned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-675195728965628798?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/675195728965628798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=675195728965628798' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/675195728965628798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/675195728965628798'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/01/beware-jquerys-nth-child-selector.html' title='Beware jquery&apos;s :nth-child selector'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-5829136193573760433</id><published>2009-01-19T15:22:00.010-05:00</published><updated>2009-04-08T13:03:01.228-04:00</updated><title type='text'>Searching tables with jquery</title><content type='html'>&lt;p&gt;Suppose you have an html table like the one below (name, favorite color) and you want to grab all the favorite colors.

&lt;table class="jt"&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Favorite Color&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;td&gt;Joe&lt;/td&gt;&lt;td&gt;blue&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Sally&lt;/td&gt;&lt;td&gt;green&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Mike&lt;/td&gt;&lt;td&gt;yellow&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Ziggy&lt;/td&gt;&lt;td&gt;orange&lt;/th&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Here are some jquery plugin functions that let you do it:

&lt;pre name="code" class="js"&gt;
/** find the index of this node among its siblings **/
jQuery.fn.childn = function() { return 1 + jQuery(this).parent().find("&gt; *").index(this); }

/** given a table row, find the cell value under the supplied heading **/
jQuery.fn.column = function(heading) {
    var idx = jQuery(this).parents("table:first").find("th:contains("+heading+")").childn();
    return jQuery(this).find("&amp;gt; *:nth-child("+idx+")");
}
&lt;/pre&gt;

&lt;p&gt;Example:

&lt;pre name="code" class="js:nocontrols"&gt;
$("table tbody tr").column("Favorite Color");
&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; use :eq instead of :nth-child! See my &lt;a href="http://jeoftp.blogspot.com/2009/01/beware-jquerys-nth-child-selector.html"&gt;follow-up post&lt;/a&gt; for details.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-5829136193573760433?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/5829136193573760433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=5829136193573760433' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/5829136193573760433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/5829136193573760433'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2009/01/searching-tables-with-jquery.html' title='Searching tables with jquery'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-5802719748555195170</id><published>2008-08-16T11:24:00.003-04:00</published><updated>2008-09-27T17:26:46.042-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web4j'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><title type='text'>How to construct immutable objects using reflection</title><content type='html'>&lt;a href="http://www.javaworld.com/javaworld/jw-07-2008/jw-07-harmful-idioms.html?page=3"&gt;John O'Hanley argues&lt;/a&gt; that Javabeans should not be used with database frameworks.

The reason various ORM frameworks use Javabeans is because they need to be able to construct objects using reflection. Javabeans provided this capability, whereas constructors were hard to introspect (at least as of Java 1.4) because the compiler saves only the types of method arguments, and not their names.

The need to reflectively set properties by name is a legitimate one. Javabeans, by virtue of their extreme mutability, are certainly a problematic approach. But rather than discarding Javabeans &lt;a href="http://www.web4j.com/Java_Web_Application_Framework_Overview.jsp#NoORMMapping"&gt;and ORM&lt;/a&gt; entirely, John, you should propose a solution for those who want ORM. Then you will be taken more seriously.

Perhaps Java 5 annotations finally give us enough capability to construct immutable objects reflectively. A constructor could be given an annotation that tells the ORM framework how its arguments map to entity fields.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-5802719748555195170?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/5802719748555195170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=5802719748555195170' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/5802719748555195170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/5802719748555195170'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2008/08/how-to-construct-immutable-objects.html' title='How to construct immutable objects using reflection'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-7843868018721457950</id><published>2007-12-26T16:38:00.000-05:00</published><updated>2007-12-26T22:22:19.932-05:00</updated><title type='text'>RulePoint and RTAM</title><content type='html'>Here is a good description of the products I worked on while at Agent Logic:

&lt;span style="font-weight: bold;"&gt;Agent Logic’s RulePoint and RTAM&lt;/span&gt;&lt;br/&gt;&lt;a href="http://www.column2.com/2007/12/agent-logics-rulepoint-and-rtam/"&gt;http://www.column2.com/2007/12/agent-logics-rulepoint-and-rtam/&lt;/a&gt;

I did the designs for both products and was the lead developer on RulePoint through the first 2 releases.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-7843868018721457950?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/7843868018721457950/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=7843868018721457950' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/7843868018721457950'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/7843868018721457950'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2007/12/here-is-good-description-of-products-i.html' title='RulePoint and RTAM'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-7651497938966275166</id><published>2007-09-14T17:23:00.000-04:00</published><updated>2007-09-14T17:25:28.841-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='db'/><title type='text'>Column-oriented databases. Yawn.</title><content type='html'>WOW, just &lt;a href="http://www.databasecolumn.com/2007/09/one-size-fits-all.html"&gt;rotate your thinking 90 degrees&lt;/a&gt; and everything gets faster!

An inline explanation of what it means to be a column-oriented database would have been nice. For that I referred to &lt;a href="http://en.wikipedia.org/wiki/Column-oriented_DBMS"&gt;http://en.wikipedia.org/wiki/Column-oriented_DBMS&lt;/a&gt;

My conclusion is that your run-of-the-mill RDBMS can easily provide a new table type that uses column-oriented storage, without varying anything else (same SQL, same drivers, same integrations).

That leaves Vertica and its column-oriented friends in a dicey position: as soon as money starts flowing towards columns instead of rows, the big boys wake up and add a column-oriented storage option. Then everyone sighs and upgrades to Oracle 12p or whatever they end up calling it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-7651497938966275166?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/7651497938966275166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=7651497938966275166' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/7651497938966275166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/7651497938966275166'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2007/09/column-oriented-databases-yawn.html' title='Column-oriented databases. Yawn.'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-568927014212611673</id><published>2007-08-14T13:24:00.000-04:00</published><updated>2007-08-14T13:29:50.209-04:00</updated><title type='text'>This irony brought to you by Google</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_x5PtJrE-t4M/RsHlrfvOrJI/AAAAAAAAABM/Bapi1uwOtgY/s1600-h/java_ms_office.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://3.bp.blogspot.com/_x5PtJrE-t4M/RsHlrfvOrJI/AAAAAAAAABM/Bapi1uwOtgY/s400/java_ms_office.png" alt="" id="BLOGGER_PHOTO_ID_5098608788576840850" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Google informed me today that &lt;span style="font-style: italic;"&gt;java.exe is a process belonging to Microsoft Office!&lt;/span&gt; See for yourself in the screenshot at left. (hint: the ad with the red box outline).&lt;br /&gt;&lt;br /&gt;Oh and by the way, &lt;span style="font-style: italic;"&gt;Google needs Java experts!&lt;/span&gt; Be sure to remember during your Google interview that java.exe is a process belonging to Microsoft Office...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-568927014212611673?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/568927014212611673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=568927014212611673' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/568927014212611673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/568927014212611673'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2007/08/this-irony-brought-to-you-by-google.html' title='This irony brought to you by Google'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_x5PtJrE-t4M/RsHlrfvOrJI/AAAAAAAAABM/Bapi1uwOtgY/s72-c/java_ms_office.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-1502521201299180793</id><published>2007-07-26T14:05:00.001-04:00</published><updated>2009-04-08T13:22:30.692-04:00</updated><title type='text'>A simple LRU cache</title><content type='html'>Here it is. (Note: apply external locking when thread-safety is required).
&lt;pre name="code" class="java"&gt;int maxEntries = 50;
java.util.Map lruCache =
  new java.util.LinkedHashMap(maxEntries, .75F, true) {
    protected boolean removeEldestEntry(Map.Entry eldest) {
      return size() &amp;gt; maxEntries
    }
  };
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-1502521201299180793?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/1502521201299180793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=1502521201299180793' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/1502521201299180793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/1502521201299180793'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2007/07/simple-lru-cache.html' title='A simple LRU cache'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-6930458066727623899</id><published>2007-06-27T13:09:00.005-04:00</published><updated>2009-04-08T12:37:58.454-04:00</updated><title type='text'>jwhich - Java classes can run but they can no longer hide</title><content type='html'>&lt;p&gt;This bash command will search all .jar files in the current directory and all subdirectories for MyClass:

&lt;pre class="brush: bash; toolbar: false"&gt;for f in `find &lt;b&gt;.&lt;/b&gt; -name '*.jar'`; do jar tf $f | grep &lt;b&gt;"MyClass"&lt;/b&gt; | xargs -n 1 echo "$f:"; done&lt;/pre&gt;

&lt;p&gt;Here is a complete shell script version that accepts a search argument, optionally followed by a start path. I keep this script at /usr/bin/jwhich on my box.

&lt;p&gt;&lt;span style="font-weight: bold;"&gt;Sample output:&lt;/span&gt;&lt;pre&gt;$ jwhich MyClass
./myapp/my/hard/to/find/subdirectory/mymissing.jar: com/Missing/MyClass.class
$&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;The script itself:&lt;/span&gt;
&lt;pre class="brush: bash"&gt;#!/bin/bash

whichclass=$1
path=$2

if [ -z $whichclass ]; then
echo "usage: $0 searchstring [path]"
echo "\tsearchstring - search for class names that contain this substring"
echo "\tpath (optional) - start at this path (default: current directory)"
echo "\nSearch for a Java Class, scanning all .jar files in the current directory and all subdirectories"
exit 1;
fi

if [ -z $path ]; then
path="."
fi

for f in `find $path -name '*.jar'`; do
jar tf $f | grep $whichclass | xargs -n 1 -r echo "$f:";
done
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-6930458066727623899?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/6930458066727623899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=6930458066727623899' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6930458066727623899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/6930458066727623899'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2007/06/jwhich-java-classes-can-run-but-they.html' title='jwhich - Java classes can run but they can no longer hide'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-8268680158101158999</id><published>2007-06-21T10:15:00.001-04:00</published><updated>2009-04-08T13:23:12.390-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>A simple servlet Filter for JDBC Connections</title><content type='html'>Here is an example of a servlet Filter that could be used in a web application to check out a JDBC Connection from a connection pool, keep it open for the duration of a web request, and then insure that it gets closed at the end even if an exception occurs.

The implementation is similar to the &lt;a href="http://www.hibernate.org/43.html"&gt;open-session-in-view pattern&lt;/a&gt;, but in this case it is more correctly described as open-&lt;i&gt;connection&lt;/i&gt;-in-view.

Note: Because connection pool initialization varies depending on the pool provider, the initialization of the pool DataSource is left as an exercise for the reader.

&lt;pre name="code" class="java"&gt;package jeoftp;

import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.FilterChain;
import javax.sql.DataSource;
import java.sql.Connection;

/**
 * ConnectionProviderFilter
 *
 * Provide JDBC Connections to downstream servlets and insure they
 * are cleaned up before the web request completes. Servlets can use
 * &lt;code&gt;request.getAttribute(ConnectionProviderFilter.CONNECTION_ATTR)&lt;/code&gt;
 * to access the Connection.
 *
 * @author &lt;a href="mailto:jeoffwilks@gmail.com"&gt;Jeoff Wilks&lt;/a&gt;
 */
public class ConnectionProviderFilter implements Filter {

  public static final String CONNECTION_ATTR = "jeoftp.ConnectionProviderFilter";

  //most connection pools provide a standard JDBC DataSource facade
  private DataSource connectionPool;

  public void init(FilterConfig filterConfig) {
    //TODO: initialize your connectionPool here
  }

  public void destroy() {
    //TODO: clean up connectionPool here
  }

  public void doFilter(ServletRequest request,
                       ServletResponse response,
                       FilterChain chain) {

    Connection connection;

    try {
      //check out a connection for this web request
      connection = connectionPool.getConnection();

      //tie it to a request attribute so other servlets can grab it
      request.setAttribute(CONNECTION_ATTR, connection);

      //let other servlets do their work
      chain.doFilter(request, response);

    //optional: roll back transaction if an exception occurs
    } catch (Exception e) {
      connection.rollback();
      throw e;

    //mandatory: insure the connection is closed
    } finally {
      if (connection != null) {
        connection.close();
      }

    }      
  }
}   
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-8268680158101158999?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/8268680158101158999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=8268680158101158999' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/8268680158101158999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/8268680158101158999'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2007/06/simple-sevlet-filter-for-jdbc.html' title='A simple servlet Filter for JDBC Connections'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-874552887987693932</id><published>2007-03-12T11:37:00.000-04:00</published><updated>2007-03-12T11:40:42.111-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>Google Spamtop (er, Desktop)</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_x5PtJrE-t4M/RfV0KIaRrnI/AAAAAAAAAAQ/xA1KLi9zt0I/s1600-h/google_desktop_spam.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://1.bp.blogspot.com/_x5PtJrE-t4M/RfV0KIaRrnI/AAAAAAAAAAQ/xA1KLi9zt0I/s400/google_desktop_spam.png" alt="" id="BLOGGER_PHOTO_ID_5041063075316346482" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Kudos to Google Desktop for making it even easier for me to "save on  the Med$".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-874552887987693932?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/874552887987693932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=874552887987693932' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/874552887987693932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/874552887987693932'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2007/03/google-spamtop-er-desktop.html' title='Google Spamtop (er, Desktop)'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_x5PtJrE-t4M/RfV0KIaRrnI/AAAAAAAAAAQ/xA1KLi9zt0I/s72-c/google_desktop_spam.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-8563648136742450240</id><published>2007-02-03T16:45:00.000-05:00</published><updated>2007-02-03T16:47:32.887-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Programming is easy...</title><content type='html'>&lt;p&gt;...which is exactly why it's so &lt;a href="http://www.salon.com/books/int/2007/02/03/leonard/index.html"&gt;hard&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Business Guy asks for some feature X. Programmer starts implementing X. But before he's done, Business Guy comes back and says, "Hey, why don't you throw Y in there too. I mean, it won't be that hard, right?"&lt;/p&gt;&lt;p&gt;Later during the demo of features X and Y, manager watches and then says, "That's not exactly what I had in mind for Y. Also, we really need Z in there too."&lt;/p&gt;&lt;p&gt;If you could freeze the requirements imposed on a piece of software, like you can for a bridge or a power plant, then the programmers could finish those features. But the temptation to slip in new features is too great, because "Hey, it's easy, right?"&lt;/p&gt;&lt;p&gt;The best defense against this "feature creep" phenomenon is to have a visual metric that shows not just the progress toward the release goal, but also how the release goal grows in size with time. (A scrum burn-down chart is one good example of this). The chart communicates to all involved that new features (or revisions to existing features) don't come for free.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-8563648136742450240?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/8563648136742450240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=8563648136742450240' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/8563648136742450240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/8563648136742450240'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2007/02/programming-is-easy.html' title='Programming is easy...'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-116542533672422988</id><published>2006-12-06T12:11:00.000-05:00</published><updated>2006-12-06T12:16:17.516-05:00</updated><title type='text'>Transparent JVM clustering solutions will improve</title><content type='html'>Originally posted in the comments to &lt;a href="http://www.infoq.com/news/2006/12/terracotta-jvm-clustering"&gt;Terracotta open sources JVM clustering&lt;/a&gt;, in response to Cameron Purdy's comment:
&lt;blockquote&gt;Terracotta can cluster two servers (one hot, one standby) while Tangosol can run a couple thousand servers as "hot" (active + active + active + etc) in an n-way fully-connected mesh (virtual channels). Our server throughput in a 100-server system is 50x ...&lt;/blockquote&gt;
Ari made a great point in his &lt;a href="http://video.google.com/videoplay?docid=7660457673499305140&amp;amp;q=terracotta"&gt;Google Tech Talk presentation&lt;/a&gt; about how when Java first came out it felt like a step backward in terms of memory management, but with HotSpot and some bake time it became much better than a developer can manage explicitly at code-time. He went on to say that VM-level clustering will eventually be that way.

One can argue about how much scale-up work the Terracotta hub needs &lt;i&gt;today&lt;/i&gt;, but it's hard to dispute Ari's basic premise: that (1) the JVM can transparently replicate objects by watching the GET_FIELD and PUT_FIELD bytecodes, and (2) that you can insure thread safety across a cluster the same way you would in a single VM, as long as you can meet the contract of the MONITOR_ENTRY and MONITOR_EXIT bytecodes.

If, in 5-10 years, it becomes as automatic as garbage collection is today, that is a huge ideological victory for Ari, Terracotta, and the KISS principle. I think they're on to something very big.

For example, from &lt;a href="http://www.martinfowler.com/bliki/OOPSLA2005.html"&gt;Martin Fowler OOPSLA2005&lt;/a&gt;:
&lt;blockquote&gt;
"While I'm on the topic of concurrency I should mention my far too brief chat with Doug Lea. He commented that multi-threaded Java these days far outperforms C, due to the memory management and a garbage collector. If I recall correctly he said 'only 12 times faster than C means you haven't started optimizing'."
&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-116542533672422988?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/116542533672422988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=116542533672422988' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/116542533672422988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/116542533672422988'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2006/12/transparent-jvm-clustering-solutions.html' title='Transparent JVM clustering solutions will improve'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-115567628676726283</id><published>2006-08-15T16:59:00.000-04:00</published><updated>2006-08-15T17:13:08.660-04:00</updated><title type='text'>Beware of connection pools when swapping in HSQL DB in a Hibernate setting</title><content type='html'>I was trying to add support for HSQL DB to a project that already uses Hibernate. The app makes use of cascading inserts, but with HSQL in place it kept throwing a foreign key constraint violation. So I turned on log4j debug output for:
&lt;ul&gt;&lt;li&gt;org.hibernate.sql  (shows prepared statements)
&lt;/li&gt;&lt;li&gt;org.hibernate.type  (shows the values bound to and retrieved from the statements)&lt;/li&gt;&lt;/ul&gt;The debug output demonstrated that Hibernate was correctly inserting the referenced row before the one with the foreign key association. So why the failure?

The debug output also showed that the value bound to the foreign key column was "0". That value is obtained by doing the INSERT and then, in HSQL's case, issuing a "call identity()" statement, which retrieves the value of the last identity generated &lt;span style="font-style: italic;"&gt;in the current connection&lt;/span&gt;.

I remembered that I'm also using c3p0 connection pooling, so I changed the max pool size to 1 for the HSQL DB configuration, and that fixed the problem of "call identity()" returning 0. It turns out HSQL DB &lt;a href="http://www.hsqldb.org/doc/guide/guide.html#N10E5F"&gt;does not benefit from connection pooling&lt;/a&gt; anyway.

It remains to be seen what other problems that will cause...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-115567628676726283?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/115567628676726283/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=115567628676726283' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/115567628676726283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/115567628676726283'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2006/08/beware-of-connection-pools-when.html' title='Beware of connection pools when swapping in HSQL DB in a Hibernate setting'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-114947164676289734</id><published>2006-06-04T21:20:00.001-04:00</published><updated>2009-04-08T13:26:19.195-04:00</updated><title type='text'>Load a Spring Resource relative to your context file</title><content type='html'>In MemeStorm's &lt;a href="http://www.memestorm.com/blog/loading-resources-from-files-or-the-classpath/"&gt;Loading resources from files or the classpath&lt;/a&gt;, I left this comment:

&lt;blockquote&gt;I was surprised how hard it was to get XmlWebApplicationContext to use a resource root *other than* the root of the webapp. For security reasons you usually want your configuration files under /WEB-INF/, not the actual webapp root. But if you place them there, your app context XML file has to reference “/WEB-INF/db/init.sql” — which means that configuration file is no longer useful outside of a web container context.&lt;/blockquote&gt;

&lt;p&gt;In solving this problem, I initially tried to subclass XmlWebApplicationContext  to remember the directory it was loaded from and use that as its base when loading other resources. However, the loading of the actual context file was buried deep within the bowels of its code, making it difficult to retain knowledge of the directory it was loaded from without slicing through a deep chain of method signatures.

&lt;p&gt;I settled on a less elegant but quicker solution. I added a new bean property "contextRoot" that is prepended to every path passed into the getResourceByPath method.

&lt;pre name="code" class="java"&gt;public class ChrootXmlWebApplicationContext
 extends XmlWebApplicationContext {

 protected Resource getResourceByPath(String path) {
   return super.getResourceByPath( getContextRoot()
     + path.startsWith("/") ? path : "/" + path);
 }
 ...
}
&lt;/pre&gt;

&lt;p&gt;The contextRoot property is pulled from a servlet context init parameter the first time it is requested; trailing slashes are trimmed. When prepending it to a resource path, a slash is reapplied between the contextRoot and the requested path, if necessary, as shown above.

&lt;p&gt;By placing the context file in WEB-INF/ and setting contextRoot to "WEB-INF/" as well, you can mimic the behavior that would occur if the context was loading files relative to itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-114947164676289734?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/114947164676289734/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=114947164676289734' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114947164676289734'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114947164676289734'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2006/06/load-spring-resource-relative-to-your.html' title='Load a Spring Resource relative to your context file'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-114805516406245744</id><published>2006-05-19T12:10:00.001-04:00</published><updated>2009-04-08T13:24:15.769-04:00</updated><title type='text'>Connect once to a Dojo Topic</title><content type='html'>Scenario: a script that is loaded/evaluated inside a ContentPane needs to subscribe to a topic that is publishing URLs, receive exactly one URL, and then reload the pane.

Option 1. Look up the topic and connectOnce to its "sendMessage" method. (Note: scripts evaluated inside a ContentPane use the "_container_" variable to access it).&lt;pre name="code" class="js"&gt;
if(_container_)
  dojo.event.connectOnce(dojo.event.topic.getTopic("treeurl"), "sendMessage",
                         _container_, "setUrl");
&lt;/pre&gt;
Option 2. Use kwConnect with once:true.&lt;pre name="code" class="js"&gt;
if(_container_)
  dojo.event.kwConnect({
    once: true,
    srcObj: dojo.event.topic.getTopic("treeurl"),
    srcFunc: "sendMessage",
    adviceObj: _container_,
    adviceFunc: "setUrl"
  });&lt;/pre&gt;

Tested with Dojo 0.3 release.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-114805516406245744?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/114805516406245744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=114805516406245744' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114805516406245744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114805516406245744'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2006/05/connect-once-to-dojo-topic.html' title='Connect once to a Dojo Topic'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-114555672136502376</id><published>2006-04-20T14:09:00.000-04:00</published><updated>2006-05-25T10:41:07.336-04:00</updated><title type='text'>The AJAX-ifier's toolkit</title><content type='html'>Here are my favorite tools for AJAX development and debugging.
&lt;h3 id="InternetExplorer"&gt;Internet Explorer&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a class="ext-link" href="http://erik.eae.net/archives/2005/07/04/21.49.50/"&gt;&lt;span class="icon"&gt;&lt;/span&gt;Microsoft Script Editor&lt;/a&gt; – contains a full debugger for javascript, not to be underestimated – it even lets you set breakpoints (comes with MS Office, but you may need to rerun setup in order to get it installed; it will be at &lt;tt&gt;c:\program files\microsoft office\office11\MSE7.EXE&lt;/tt&gt; if you already have it) &lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.fiddlertool.com/"&gt;&lt;span class="icon"&gt;&lt;/span&gt;Fiddler Tool&lt;/a&gt; – sniffs your HTTP requests so you can really see what’s going on (download free at www.fiddlertool.com) &lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.microsoft.com/downloads/details.aspx?FamilyID=e59c3964-672d-4511-bb3e-2d5e1db91038&amp;DisplayLang=en"&gt;&lt;span class="icon"&gt;&lt;/span&gt;IE Web Developer Toolbar&lt;/a&gt; – get it for your sanity &lt;/li&gt;&lt;/ul&gt;&lt;h3 id="Firefox"&gt;Firefox&lt;a title="Link to this section" class="anchor" href="http://dev.agentlogic.com/projects/agentlogic/wiki/AjaxTools#Firefox"&gt;
&lt;/a&gt;&lt;/h3&gt; &lt;ul&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.joehewitt.com/software/firebug/"&gt;&lt;span class="icon"&gt;&lt;/span&gt;Firebug&lt;/a&gt; – shows you Javascript errors, XMLHttpRequests; lets you filter out CSS requests and other junk; also includes a DOM inspector with tag-search filter and a command console &lt;/li&gt;&lt;li&gt;&lt;a class="ext-link" href="http://chrispederick.com/work/webdeveloper/"&gt;Web Developer Extension&lt;/a&gt; – lets you toggle JS/CSS, change forms on the fly, clear cache, outline DOM elements, resize the window to emulate various resolutions, etc. (go to to get it)&lt;/li&gt;&lt;li&gt;&lt;a href="http://livehttpheaders.mozdev.org/"&gt;Live HTTP Headers&lt;/a&gt; - don't trust your intuition: use this to sniff requests and responses so you can see what URLs are really getting hit, what the cache and auth settings really are, etc.
&lt;/li&gt;&lt;/ul&gt;&lt;h3 id="Eclipse"&gt;Eclipse&lt;a title="Link to this section" class="anchor" href="http://dev.agentlogic.com/projects/agentlogic/wiki/AjaxTools#Eclipse"&gt;
&lt;/a&gt;&lt;/h3&gt; &lt;ul&gt;&lt;li&gt;&lt;a class="ext-link" href="http://www.interaktonline.com/Products/Eclipse/JSEclipse/Installation-Update/"&gt;&lt;span class="icon"&gt;&lt;/span&gt;JSEclipse plugin&lt;/a&gt; – syntax highlighting, code completion, etc. (add it to your Eclipse Update list by following the instructions) &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/12172455-114555672136502376?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/114555672136502376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=114555672136502376' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114555672136502376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114555672136502376'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2006/04/ajax-ifiers-toolkit.html' title='The AJAX-ifier&apos;s toolkit'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-114495196807908381</id><published>2006-04-13T13:59:00.000-04:00</published><updated>2006-04-13T14:21:14.900-04:00</updated><title type='text'>IE will cache CSS background images, if you give it a chance</title><content type='html'>You're using the latest CSS background-image trickery to get lots of cool effects on your web pages. But when you load the pages up in IE, the images seem to flicker when you move the mouse around over them. You never noticed it before when developing on your local machine, but it's painfully apparent when operating over a slow connection. The reason for this is that Internet Explorer 6 takes a conservative approach to caching, that is: &lt;span style="font-style: italic;"&gt;When in doubt, do not cache&lt;/span&gt;.

But IE will in fact cache your CSS background images -- even when it is configured to &lt;span style="font-style: italic;"&gt;Check for newer versions of stored pages [x] Automatically&lt;/span&gt; -- if you give it a chance.

The trick is to configure the web server to properly set the &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9"&gt;cache-control header&lt;/a&gt;. In order for images to get cached in Automatic mode, IE needs to see an HTTP header like this in the response:
&lt;blockquote style="font-weight: bold;"&gt; Cache-Control: max-age=86400&lt;/blockquote&gt;The number is the # of seconds to cache (24 hours in this case).

If you're using a Java server like Tomcat you will not get cache-control automatically, but you can &lt;a href="http://www.onjava.com/pub/a/onjava/2004/03/03/filters.html"&gt;do it with a servlet filter&lt;/a&gt;. If you're using Apache httpd then you can just turn on a &lt;a href="http://httpd.apache.org/docs/2.0/mod/mod_expires.html"&gt;configuration directive&lt;/a&gt;.

WARNING: If anything else on the server is setting the "no-cache" directive, then you may end up with a response header like this:
&lt;blockquote style="color: rgb(255, 0, 0); font-weight: bold;"&gt;  Cache-Control: max-age=86400, no-cache&lt;/blockquote&gt;That header says "cache me for 24 hours, but actually, don't cache me after all." So IE will panic and simply not cache it.

To verify you're getting the right cache-control header, inspect the response headers using &lt;a href="http://www.fiddlertool.com"&gt;Fiddler Tool&lt;/a&gt; in IE (fiddlertool.com) or the &lt;a href="http://livehttpheaders.mozdev.org/"&gt;livehttpheaders extension&lt;/a&gt; in Firefox.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-114495196807908381?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/114495196807908381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=114495196807908381' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114495196807908381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114495196807908381'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2006/04/ie-will-cache-css-background-images-if.html' title='IE will cache CSS background images, if you give it a chance'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-114488109554587292</id><published>2006-04-12T17:02:00.000-04:00</published><updated>2006-04-13T10:48:15.310-04:00</updated><title type='text'>Spring MVC and lost man-months</title><content type='html'>Like many others, I hopped on the Spring MVC bandwagon with sparkling eyes. Many futile developer-months later (both for me and the small team of developers I lead), I can step back and say definitively: Spring MVC is not fit for prime-time.

It purports to be a replacement for Struts, and it is. But Struts has many replacements, and the Spring MVC team has done the Java community a disservice by muddying the waters with their mildly evolutionary offering. I suspect it is because the Spring folks want to be a "one-stop shop" for J2EE developers: the more of their stack you use, the more time you will spend reading their books and attending their trainings -- the more valuable their brains become. The problem is, Spring MVC has a lot of problems under the hood.

I detail a partial list of gripes below. I will add to this list if/when I think of others.
&lt;h3&gt;Binding you down
&lt;/h3&gt;The form-binding mechanism provided in Spring MVC has proven to be a constant headache. A little searching through the &lt;a href="http://forum.springframework.org/forumdisplay.php?f=25"&gt;Spring Web Forum&lt;/a&gt; will convince you that no one really knows how to bind complex form elements to Javabean collections (List, Set, etc.). This one flaw will constantly bite you if you dare to use Spring MVC.

Someone may tell you to just use an array and save yourself some hassle (it's typed, etc.). Don't be fooled so easily: that may work for Spring, but not for Hibernate, which needs your collections to be interfaces, so that it can work its behind-the-scenes magic on them. (And chances are, if you're using Spring, you're probably using Hibernate too).

I have spent many hours sifting through the source code in the Eclipse debugger trying to understand what's going on during various binding snafus. The first strangeness came onscreen almost immediately after I attached the Eclipse debugger to Tomcat. The debugger kept halting at a curious line where an exception was being created for no apparent reason. It was the BindException, and it gets created every time the binding process occurs (hint: every single web request).

It turns out the BindException is central to the design of MVC. Every request causes a BindException. Hmm. All sorts of wonderful stuff gets stuffed inside the BindException.

Assuming you can get binding to work at all, you'll have the pleasure of using &amp;lt;spring:bind&amp;gt; in your JSP views. One real nicety is that it runs your property values through property editors so that you get an appropriately-formatted string value. This works great for viewing objects and presenting their properties as editable values.

Like every other web application in existence, mine also needed to be able to display objects in tables (the list view). And I needed those to display the same neatly-formatted values as you would see in the detail view. No problem, right? Just drop your &amp;lt;spring:bind&amp;gt; tags inside a &amp;lt;jsp:foreach&amp;gt; loop so that each row would have nicely-formatted values. For a simple collection where the concrete class of every element is identical and matches your controller's commandClass property, that works fine.

But the collection I needed to display was based on an interface type, where the concrete class of each element may vary. The &amp;lt;spring:bind&amp;gt; tag does not like that, because it is hard-wired to deal with only one command class in any given request, invariable at view-time. So we quickly hit a wall where we could not utilize property-editor-based display formatting for something so simple as a table.
&lt;h3&gt;Custom property editors, sort of
&lt;/h3&gt;Spring allows you to register custom property editors. It can also use the system property editors provided by the java.beans.Introspector. What it does not do is leverage BeanInfo classes very well. For example, if you provided BeanInfo classes (easily generated using doclets) then a PropertyDescriptor can have a propertyEditorClass. But Spring makes no attempt to use that class. This flaw cannot be understated: Spring promises to treat your command objects as JavaBeans, but then it ignores the BeanInfo classes that make JavaBeans somewhat plausible as a component framework. Without the ability to leverage BeanInfo classes your JavaBeans are just a fiesta of getters and setters.

So how do you get custom property editors for your beans? The Spring Way, of course:  by registering them manually, in the controller, when you handle the request.

That, combined with the single-class binding limitations described earlier, conspire to insure that a Spring controller has zero reusability -- you can make it work for exactly one concrete class, but don't get your hopes up about writing a controller that can be reused easily.
&lt;h3&gt;Resource "/WEB-INF/myconfig.xml" not found
&lt;/h3&gt;Let's say one of my beans wants to load a resource: "myconfig.xml". If I include it in my dispatcher servlet's web application context as an import, it will be loaded relative to the directory the spring context file is in (usually /WEB-INF). Now what if I want beans in my context to be able to load resources themselves, from that same directory? Sorry, no luck. The web application context resource loader pins its root at the webapp root, NOT the /WEB-INF subdirectory where the context and other configuration files are typically located. Furthermore, there is no easy way to modify this: it's buried deep within the helper classes that spring app contexts use to do the initial resource load. That means you have to specify your resources as "/WEB-INF/myconfig.xml" -- which then breaks when you try to run tests outside of a webapp container. Because when you're running tests, you're using a different application context class, and chances are it's loading resources relative to its own location. The alternative is to place all your configuration files in your webapp root, which may be accessible to end users. Probably not a good idea...

I cannot easily communicate the pain and frustration this has caused---that something so simple as a root resource path cannot be modified easily. I eventually subclassed web application context so that I could override the way it determines the resource root. The pain has subsided somewhat since then...
&lt;h3&gt;Spring.htm
&lt;/h3&gt;Okay, back to square one: let's learn Spring MVC by starting in the &lt;a href="http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step-Part-1.html"&gt;step-by-step tutorial&lt;/a&gt;. Throw some code here, paste some XML there, and voila, you have a controller available to you at /hello.htm.

Wait a second: /hello.htm? Does anyone but me find that strange that all your controllers are mapped via &lt;span style="font-weight: bold;"&gt;*.htm&lt;/span&gt;? Perhaps in some strange way it's a step up from the *.do typical of Struts lore. Or perhaps it's how we trick our users into thinking we're using static files created in Front Page. But what if we're outputting XML or JSON from the controller? What if we want to support content negotiation, or alternate representations, or worse, what if we have an honest-to-goodness static file that ends in *.htm? "Houston, we have a problem." Or at least a very misleading URL.

In adopting Spring MVC for my company's project, I first set out to wrest control over URLs by providing the capability to map to clean ones. Luckily, Spring MVC promises a lot of flexibility in mapping. You can write your own Handler classes and your own HandlerMapper classes. So I created one that maps regular expressions to controllers. Each controller can select a different view template based on the extension provided in the URL: so /myapp.xml and /myapp.html both go to the same controller, but get routed to a different view template.

Things got worse when I realized that URL mappings in web.xml are very limited. To get the flexibility I needed, the Spring dispatcher would have to capture "/*" in web.xml. That meant that static files would no longer be handled by the servlet container's default servlet. DANGER WILL ROBINSON! I  ended up writing a Servlet Filter to wrap the Dispatcher Servlet. This presented its own set of problems, as the Spring Dispatcher does not think it's running from within a servlet -- so if you're not careful you can end up with infinite forwarding loops when trying to forward to a view. I was able to eliminate that problem by having the servlet filter set a request attribute that basically says, "I was here already" and then ignore requests that it had already seen before.

In the end I was relatively happy with the way I can map URLs now. Unfortunately, I realize in retrospect that I pretty much rewrote the mapping system entirely. There are a lot of things I could still improve, but when all is said and done I would basically have my own web subframework written inside of Spring MVC.
&lt;h3&gt;Dog Food, and the eating of it&lt;/h3&gt;There is a saying Joel Spoelsky of joelonsoftware.com fame really likes: "Eat your own dog food." And Spring simply does not do this. You can probably read about the reasons why somewhere on their site:
&lt;blockquote&gt;&lt;a href="http://www.springframework.org/index.php"&gt;http://www.springframework.org/index.php&lt;/a&gt;
&lt;/blockquote&gt;
"No!" you say. "Not PHP! They probably just have their Spring dispatcher mapped to &lt;span style="font-weight: bold;"&gt;*.php&lt;/span&gt;!"

No, they really are using PHP.

Somewhere on the internet I remember reading their list of apologetic reasons for doing this: "use the right tool for the job" and all that. So which job is right for the Spring MVC tool? It's obviously not the right tool for "the leading full-stack Java/J2EE application framework" and perhaps it's not a good idea for you either.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-114488109554587292?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/114488109554587292/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=114488109554587292' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114488109554587292'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/114488109554587292'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2006/04/spring-mvc-and-lost-man-months.html' title='Spring MVC and lost man-months'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-12172455.post-112148445365143334</id><published>2005-07-15T22:41:00.001-04:00</published><updated>2009-04-08T13:28:05.454-04:00</updated><title type='text'>Janino in 30 seconds, using Jython</title><content type='html'>Janino looks powerful. It compiles expressions, scripts, classes, etc. either from source code or programmatically. But the code samples elicited my moan reflex.
&lt;ul&gt;

&lt;li&gt;The "Use Janino as an Expression Evaluator" &lt;a href="http://www.janino.net/use.html#expression_evaluator"&gt;code snippets&lt;/a&gt; look simple enough, but to really try them yourself, you need to: wrap in boilerplate, compile, tweak, execute, scratch head, add &lt;span style="font-family:courier new;"&gt;System.out.println()&lt;/span&gt; statements, &lt;em&gt;remix&lt;/em&gt; ... keep that cycle up and you'll know what's going on after a while.&lt;/li&gt;

&lt;li&gt;Or, just run the &lt;a href="http://www.janino.net/download.html#examples"&gt;example classes&lt;/a&gt; with their command-line syntax. Simple enough... but we're trying to learn an API here, right? Command-line switches insulate us from the API, which is the opposite of what we want.&lt;/li&gt;&lt;/ul&gt;Enter &lt;a href="http://www.jython.org/"&gt;Jython&lt;/a&gt;. The same code from the Expression Evaluator code snippets, &lt;em&gt;remixed&lt;/em&gt; here in shot-glass form:

&lt;pre name="code" class="python"&gt;D:\&amp;gt;jython
Jython 2.1 on java1.4.2_06 (JIT: null)
Type "copyright", "credits" or "license" for more information.
&amp;gt;&amp;gt;&amp;gt; from org.codehaus.janino import ExpressionEvaluator
&amp;gt;&amp;gt;&amp;gt; from java.lang import Integer
&amp;gt;&amp;gt;&amp;gt; ee = ExpressionEvaluator("c &gt; d ? c : d", Integer.TYPE, ["c","d"], [Integer.TYPE,Integer.TYPE] )
&amp;gt;&amp;gt;&amp;gt; ee.evaluate( [10,11] )
11
&amp;gt;&amp;gt;&amp;gt;&lt;/pre&gt;

Nice. I typed as many Janino lines as boilerplate lines. Four lines in, I'm already seeing useful results. That actually makes me want to, I don't know, evaluate another expression:

&lt;pre name="code" class="python"&gt;&amp;gt;&amp;gt;&amp;gt; ee.evaluate( [5,2] )
5
&amp;gt;&amp;gt;&amp;gt;&lt;/pre&gt;

It's going to be great fun exploring this API. ScriptEvaluator, ClassBodyEvaluator, Compiler, AST ...

Jython users, start your engines.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-112148445365143334?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/112148445365143334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=112148445365143334' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/112148445365143334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/112148445365143334'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2005/07/janino-in-30-seconds-using-jython.html' title='Janino in 30 seconds, using Jython'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-111963134862903817</id><published>2005-06-24T12:24:00.001-04:00</published><updated>2009-04-08T13:29:37.796-04:00</updated><title type='text'>script language="python" - in a web browser</title><content type='html'>An interesting discussion came up on comp.lang.python about adding python script support to web browsers. Some people were saying it can't be done because Python can't provide an adequate security sandbox. I wrote this post to clarify how the browser provides the sandbox for the scripting language.

&lt;pre&gt;
Newsgroups: comp.lang.python
Date: 24 Jun 2005 09:15:37 -0700
Local: Fri,Jun 24 2005 12:15 pm
&lt;b&gt;Subject: Re: Python as client-side browser script language&lt;/b&gt;

Sorry to resurrect a slightly older topic, but I want to clarify some points about how the browser DOM and the script language interact.

Paul Rubin wrote:
&gt; Huh?  The language itself has to provide the sandbox.
&gt; Remember that scripts have to be able to see
&gt; certain DOM elements but not others, and some of them have to be
&gt; read-only, etc.

Greg Ewing wrote:
&gt; If the DOM objects are implemented as built-in Python
&gt; types, there shouldn't be any difficulty with that.
&gt; Python objects have complete control over which attributes
&gt; can be read or written by Python code.

In web browsers, Javascript does not provide the sandbox. The browser provides scripts with certain objects like "document" or "window" but these are not native script objects at all. They're wrappers for browser-native objects, usually written in C/C++ and exposed via an ActiveX or XPCOM interface. (IE uses ActiveX, Mozilla uses XPCOM). The interfaces to these browser-native objects enforce the security model by restricting what the scripting language is allowed to do within the browser context.

If you attempt any foul play with a browser-native object, it can simply feed an exception back to the script wrapper, and your script code fails. That's the sandbox.

Following are some relevant links for those interested in further details:

&lt;b&gt;DOM Intro (especially the section, DOM vs Javascript): &lt;/b&gt;
"That is to say, [the script is] *written* in JavaScript, but it *uses* the DOM to access the web page and its elements."
&lt;a href="http://www.mozilla.org/docs/dom/domref/dom_intro.html"&gt;http://www.mozilla.org/docs/dom/domref/dom_intro.html&lt;/a&gt;

"Mozilla's DOM is coded almost entirely in C++. ... &lt;span style="font-style: italic;"&gt;When, in JavaScript, a client tries to access a DOM object or a DOM method on a DOM object, the JS engine asks XPConnect to search for the relevant C++ method to call&lt;/span&gt;."
&lt;a href="http://www.mozilla.org/docs/dom/mozilla/hacking.html"&gt;http://www.mozilla.org/docs/dom/mozilla/hacking.html&lt;/a&gt;

"The DOM makes extensive use of XPCOM. In fact, to do anything with the DOM implementation you need XPCOM."
&lt;a href="http://www.mozilla.org/docs/dom/mozilla/xpcomintro.html"&gt;http://www.mozilla.org/docs/dom/mozilla/xpcomintro.html&lt;/a&gt;

&lt;b&gt;Talking to XPCOM in Python &lt;/b&gt;
"&lt;span style="font-style: italic;"&gt;The techniques for using XPCOM in Python have been borrowed from JavaScript - thus, the model described here should be quite familiar to existing JavaScript XPCOM programmers&lt;/span&gt;."
&lt;a href="http://public.activestate.com/pyxpcom/tutorial.html"&gt;http://public.activestate.com/pyxpcom/tutorial.html&lt;/a&gt;

So in theory you should be able to create a python script interpreter, at least for Mozilla. In practice you'd either need to be an expert with the Mozilla source code, XPCOM, and Python... or you'd find yourself becoming an expert by necessity.
&lt;/pre&gt;

---

&lt;p&gt;Incidentally when people don't understand the difference between DOM
objects and Javascript objects, they end up with lots of memory leaks:
&lt;a href="http://jgwebber.blogspot.com/2005_01_01_jgwebber_archive.html"&gt;http://jgwebber.blogspot.com/2005_01_01_jgwebber_archive.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-111963134862903817?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/111963134862903817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=111963134862903817' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/111963134862903817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/111963134862903817'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2005/06/or-in-other-words-python-scripting-in.html' title='script language=&quot;python&quot; - in a web browser'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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-12172455.post-111348933041466474</id><published>2005-04-14T13:30:00.001-04:00</published><updated>2009-04-08T13:32:06.956-04:00</updated><title type='text'>Installing Debian Sarge on Dell PowerEdge SC 1425</title><content type='html'>I ran into problems, first with lack of hardware support for SATA drives, then with a real-time clock bug. Here are the steps that eventually worked.

&lt;p&gt;1. Boot from Sarge Installation CD-ROM

&lt;p&gt;2. At boot prompt, type &lt;span style="font-weight: bold;"&gt;linux26&lt;/span&gt; to load the boot-installer using the 2.6 kernel.
&lt;blockquote&gt;The default installer kernel, 2.4, doesn't include the &lt;span style="font-weight: bold;"&gt;ata_piix&lt;/span&gt; module, which is required to detect SATA hard drives.  (It's probably missing other modules as well).
  &lt;/blockquote&gt;

&lt;p&gt;3. Complete the first installation phase normally (partitioning, etc.).

&lt;p&gt;4. After the Debian Installer reboots, the console will freeze each time it tries to read the real-time hardware clock (RTC). Press &lt;span style="font-weight: bold;"&gt;CTRL+C&lt;/span&gt; each time to skip it. (For me it froze 3 times).

&lt;blockquote&gt;This is a confirmed Intel bug. There are different fixes (&lt;a href="http://lists.debian.org/debian-kernel/2004/10/msg00304.html"&gt;patch the kernel&lt;/a&gt;, maybe patch the BIOS, use a &lt;a href="http://linux.dell.com/distributions.shtml#debian"&gt;2.4 kernel with driver patches&lt;/a&gt;), but I present what I believe to be the simplest workaround. For details, continue on.&lt;/blockquote&gt;

&lt;p&gt;5. Open a second boot console as soon as you can by pressing &lt;span style="font-weight: bold;"&gt;ALT+F2&lt;/span&gt;. Log in as &lt;span style="font-weight: bold;"&gt;root&lt;/span&gt; with no password. (Debian Installer hasn't set a root password yet).

&lt;p&gt;6. Add the &lt;span style="font-weight: bold;"&gt;--directisa&lt;/span&gt; option to every hwclock call by creating a shell script in its place.

&lt;pre name="code" class="bash"&gt;# mv /sbin/hwclock /sbin/hwclock.bin
# vi /sbin/hwclock
----- contents -----
#! /bin/sh
/sbin/hwclock.bin --directisa $*
----- end -----
# chmod a+x /sbin/hwclock
&lt;/pre&gt;

&lt;p&gt;(Lifted from &lt;a href="http://zacbowling.com/archives/2004/10/06/debian-dell/"&gt;a comment by &lt;span style="font-weight: bold;"&gt;phg&lt;/span&gt; here&lt;/a&gt;, with one minor correction -- two dashes before directisa instead of just one).

&lt;p&gt;7. Type &lt;span style="font-weight: bold;"&gt;reboot&lt;/span&gt; at the prompt. After restart, the system should boot normally and continue into the Sarge boot-installer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12172455-111348933041466474?l=jeoftp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jeoftp.blogspot.com/feeds/111348933041466474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=12172455&amp;postID=111348933041466474' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/111348933041466474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/12172455/posts/default/111348933041466474'/><link rel='alternate' type='text/html' href='http://jeoftp.blogspot.com/2005/04/installing-debian-sarge-on-dell.html' title='Installing Debian Sarge on Dell PowerEdge SC 1425'/><author><name>Jeoff Wilks</name><uri>http://www.blogger.com/profile/15580776653929196013</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>
