tag:blogger.com,1999:blog-121724552024-03-13T23:07:52.705-07:00JeoFTPDesigning the future of software.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.comBlogger37125tag:blogger.com,1999:blog-12172455.post-42244193627900237752013-09-03T12:17:00.003-07:002013-09-03T12:23:52.847-07:00Marriott and the Unexpected Non-Bonus: A Case Study in Bad Customer Experience<span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">This just in from the <i>Bad Customer Experiences Dept.</i> (Yes, it's next door to the Bad User Experience Dept.)</span><br />
<span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><br />
</span> <span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">After </span><a href="http://www.customerservicescoreboard.com/Marriott+Rewards">waiting on hold with Marriott Rewards for 30 minutes</a><span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">, I learned that they didn't credit any points for my June 5-9 stay at the JW Marriott Ihilani in Hawaii. In fact, as far as their records are concerned, I didn't even stay there at all: I simply parked in their parking garage a few nights and ate a few meals. In their words, it was an "incidental stay" only. Funny, I remember booking an actual room and sleeping in it 4 nights... and paying about $1600 for the privilege. </span><br />
<span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><br />
</span> <span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">I fell victim to the lure of convenience and booked a flight+hotel package through <a href="http://www.alaskaair.com/content/vacations/packages.aspx">Alaska Airlines Vacations</a>. </span><span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">(The actual booking was $2,946.88, of which about $350 was rental car and $1,000 was airfare for two.)</span><br />
<span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><br />
</span> <span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><b>Fool me once:</b> Marriott decided this stay is not eligible for points. I suppose their reasoning is that since I "only" paid about $400/night instead of the usual $450, their points budget should justifiably drop from <b>10</b> x 450 x 4 = 18000 points all the way down to <b>0</b> x 400 x 4 = 0 points. This is exemplary MBA math that equates to terrible customer experience.</span><br />
<span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><br />
</span> <span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">Unfortunately, they never notified me of this, and I didn't scrutinize my rewards account activity closely enough to notice something was amiss. So that set me up for the next big disappointment.</span><br />
<br />
<span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><b>Fool me twice:</b></span><span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><b> </b>I booked a weekend trip to Victoria a couple weekends ago. I logged into my Marriott Rewards account at that time and noticed the</span><span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"> </span><a href="http://news.marriott.com/2013/05/stays-add-up-to-free-nights-with-marriott-rewards-unexpected-bonus-promotion.html">Unexpected Bonus promotion</a><span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"> showed 1 stay; at 2 stays I would receive a free night certificate. So my wife and I turned down a second night in the fabulous <a href="https://www.deltahotels.com/Hotels/Delta-Victoria-Ocean-Pointe-Resort-Spa">Delta Victoria Ocean Pointe</a> to stop in at the Marriott and score our "unexpected" bonus. </span><br />
<span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><br />
</span> <span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">Unfortunately, since the Hawaii stay was never actually credited as a stay, my unexpected bonus turned into an </span><b style="color: #222222;">Unexpected Non-Bonus</b><span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">.</span><br />
<span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;"><br />
</span> <span style="background-color: white; color: #222222; font-family: arial; font-size: x-small;">The joke's on me for assuming the customer comes first at Marriott.</span>Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-27398546575269686852012-05-01T23:16:00.000-07:002013-04-04T16:47:59.446-07:00IBM BPM V8 brings new Coach designer and live collaborationI led the design for IBM BPM 8.0. We demonstrated it live to an audience of about 8,000 people at IBM's Impact 2012 conference. On seeing the demo, <a href="http://www.brsilver.com/2012/05/01/bpm-at-ibm-impact/">Bruce Silver wrote</a>:<br />
<br />
<blockquote class="tr_bq">
"More impressive to me is the new task UI (“Coach”) designer, which features reusable composite controls that dramatically simplify authoring of complex task user interfaces without so much javascript and css code. For example, a data entry and a graph control can both point to the same data and communicate with each other automatically without scripting. IBM has also carried forward real-time collaborative editing from Blueworks Live into the Coach designer. Very cool."</blockquote>
<br />
Lots of interesting examples are starting to land on the BPM Community Samples site. Start with <a href="http://bpmwiki.blueworkslive.com/display/samples/Coach+Bonus+toolkit">Coach Bonus toolkit</a> (link requires registration) which includes the live data examples Bruce was writing about.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com1tag:blogger.com,1999:blog-12172455.post-58796052592128254862012-01-05T14:37:00.000-08:002013-04-04T16:44:27.363-07:00Web Development for the fun of itRight now I'm co-leading Product Design for <a href="http://www-01.ibm.com/software/info/bpm/">IBM BPM</a>. 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.<br />
<br />
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.<br />
<br />
<a href="https://github.com/thoughtbot/bourbon#readme">Bourbon</a> makes it reeeeally easy to write modern CSS3, hiding all the convoluted vendor-specific repetitive ugliness from you. It's built on <a href="http://sass-lang.com/">sass</a>, whose mission was basically to fix CSS's stuttering problem. Finally, writing CSS is actually fun again!<br />
<br />
<b>"But Jeoff, what about IE, I thought it can't do CSS3?"</b> No problem, even Internet Elephant (IE) can be made to dance with CSS3 gradients, rounded corners, and shadows: it's as easy as <a href="http://css3pie.com/">PIE</a>! Also, check out <a href="http://flexiejs.com/">flexie</a>, 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.<br />
<br />
Now that you're ready to write some CSS3, <a href="http://graphicpeel.com/cssiosicons">check out what's possible</a>!<br />
<br />
p.s. Yes, I'm <a href="http://jeoftp.blogspot.com/2010/01/why-i-left-gwt-and-came-crawling-back.html">still using jquery</a>. I agree with the SproutCore folks that <a href="http://blog.sproutcore.com/giving-back-to-jquery/">jQuery has become the standard library of the web</a>.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com1tag:blogger.com,1999:blog-12172455.post-60482876505237046862010-10-20T14:00:00.000-07:002013-04-04T16:48:21.882-07:00Long live the quirks-mode box model!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:<br />
<br />
<pre class="css" name="code">* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
</pre>
<br />
<a href="http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug#Support_for_Internet_Explorer.27s_box_model">From wikipedia</a>:<br />
<blockquote>
<span class="Apple-style-span" style="font-family: sans-serif; font-size: 13px; line-height: 19px;"></span><br />
<div style="line-height: 1.5em; margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.4em;">
<span class="Apple-style-span" style="font-family: sans-serif; font-size: 13px; line-height: 19px;">Web designer Doug Bowman has said that the original Internet Explorer box model represents a better, more logical approach.<sup class="reference" id="cite_ref-17" style="font-style: normal; font-weight: normal; line-height: 1em;"><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;">[18]</a></sup> 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.<sup class="reference" id="cite_ref-CSS2_-_Box_model_tweaking_9-1" style="font-style: normal; font-weight: normal; line-height: 1em;"><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;">[10]</a></sup> 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.<sup class="reference" id="cite_ref-18" style="font-style: normal; font-weight: normal; line-height: 1em;"><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;">[19]</a></sup> 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.<sup class="reference" id="cite_ref-19" style="font-style: normal; font-weight: normal; line-height: 1em;"><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;">[20]</a></sup></span></div>
<div style="line-height: 1.5em; margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 0.4em;">
<span class="Apple-style-span" style="font-family: sans-serif; font-size: 13px; line-height: 19px;">The W3C has included a "box-sizing" property in CSS3. When <code style="background-color: #f9f9f9; font-family: monospace, sans-serif;">box-sizing: border-box;</code> is specified for an element, any padding or border of the element is drawn <i>inside</i> the specified width and height, "as commonly implemented by legacy HTML user agents".<sup class="reference" id="cite_ref-20" style="font-style: normal; font-weight: normal; line-height: 1em;"><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;">[21]</a></sup> <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">Internet Explorer 8</a>, <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)">Opera</a> 7.0 and later, and <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">Konqueror</a> 3.3.2 and later support the CSS3 box-sizing property. <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)">Gecko-based</a> browsers such as <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">Mozilla Firefox</a> support the same functionality using a <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">proprietary</a> "-moz-box-sizing" property,<sup class="reference" id="cite_ref-21" style="font-style: normal; font-weight: normal; line-height: 1em;"><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;">[22]</a></sup> and <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">WebKit</a> browsers such as <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)">Apple Safari</a> and <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">Google Chrome</a> support it as a proprietary "-webkit-box-sizing" property.<sup class="reference" id="cite_ref-22" style="font-style: normal; font-weight: normal; line-height: 1em;"><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;">[23]</a></sup></span></div>
</blockquote>
<blockquote>
</blockquote>
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-11444666185316876682010-01-15T09:00:00.009-08:002010-01-15T09:47:11.914-08:00jquery poised to gain on GWT's compiler, thanks to, um ... Google<p>Buried in the <a href="http://jquery14.com/day-01/jquery-14">jquery 1.4 release announcement</a>, under the Miscellaneous section, is the following tidbit:
<blockquote>Now use Closure Compiler instead of YUI Min (<a href="http://github.com/jquery/jquery/commit/3fd62eae9df3159fc238a515bb748140a942313d">commit</a>)</blockquote>
<p>So what is Closure Compiler? According to <a href="http://code.google.com/closure/compiler/">the project page at Google Labs</a>,
<blockquote>"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."</blockquote>
<p>According to the commit message, switching from YUI Compressor to Google Closure Compiler reduced the filesize by 13% with no other changes.
<h3>But wait, there's more!</h3>
<p>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 <a href="http://code.google.com/closure/compiler/docs/api-tutorial3.html">Advanced Optimizations</a>. With advanced optimizations enabled, Closure Compiler also:
<p><ol><li>renames global variables and function names to make them shorter, </li><li>removes unused functions, and </li><li>inlines references where possible, including function calls, constants, and variables.</li></ol>
<h3>The catch is...</h3>
<p>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.
<p>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.
<p>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.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com4tag:blogger.com,1999:blog-12172455.post-25792156483942898882010-01-05T14:04:00.007-08:002013-04-04T16:48:47.166-07:00The best Javascript toolkit is...The contenders are dojo, yui, jquery, mootools, gwt, scriptaculous. The votes are in. The people have spoken. The best Javascript toolkit, by popularity, is....
<a href="http://www.google.com/trends?q=jquery%2C+extjs%2C+gwt%2C+yui%2C+dojo%2C+mootools%2C+scriptaculous&ctab=0&geo=all&date=all&sort=1" style="text-decoration: none;">
<img src="http://www.google.com/trends/viz?q=jquery,+extjs,+gwt,+yui,+dojo,+mootools,+scriptaculous&date=all&geo=all&graph=weekly_img&sort=1&sa=N" />
</a><br />
<br />
<a href="http://www.google.com/trends?q=jquery%2C+extjs%2C+gwt%2C+yui%2C+dojo%2C+mootools%2C+scriptaculous&ctab=0&geo=all&date=all&sort=1" style="text-decoration: none;">jquery.</a><br />
<br />
<br />
<br />
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com1tag:blogger.com,1999:blog-12172455.post-16268576901051127142010-01-05T12:11:00.009-08:002013-04-04T16:49:48.835-07:00Why I left GWT and came crawling back to jquery<a href="http://jquery.com/">jquery</a>, I'm sorry I said those <a href="http://jeoftp.blogspot.com/2009/02/jquery-rocks-now-back-to-gwt.html">mean things</a> about you. After all the good times we had together, I walked away the moment GWT batted its eyes at me.
<br />
<br />
We got along okay at first, GWT and me. But then I started running into <a href="http://groups.google.com/group/google-web-toolkit/browse_thread/thread/6ad355abb12505f6/fd7078e216c66ac9?hl=en&ie=UTF-8&oe=utf-8&q=gwt+jeoffwilks#fd7078e216c66ac9">problems with the simplest things</a> due to GWT's convoluted overuse of container markup for things like text and links. Trudging on, I encountered <a href="http://code.google.com/p/google-web-toolkit/issues/detail?id=2802">vexing UI layout bugs</a> 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 <a href="http://development.lombardi.com/?p=253#comment-1472">how many containers</a> 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.
<br />
<br />
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.
<br />
<h4>
The two faces of GWT-RPC</h4>
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 <a browse_thread="" c768d8d33bfb1dc="" ee0a3e809813e24a="" google-web-toolkit-contributors="" group="" groups.google.com="" hl="en&ie=UTF-8&oe=utf-8&q=gwt+gae+dto" href="http://www.blogger.com/blogger.g?blogID=12172455" http:="" thread="">nasty RPC problems</a> 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.
<br />
<br />
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.
<br />
<br />
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.)
<br />
<h4>
Closures</h4>
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...
<br />
<br />
Java has a degree of elegance when compared to C++, but not next to Javascript: closures save time. Hip companies like Apple <a href="http://developer.apple.com/mac/articles/cocoa/introblocksgcd.html">know this</a>. Maybe someday <a href="http://blogs.sun.com/mr/entry/closures_qa">Sun will catch up</a>.
<br />
<h4>
Here's your host.... (loading)</h4>
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?
<br />
<br />
Dump GWT-RPC and rewrite my own RPC mechanism? Not a bad idea....
<br />
<br />
And so I did.
<br />
<br />
Using jquery.
<br />
<h4>
JSON, JSOFF</h4>
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.
<br />
<br />
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.)
<br />
<br />
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.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com11tag:blogger.com,1999:blog-12172455.post-39962982363848259642009-12-16T13:28:00.001-08:002013-04-04T16:50:32.026-07:00IBM Acquires LombardiLombardi, my employer, announced this morning that they have been <a href="http://www.lombardisoftware.com/ibm_to_acquire_lombardi.php">acquired by IBM</a>.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-30768019932412093672009-07-06T12:47:00.004-07:002013-04-04T16:50:49.739-07:00Tweaks to Zach's humane Date scriptI like <a href="http://www.zachleat.com/web/2008/03/23/yet-another-pretty-date-javascript/">Zach's humane Date script</a> (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:
<br />
<pre class="js" name="code">
/*
* 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 < 0) {
seconds = Math.abs(seconds);
token = ' from now';
}
while (format = time_formats[i++]) {
if (seconds < 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 > 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 && jQuery(this).text() != date) // don't modify the dom if we don't have to
jQuery(this).text(date);
});
};
}
</pre>
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com1tag:blogger.com,1999:blog-12172455.post-28610276497062134662009-04-17T18:44:00.007-07:002013-04-04T16:51:35.675-07:00JSON AnnotationsSuppose you want a generic parser that will turn <a href="http://json.org/">JSON</a> 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. <b>What if those type hints were embedded within the JSON itself?</b>
<br />
<h4>
Option 1. Processing Instructions</h4>
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.
<br />
<pre class="js" name="code">{ "meta": "@"
"data": {
"@type": "java.util.HashMap",
"Joe" : {
"@type": "jeoftp.Person"
"firstName": "Joseph",
"lastName": "Robinson",
"age": 45,
"birthday": new Date(23493822934)
},
"Sue" : {
"@type": "jeoftp.Person"
...
}
}
}
</pre>
<h4>
Option 2. Wrap Typed Objects with Annotations</h4>
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.
<br />
<pre class="js" name="code">{
"@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" : { ... }
}
}
}
</pre>
<h4>
Option 3. Embed Annotations in Comments</h4>
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.
<br />
<pre class="js" name="code">{
/* @type java.util.HashMap */
"Joe" : {
/* @type jeoftp.Person */
"firstName": "Joseph",
"lastName": "Robinson",
"age": 45,
"birthday": new Date(23493822934)
},
"Sue" : {
/* @type jeoftp.Person */
...
}
}
</pre>
If your type parser was smart enough, perhaps you could even handle generics:
<br />
<pre class="js" name="code">{
/* @type java.util.HashMap<java .lang.string="" org.jeoftp.person=""> */
"Joe" : {
"firstName": "Joseph",
"lastName": "Robinson",
"age": 45,
"birthday": new Date(23493822934)
},
"Sue" : {
...
}
}
</java></pre>
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.<br />
<br />
<br />
<br />
<br />
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-34997124921821418612009-03-23T08:10:00.008-07:002013-04-04T16:52:11.320-07:00Determine the Java TimeZone of your web visitors using client-side javascriptYou want your Java web application to know what <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/TimeZone.html">TimeZone</a> your visitors are in. You can use client-side javascript code like the following to produce a timezone string that Java can understand:
<br />
<pre class="js" name="code">function getTimezone() {
var tzo = new Date().getTimezoneOffset(); //returns timezone offset in minutes
function pad(num, digits) {
num = String(num); while (num.length < digits) { num="0"+num; }; return num;
}
return "GMT" + (tzo > 0 ? "-" : "+") + pad(Math.floor(tzo/60), 2) + ":" + pad(tzo%60, 2);
}
</pre>
This will produce strings like <b>GMT-04:00</b> and <b>GMT-07:00</b> which you can then pass up to the server and into <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)">TimeZone.getTimeZone()</a> to produce a TimeZone object.<br />
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com3tag:blogger.com,1999:blog-12172455.post-66020237508118349352009-03-19T20:04:00.012-07:002009-09-19T05:18:55.839-07:00Timing Oracle SQL queries with Glassbox<p><a href="http://www.glassbox.com/">Glassbox</a>, 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.
</p><p>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.
</p><p>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.
</p><p>I became interested again over the past couple days, so I contacted <a href="http://rbodkin.blogs.com/">Ron Bodkin</a> 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!
</p><p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_x5PtJrE-t4M/ScMLd0Qn4LI/AAAAAAAAAw4/r55FapiCoko/s1600-h/glassbox_slow_database.png"><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" /></a>
</p><p>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.</p>
<h3>The cookbook</h3>
<p>First, grab <a href="http://downloads.sourceforge.net/glassbox/glassbox.war">Glassbox 2.0</a> and <a href="http://www.eclipse.org/downloads/download.php?file=/tools/aspectj/aspectj-1.5.3.jar">AspectJ 1.5.3</a>. (To do offline weaving you'll need aspectjtools.jar, which doesn't ship with Glassbox). </p>
<p>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.</p>
<p>Finally, run the following ant script:</p>
<textarea name="code" style="width:100%" rows="10" class="xml">
<project name="glassbox-instrument-oracle" default="compile">
<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
<classpath>
<pathelement location="./lib/aspectjtools.jar" />
</classpath>
</taskdef>
<target name="compile">
<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" />
</target>
</project>
</textarea>
<p>Note: Adjust your path to AspectJ as needed.</p>Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-46237693204207072562009-03-19T18:02:00.003-07:002009-03-19T18:20:17.590-07:00Things to study when I get a minuteSome of the things I'd like to spend more time with:
<ul><li> <a href="http://www.eclipse.org/aspectj/">AspectJ</a>, <a href="http://www.eclipse.org/ajdt/">AJDT</a>, <a href="http://www.glassbox.com/">Glassbox</a> - AOP supertools
</li><li> <a href="http://www.aptana.com/jaxer">Jaxer</a>, <a href="http://www.axiomstack.com/">Axiom Stack</a>, <a href="http://helma.org/">Helma</a>, <a href="http://appjet.com/">AppJet</a> - web platforms that leverage the symmetry of using javascript on both the server and client side
</li><li> <a href="http://lucene.apache.org/java/docs/">Lucene</a>, <a href="http://en.wikipedia.org/wiki/Berkeley_DB">Berkeley DB</a> - faster than a database, given the right use case</li><li><a href="http://www.terracotta.org/">Terracotta</a> - enforces java memory model across a grid of servers</li></ul>Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-65456980711136127592009-03-17T12:34:00.006-07:002009-04-08T09:58:25.596-07:00Reason #82734 why I hate Oracle JDBC drivers<p>Are you wondering why the date/time values you read from Oracle 10g's jdbc driver are <a href="http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.html#08_03">off by several hours?</a> It's because the 9i/10g drivers don't properly account for the jdbc client's time zone when writing the date value.
<h3>So what's the fix?</h3>
<p>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.
<pre name="code" class="java">
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());
}
}
</pre>Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-63465148910319044262009-02-12T06:28:00.005-08:002013-04-04T16:52:26.892-07:00jquery rocks! now back to GWT<b>Update:</b> read the followup to this post, <a href="http://jeoftp.blogspot.com/2010/01/why-i-left-gwt-and-came-crawling-back.html">Why I left GWT and came crawling back to jquery</a>.
<br />
I just finished a project that includes a rich, complex web interface built on <a href="http://jquery.com/">jquery</a>, and now it's time to report the good, the bad, and the ugly.
<br />
<h4>
The Good</h4>
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.
<br />
<h4>
The Bad</h4>
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.
<br />
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.
<br />
<h4>
The Ugly</h4>
In reaching for the ideal of progressive enhancement, jquery performs weakly in one important feature of traditional software engineering: <b>encapsulation</b>. 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.
<br />
<h4>
Back to GWT</h4>
I'm starting a new project now, and I'll be doing this one on <a href="http://code.google.com/webtoolkit/">GWT</a>. 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.
<br />
Fear not, jquery, my friend. We have built great things together, and there <em>will</em> be other times.<br />
<br />
<br />
<br />
<br />
<br />
<br />
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com8tag:blogger.com,1999:blog-12172455.post-29189207063823621652009-02-12T06:21:00.004-08:002009-02-12T06:28:01.570-08:00Review and annotate PDF files without Adobe messiness<p>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!
<p>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.
<p>The quick solution: grab <a href="http://www.docu-track.com/downloads/users/">PDF-XChange Viewer</a> (which allows mere mortals to annotate any PDF document), and leave the Adobe mess for someone else.
<p>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.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-6751957289656287982009-01-22T11:43:00.004-08:002013-04-04T16:52:43.763-07:00Beware jquery's :nth-child selectorThe past day or two I've been using selectors like this to manipulate tabular data:
<br />
<ul>
<li> $("table#summary tr:nth-child("+k+")")</li>
</ul>
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.
<br />
So I cranked on the <a href="http://www.getfirebug.com/">Firebug</a> profiler and profiled the function that does the heavy lifting. Calls to nth-child were taking up 4700ms out of about 5000ms total!
<br />
After staring at it for a minute, I recalled that <a href="http://docs.jquery.com/Selectors/nthChild#index">:nth-child</a> looks for all elements that are the nth child of their parent -- that's what I want, right? -- but it <em>does not assume that they are direct descendants of the preceding element in the selection chain</em>.
<br />
<blockquote>
"Matches all elements that are the nth-child of their parent ... While :eq(index) matches only a single element, this matches more than one ..."</blockquote>
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:
<br />
<ul>
<li> $("table#summary tr:<b>eq</b>("+k+")")
</li>
<li> $("table#summary tr")<b>.eq</b>(k)</li>
</ul>
Once I made that change, Firebug's Profiler timing went from 5000ms+ down to the 300-400ms range. Lesson learned.<br />
<br />
<br />
<br />
<br />
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com3tag:blogger.com,1999:blog-12172455.post-58291361935737604332009-01-19T12:22:00.010-08:002009-04-08T10:03:01.228-07:00Searching tables with jquery<p>Suppose you have an html table like the one below (name, favorite color) and you want to grab all the favorite colors.
<table class="jt">
<thead>
<tr><th>Name</th><th>Favorite Color</th></tr>
</thead>
<tbody>
<tr><td>Joe</td><td>blue</th></tr>
<tr><td>Sally</td><td>green</th></tr>
<tr><td>Mike</td><td>yellow</th></tr>
<tr><td>Ziggy</td><td>orange</th></tr>
</tbody>
</table>
<p>Here are some jquery plugin functions that let you do it:
<pre name="code" class="js">
/** find the index of this node among its siblings **/
jQuery.fn.childn = function() { return 1 + jQuery(this).parent().find("> *").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("> *:nth-child("+idx+")");
}
</pre>
<p>Example:
<pre name="code" class="js:nocontrols">
$("table tbody tr").column("Favorite Color");
</pre>
<p><b>Update:</b> use :eq instead of :nth-child! See my <a href="http://jeoftp.blogspot.com/2009/01/beware-jquerys-nth-child-selector.html">follow-up post</a> for details.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-58027197485551951702008-08-16T08:24:00.003-07:002008-09-27T14:26:46.042-07:00How to construct immutable objects using reflection<a href="http://www.javaworld.com/javaworld/jw-07-2008/jw-07-harmful-idioms.html?page=3">John O'Hanley argues</a> 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 <a href="http://www.web4j.com/Java_Web_Application_Framework_Overview.jsp#NoORMMapping">and ORM</a> 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.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com2tag:blogger.com,1999:blog-12172455.post-78438680187214579502007-12-26T13:38:00.000-08:002013-04-04T16:53:02.696-07:00RulePoint and RTAMHere is a good description of the products I worked on while at Agent Logic:
<span style="font-weight: bold;">Agent Logic’s RulePoint and RTAM</span><br />
<a href="http://www.column2.com/2007/12/agent-logics-rulepoint-and-rtam/">http://www.column2.com/2007/12/agent-logics-rulepoint-and-rtam/</a>
I did the designs for both products and was the lead developer on RulePoint through the first 2 releases.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-76514979389662751662007-09-14T14:23:00.000-07:002013-04-04T16:54:39.485-07:00Column-oriented databases. Yawn.WOW, just <a href="http://www.databasecolumn.com/2007/09/one-size-fits-all.html">rotate your thinking 90 degrees</a> and everything gets faster!<br />
<br />
An inline explanation of what it means to be a column-oriented database would have been nice. For that I referred to wikipedia's <a href="http://en.wikipedia.org/wiki/Column-oriented_DBMS">Column-oriented DBMS</a> page.<br />
<br />
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).<br />
<br />
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.Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-5689270142126116732007-08-14T10:24:00.000-07:002013-04-04T16:55:50.397-07:00This irony brought to you by Google<a href="http://3.bp.blogspot.com/_x5PtJrE-t4M/RsHlrfvOrJI/AAAAAAAAABM/Bapi1uwOtgY/s1600-h/java_ms_office.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5098608788576840850" src="http://3.bp.blogspot.com/_x5PtJrE-t4M/RsHlrfvOrJI/AAAAAAAAABM/Bapi1uwOtgY/s400/java_ms_office.png" style="cursor: pointer; float: left; margin: 0pt 10px 10px 0pt;" /></a><br />
Google informed me today that <span style="font-style: italic;">java.exe is a process belonging to Microsoft Office!</span> See for yourself in the screenshot at left. (hint: the ad with the red box outline).<br />
<br />
Oh and by the way, <span style="font-style: italic;">Google needs Java experts!</span> Be sure to remember during your Google interview that java.exe is a process belonging to Microsoft Office...Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-15025212012991807932007-07-26T11:05:00.001-07:002013-04-04T16:55:17.854-07:00A simple LRU cacheHere it is. (Note: apply external locking when thread-safety is required).
<br />
<pre class="java" name="code">int maxEntries = 50;
java.util.Map lruCache =
new java.util.LinkedHashMap(maxEntries, .75F, true) {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > maxEntries
}
};
</pre>
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-69304580667276238992007-06-27T10:09:00.005-07:002013-04-04T16:55:24.896-07:00jwhich - Java classes can run but they can no longer hideThis bash command will search all .jar files in the current directory and all subdirectories for MyClass:
<br />
<pre class="brush: bash; toolbar: false">for f in `find <b>.</b> -name '*.jar'`; do jar tf $f | grep <b>"MyClass"</b> | xargs -n 1 echo "$f:"; done</pre>
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.
<br />
<span style="font-weight: bold;">Sample output:</span><br />
<pre>$ jwhich MyClass
./myapp/my/hard/to/find/subdirectory/mymissing.jar: com/Missing/MyClass.class
$</pre>
<span style="font-weight: bold;">The script itself:</span>
<pre class="brush: bash">#!/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
</pre>
<br />
<br />
Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0tag:blogger.com,1999:blog-12172455.post-82686801581011589992007-06-21T07:15:00.001-07:002009-04-08T10:23:12.390-07:00A simple servlet Filter for JDBC ConnectionsHere 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 <a href="http://www.hibernate.org/43.html">open-session-in-view pattern</a>, but in this case it is more correctly described as open-<i>connection</i>-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.
<pre name="code" class="java">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
* <code>request.getAttribute(ConnectionProviderFilter.CONNECTION_ATTR)</code>
* to access the Connection.
*
* @author <a href="mailto:jeoffwilks@gmail.com">Jeoff Wilks</a>
*/
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();
}
}
}
}
</pre>Anonymoushttp://www.blogger.com/profile/15580776653929196013noreply@blogger.com0