<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="W/&quot;D0YCSXoyeip7ImA9WxNWFE0.&quot;"><id>tag:blogger.com,1999:blog-10814242</id><updated>2009-10-13T05:39:28.492+02:00</updated><title>The 90th percentile</title><subtitle type="html">Ignacio Coloma on tech and the remaining 10%</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/" /><link rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>67</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><link rel="self" href="http://icoloma.blogspot.com/feeds/posts/default" type="application/atom+xml" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ficoloma.blogspot.com%2Ffeeds%2Fposts%2Fdefault" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ficoloma.blogspot.com%2Ffeeds%2Fposts%2Fdefault" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bloglines.com/sub/http://icoloma.blogspot.com/feeds/posts/default" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ficoloma.blogspot.com%2Ffeeds%2Fposts%2Fdefault" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ficoloma.blogspot.com%2Ffeeds%2Fposts%2Fdefault" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry gd:etag="W/&quot;Dk8ARXg5fCp7ImA9WxNQEEQ.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-4311599076489044218</id><published>2009-09-16T11:55:00.002+02:00</published><updated>2009-09-16T11:54:04.624+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-16T11:54:04.624+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><category scheme="http://www.blogger.com/atom/ns#" term="cache" /><category scheme="http://www.blogger.com/atom/ns#" term="web" /><title>Caching user-supplied images in the browser</title><content type="html">We have been engaged in a big project for the last months. Despite of the nice performance marks, there was a warning in Google PageSpeed that was itching us badly: "The following resources are missing a cache expiration".&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img alt="It's all about speed" src="http://2.bp.blogspot.com/_5fcKwF7jItc/SrCvM9DoXeI/AAAAAAAAAYI/dB92IhmgAhQ/s800/its-all-about-speed.jpg" title="It's all about speed" border="0" /&gt;&lt;/div&gt;&lt;br /&gt;PageSpeed was complaining about images uploaded by the user. Resources referenced from CSS are conveniently handled by Loom, but user-provided files were not setting their cache headers appropriately.&lt;br /&gt;&lt;br /&gt;The following is, to the best of my knowledge, the same algorithm implemented by blogger and wordpress to avoid delivering the same image file over and over again. The theory has been &lt;a href="http://www.slideshare.net/icoloma/caching-web-contents-in-the-browser-999134"&gt;already covered here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I will be using Loom in this example, but you can reproduce this using any web framework.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Choosing your Strong Cache Validator&lt;/h2&gt;&lt;br /&gt;Cache Validators are introduced in the &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3"&gt;http 1.1 spec&lt;/a&gt;. Here we will use a Strong Cache Validator to indicate that a resource should never expire. You should include both validator and resource ID in the URL, like "/resources/{id}/{cacheValue}".&lt;br /&gt;&lt;br /&gt;The most well-known candidates are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;MD5&lt;/b&gt;: The file checksum can be calculated when the file is saved or when the application starts up. This is how Loom (and others) set the cache headers for CSS and javascript files.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Version numbers&lt;/b&gt;: any @Version field can be used to identify persistent JPA entities.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;In this case we will be using &lt;a href="http://loom.svn.sourceforge.net/viewvc/loom/loom/trunk/src/java/org/loom/persistence/file/PersistentFile.java?view=markup"&gt;PersistentFile&lt;/a&gt;, which includes both fields. After trying both, we found that using @Version yields much shorter URLs ("/resources/151/2" instead of "/resources/151/3b4efe6405d4fb1ada4a081f4dbef6a9").&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@Entity&lt;br /&gt;public class PersistentFile { &lt;br /&gt;&lt;br /&gt; /** the primary key value */ &lt;br /&gt; @Id @GeneratedValue private Integer id; &lt;br /&gt;&lt;br /&gt; /** the MD5 hash of this file */ &lt;br /&gt; @Column(length=32) private String MD5;  &lt;br /&gt;&lt;br /&gt; /** the revision number */ &lt;br /&gt; @Version private Integer version;  &lt;br /&gt;&lt;br /&gt; /** the last update timestamp */ &lt;br /&gt; private Date lastModified; &lt;br /&gt; &lt;br /&gt; /** ... etc ... */&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This field will be used to set "Expires" and "Cache-Control" headers to expire ten years in the future. As long as the user is clicking links to browse normally these resources should never be fetched again, but it will be ignored if the user clicks the "Refresh" button, which is where Weak Cache Validation comes into place.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Weak Cache Validator&lt;/h2&gt;&lt;br /&gt;With Weak Cache Validation the browser must ask for the resource, giving the server a chance to answer with a 304 (not modified) or 200 (OK) response.&lt;br /&gt;&lt;br /&gt;For this purpose we will use the "Last-Modified" header. The first request by the browser (the empty cache experience or any "Shift + Refresh" click) will be answered by:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img src="http://2.bp.blogspot.com/_5fcKwF7jItc/Sq_NnF1sgaI/AAAAAAAAAX4/GP4hfrv5ROM/s800/200.gif" border="0" /&gt;&lt;/div&gt;&lt;br /&gt;On a browser refresh the server will return a 304 response based on the value of the If-Modified-Since header:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img src="http://1.bp.blogspot.com/_5fcKwF7jItc/Sq_NoUl6SwI/AAAAAAAAAYA/8idxuzB640s/s800/304.gif" border="0" /&gt;&lt;/div&gt;&lt;br /&gt;&lt;h2&gt;The big picture&lt;br /&gt;&lt;/h2&gt;&lt;br /&gt;During the sample navigation to the homepage, images have run down from this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img alt="59 image requests = 544 KB (492 KB from cache)" src="http://2.bp.blogspot.com/_5fcKwF7jItc/SolbT5bjbRI/AAAAAAAAAXQ/217dCUL1I84/s800/images-nocache.gif" title="59 image requests = 544 KB (492 KB from cache)" border="0" /&gt;&lt;/div&gt;&lt;br /&gt;To this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img alt="0 image requests = 0 KB" src="http://3.bp.blogspot.com/_5fcKwF7jItc/SolbSXFGhfI/AAAAAAAAAXI/g1LRHLHv0C0/s800/images-cache.gif" title="0 image requests = 0 KB" border="0" /&gt;&lt;/div&gt;&lt;br /&gt;Saving bandwidth is great, but keep in mind that we are doing it to reduce response time. In this case, 2-3 seconds have been reduced to 0.3 just by introducing this change.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Code&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class FileAction extends AbstractAction {  &lt;br /&gt;&lt;br /&gt; @Autowired private FileManager fileManager;  &lt;br /&gt;&lt;br /&gt; /** the id of the file */ &lt;br /&gt; private Integer id; &lt;br /&gt;&lt;br /&gt; /** the version number of the resource */ &lt;br /&gt; private Integer version;  &lt;br /&gt;&lt;br /&gt; /** the retrieved file */ &lt;br /&gt; private PersistentFile pfile;  &lt;br /&gt;&lt;br /&gt; /**  &lt;br /&gt;  * This method will be invoked before getFile(). If the current request includes a If-Modified-Since  &lt;br /&gt;  * header that matches the lastModified attribute of the file, a 304 (NOT MODIFIED) response will be   &lt;br /&gt;  * automatically sent, and getFile() will not be invoked.  &lt;br /&gt;  */ &lt;br /&gt; @Cache(on="getFile") &lt;br /&gt; public CacheControl getCacheControl() {  &lt;br /&gt;  pfile = fileManager.find(id);  &lt;br /&gt;  CacheControl c = new CacheControl();  &lt;br /&gt;  if (pfile.getVersion().equals(version)) {   &lt;br /&gt;   c.setLastModified(pfile.getLastModified());   &lt;br /&gt;   c.setCacheForever();  &lt;br /&gt;  }  &lt;br /&gt;  return c; &lt;br /&gt; } &lt;br /&gt;&lt;br /&gt; @GET @Path("/{id}/{version}") &lt;br /&gt; public Resolution getFile() {  &lt;br /&gt;  if (pfile.getVersion().equals(version)) {   &lt;br /&gt;   return send(pfile);  &lt;br /&gt;  } else {   &lt;br /&gt;   // if the version number differs, send a 301 redirect   &lt;br /&gt;   return redirect(FileAction.class, "getFile")    &lt;br /&gt;     .addParameter("id", pfile.getId())    &lt;br /&gt;     .addParameter("version", pfile.getVersion())    &lt;br /&gt;     .permanent();  &lt;br /&gt;  } &lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://loom.svn.sourceforge.net/viewvc/loom/loom/trunk/src/java/org/loom/servlet/CacheControl.java?view=markup"&gt;CacheControl&lt;/a&gt; is the class that does all the thinking. It sets the http headers and decides whether to return a 304 or 200 response.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;We will be in London on Sept 23rd&lt;/h2&gt;&lt;br /&gt;We are quite excited that Loom has been selected finalist of the &lt;a href="http://www.access-it-events.org/2009_access_it_awards.php"&gt;ACCESS-IT awards&lt;/a&gt; in the web 2.0 category. We will be in London on September 22 and 23, which is my first trip there. Should be fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-4311599076489044218?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/4311599076489044218/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2009/09/caching-user-supplied-images-in-browser.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4311599076489044218?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4311599076489044218?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2009/09/caching-user-supplied-images-in-browser.html" title="Caching user-supplied images in the browser" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_5fcKwF7jItc/SrCvM9DoXeI/AAAAAAAAAYI/dB92IhmgAhQ/s72-c/its-all-about-speed.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;D08ARXw6fCp7ImA9WxJWGUk.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-935585089578156295</id><published>2009-06-25T18:02:00.002+02:00</published><updated>2009-06-25T18:04:04.214+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-25T18:04:04.214+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="talks" /><category scheme="http://www.blogger.com/atom/ns#" term="tests" /><title>30 slides about testing ajax components</title><content type="html">Last &lt;a href="http://sunopencommunitiesforum.es/"&gt;Sun Open Community Forum&lt;/a&gt; is over, and like past events (the name changes, but people remains the same) it was the perfect opportunity to get the spanish java community together to enjoy some beer and - incidentally - to talk about software too :)&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_5fcKwF7jItc/SkNSeuQWipI/AAAAAAAAAXA/-LksfAYVguY/s1600-h/apples-in-line.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_5fcKwF7jItc/SkNSeuQWipI/AAAAAAAAAXA/-LksfAYVguY/s400/apples-in-line.jpg" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="clear: right;"&gt;This year has been great. Due to my nemesis-agenda I could not be there for the second day, which means that I missed the groovy lab by Nacho Brito (if you are talking about groovy in Spain, &lt;a href="http://groovy.org.es/"&gt;that's Nacho&lt;/a&gt;) and Daniel "greeneyed" Lopez - seriously, I wanted to see him running &lt;a href="https://swww.uib.es/webleaf/confluence/pages/viewpage.action;jsessionid=_7qNPLNUKRdAVoAwis?pageId=23691266"&gt;five languages&lt;/a&gt; in the JVM during a 2-hour lab.&lt;/div&gt;&lt;br /&gt;The ones that I managed to attend were great: &lt;a href="http://blogs.sun.com/jorgeSanchez/"&gt;Jorge Sanchez&lt;/a&gt; introduced some alternatives for large-scale integration and presented the new spanish MySQL community, and &lt;a href="http://weblogs.javahispano.org/artesanodeprimera/"&gt;Alfredo Casado&lt;/a&gt; fired some great slides about content negotiation and all things REST.&lt;br /&gt;&lt;br /&gt;We were also there: &lt;a href="http://sergialmar.wordpress.com/"&gt;Sergi Almar&lt;/a&gt; had a really succesful lab about what's  coming in Spring 3.0 which enjoyed some massive assistance. Due to lack of space, some people had to spend the entire lab with their laptops over their kneels but they still  were not leaving. I delivered a brief (30-min) talk about how to mix progressive enhancement, java, and javascript tests.&lt;br /&gt;&lt;br /&gt;&lt;div id="__ss_1630811" style="text-align: left; width: 425px;"&gt;&lt;a href="http://www.slideshare.net/icoloma/developing-and-testing-ajax-components?type=powerpoint" style="margin: 12px 0pt 3px; display: block; font-family: Helvetica,Arial,Sans-serif; font-size-adjust: none; font-size: 14px; font-stretch: normal; font-style: normal; font-variant: normal; font-weight: normal; line-height: normal; text-decoration: underline;" title="Developing and testing ajax components"&gt;Developing and testing ajax components&lt;/a&gt;&lt;object style="margin: 0px;" height="355" width="425"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=testing-ajax-components-090624042308-phpapp02&amp;amp;stripped_title=developing-and-testing-ajax-components"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=testing-ajax-components-090624042308-phpapp02&amp;amp;stripped_title=developing-and-testing-ajax-components" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="355" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;div style="font-family: tahoma,arial; font-size: 11px; height: 26px; padding-top: 2px;"&gt;View more &lt;a href="http://www.slideshare.net/" style="text-decoration: underline;"&gt;Keynote presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/icoloma" style="text-decoration: underline;"&gt;Ignacio Coloma&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;Lots of other things to share, but that will be in a couple of weeks. Cheers!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-935585089578156295?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/935585089578156295/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2009/06/30-slides-about-testing-ajax-components.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/935585089578156295?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/935585089578156295?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2009/06/30-slides-about-testing-ajax-components.html" title="30 slides about testing ajax components" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_5fcKwF7jItc/SkNSeuQWipI/AAAAAAAAAXA/-LksfAYVguY/s72-c/apples-in-line.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;D04AR3w4eip7ImA9WxJRGU0.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-6546857521235608395</id><published>2009-05-21T13:36:00.003+02:00</published><updated>2009-05-21T13:39:06.232+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-21T13:39:06.232+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><category scheme="http://www.blogger.com/atom/ns#" term="training" /><category scheme="http://www.blogger.com/atom/ns#" term="web" /><title>Loom 1.5 is out; are you doing something interesting today?</title><content type="html">Today we are releasing Loom 1.5. This is our first release intended for public consumption, so if you are thinking about giving it a twirl we will be more than glad to help.&lt;br /&gt;&lt;br /&gt;I am not going to talk about things included in this release, such as a YSlow! 'A' rate for the &lt;a href="http://loom.extrema-sistemas.org/loomdemo/mortgages/"&gt;demo application&lt;/a&gt; (94 out of 100, yay!), the &lt;a href="http://loom.extrema-sistemas.org/doc/1.x/ref/property-annotations#ImageValidation"&gt;ImageValidation annotation&lt;/a&gt; or the &lt;a href="http://loom.extrema-sistemas.org/doc/1.x/ref/configuration#spring"&gt;simplified spring config&lt;/a&gt;. Instead, I wanted to comment something present since our 1.0 release.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Things that your web framework should be doing for you&lt;/h2&gt;&lt;br /&gt;&lt;a href="http://icoloma.blogspot.com/2008/12/start-testing-your-javascript-server.html"&gt;Last entry&lt;/a&gt; was about how your javascript and server-side code should talk HTML at both sides, because generating javascript from the server is a bad practice. This one is about how you (and by "you" I mean your web framework) should be generating your HTML markup.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;What follows is what we like to call a Contributed View, meaning that each piece of the web framework can contribute aspects to the generated HTML.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Form Fields&lt;/h3&gt;&lt;br /&gt;An integer field validated between 5 and 100 can contribute this to the generated HTML:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_5fcKwF7jItc/ShGyWg-Im_I/AAAAAAAAAWw/m-QfpIsxjPg/s1600-h/contributed+view+-+input+field.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_5fcKwF7jItc/ShGyWg-Im_I/AAAAAAAAAWw/m-QfpIsxjPg/s400/contributed+view+-+input+field.jpg" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Converter&lt;/b&gt;: inject a 'number' css class to be used by javascript and CSS style sheets.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Validator&lt;/b&gt;: inject attributes (scale="0" min="5" max="100") to be used by javascript validations. Better yet, inject maxlength="3" to make the browser enforce the maximum length of this field. It can get tricky since you must take into consideration negative values and decimal point.&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;Notice what the server should not be generating: javascript code. You can still use prototype, jquery or dojo to validate user input, as the server side is just providing the required metadata.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Links &lt;/h3&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_5fcKwF7jItc/ShGyXWoFmFI/AAAAAAAAAW4/xY7oZ-eDyB8/s1600-h/contributed+view+-+link.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img src="http://2.bp.blogspot.com/_5fcKwF7jItc/ShGyXWoFmFI/AAAAAAAAAW4/xY7oZ-eDyB8/s400/contributed+view+-+link.jpg" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The easiest example of URL decoration affects links pointing to forbidden resources. If the application developer is using annotations  to specify permissions (with JSR-250 &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/annotation/security/RolesAllowed.html"&gt;@RolesAllowed&lt;/a&gt;, for example) the security interceptor can contribute an "unauthorized" css class to links and forms. Again, it is your responsibility as the application developer to use CSS or javascript to hide or disable them.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;About our &lt;i&gt;next &lt;/i&gt;few weeks&lt;/h2&gt;&lt;br /&gt;I have been seriously lectured by some friends about how we are not dedicating enough time to make visible the awesome things we are into these days. I hate sounding like a salesman, but people got so serious that I thought I should make an exception just for today:&lt;br /&gt;&lt;br /&gt;We are an heterogeneous software company, which means that the PHP guy sits next to the java freak most of the time and we still somehow manage to keep the peace - most of the time. When we can choose, we have been using the Loom-Spring-Hibernate combo for almost two years and are quite happy with it. There are lots of cool details like the &lt;a href="http://loom.extrema-sistemas.org/doc/1.x/ref/method-annotations#cache"&gt;browser cache annotation&lt;/a&gt; that do not have an equivalent in other web frameworks.&lt;br /&gt;&lt;br /&gt;If you test and like it, I want to know. If you test and find things that you don't like, hey, I still want to know!&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;About our &lt;i&gt;last&lt;/i&gt; few weeks&lt;/h2&gt;&lt;br /&gt;Our training courses have been a great success! We are really proud of the feedback received, 80% of the attendees to  both courses have given us the highest qualification possible. In our first &lt;a href="http://extrema-sistemas.com/en/training/springsource"&gt;Core Spring Course&lt;/a&gt; in  Madrid, people with previous spring experience started learning something new from day 2, which is a remarkable achievement for an introductory course.&lt;br /&gt;&lt;br /&gt;On a related note, a friend from Tenerife was quite pissed at me because he had to investigate a big deal to find our &lt;a href="http://extrema-sistemas.com/en/training/agile-project-management-practice"&gt;Agile Project Management Course Contents&lt;/a&gt;. It's more or less the same thing you can see in this blog, but four hours a day. Last week lots of people confessed that they were expecting to fall asleep in the middle of the course, but instead had lots of fun - and with 'we', I mean 'me included'.&lt;br /&gt;&lt;br /&gt;On the "things to improve" comments, we have been asked to provide more practical examples for both courses. We are certainly looking forward to improve that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-6546857521235608395?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/6546857521235608395/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2009/05/loom-15-is-out-are-you-doing-something.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/6546857521235608395?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/6546857521235608395?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2009/05/loom-15-is-out-are-you-doing-something.html" title="Loom 1.5 is out; are you doing something interesting today?" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_5fcKwF7jItc/ShGyWg-Im_I/AAAAAAAAAWw/m-QfpIsxjPg/s72-c/contributed+view+-+input+field.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;CkYGR348fSp7ImA9WxVaGEo.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-3097687324131257522</id><published>2009-04-16T10:15:00.001+02:00</published><updated>2009-04-16T10:22:06.075+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-16T10:22:06.075+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="training" /><category scheme="http://www.blogger.com/atom/ns#" term="spring" /><title>Guess who is the first SpringSource partner in Spain</title><content type="html">Yep. We are. &lt;br /&gt;&lt;br /&gt;These last weeks have been like playing the violin with one hand while running across a Super Bowl match - with the ball -, but results are finally here: we are quite excited to &lt;a href="http://www.extrema-sistemas.com/en/news/2009/04/extrema-sistemas-first-springsource-certified-official-partner-spain"&gt;announce the partnership between Extrema Sistemas and SpringSource&lt;/a&gt;.&lt;br /&gt;&lt;img style="float:right; margin:0 0 10px 10px;width: 320px; height: 304px;" src="http://1.bp.blogspot.com/_5fcKwF7jItc/SebqeGAMa2I/AAAAAAAAAWY/PHabKDcpsb4/s400/concentrate.jpg" border="0" alt="Concentrate in your training" title="Concentrate in your training" id="BLOGGER_PHOTO_ID_5325201412140591970" /&gt;&lt;br /&gt;This agreement is the conclusion of lots of work on both sides, notably &lt;a href="http://sergialmar.wordpress.com/"&gt;Sergi Almar&lt;/a&gt; who started the whole thing in the first place. Extrema Sistemas will be delivering the SpringSource official courses in Spain, starting in Madrid on Apr 27-30 and later in Barcelona on May 25-28.&lt;br /&gt;&lt;br /&gt;On a related note, I will be in Tenerife on May 4-8 to deliver our &lt;a href="http://www.extrema-sistemas.com/en/agile-project-management-practice-0"&gt;Agile Project Management Course&lt;/a&gt; on-site for a coalition of software development companies. It will be a full house, and as an interesting extra I will give a quick talk (well, "quick" be pronounced as "like two hours") at my former University. &lt;br /&gt;&lt;br /&gt;I am really excited with the perspective of looking at my old college rooms from the other side :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-3097687324131257522?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/3097687324131257522/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2009/04/guess-who-is-first-springsource-partner.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/3097687324131257522?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/3097687324131257522?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2009/04/guess-who-is-first-springsource-partner.html" title="Guess who is the first SpringSource partner in Spain" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_5fcKwF7jItc/SebqeGAMa2I/AAAAAAAAAWY/PHabKDcpsb4/s72-c/concentrate.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry gd:etag="W/&quot;D08BRH4zfCp7ImA9WxJWGUk.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-8250072958802885714</id><published>2009-03-23T16:00:00.004+01:00</published><updated>2009-06-25T18:04:15.084+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-25T18:04:15.084+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="tests" /><title>Start testing your Javascript server-less</title><content type="html">Are you still launching tomcat to test your javascript code? You shouldn't.&lt;br /&gt;&lt;br /&gt;When applying the MVC pattern, HTML as always been considered to be part of the View, which is only correct from a server point of view. In browser-land HTML is the Model, the Controller is our javascript code, and the View is - well, it can be your chrome, your CSS, whatever. The important thing is, there are two MVCs involved:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/ScJvAFPXXNI/AAAAAAAAAWA/c8UIS5uYsFM/s1600-h/server-and-browser.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 213px;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/ScJvAFPXXNI/AAAAAAAAAWA/c8UIS5uYsFM/s400/server-and-browser.gif" alt="" id="BLOGGER_PHOTO_ID_5314932557447912658" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Two years ago we started applying some simple rules for javascript development:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;No javascript code generated by the server&lt;/strong&gt;. Nil. Zero. Nada. That would only make things harder to port and reuse. Instead, the server generates the HTML attributes (some standard and &lt;a href="http://icoloma.blogspot.com/2008/06/extend-html-with-your-own-metadata.html"&gt;some not&lt;/a&gt;) to be used by javascript code. Combine this with progressive enhancement and you will have an almost functional interface to add your javascript icing.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Javascript is always tested using static HTML files&lt;/strong&gt;, which represent the approved API between server and client software. You can test easily your javascript code, and the server only has to stick to this API.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Javascript unit testing may be added later&lt;/strong&gt;. We use &lt;a href="http://github.com/madrobby/scriptaculous/wikis/unit-testing"&gt;UnitTest&lt;/a&gt;, but there are others: &lt;a href="http://docs.jquery.com/QUnit"&gt;JQuery&lt;/a&gt;, &lt;a href="http://fireunit.org/"&gt;Firebug&lt;/a&gt;, &lt;a href="http://blog.jeffhaynie.us/introducing-testmonkey.html"&gt;TestMonkey&lt;/a&gt;... the list goes on. Notice that I am not considering Selenium here, as we are not testing interfaces but javascript code.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_5fcKwF7jItc/ScJvAYUd3kI/AAAAAAAAAWI/j1Gm6EqPo1s/s1600-h/testsuite-and-browser.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 333px; height: 400px;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/ScJvAYUd3kI/AAAAAAAAAWI/j1Gm6EqPo1s/s400/testsuite-and-browser.gif" alt="" id="BLOGGER_PHOTO_ID_5314932562569584194" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;No matter the programming language, life is easier if you do not try to generate your Controller. Now your test environment can be 100% static.&lt;br /&gt;&lt;br /&gt;These simple rules have made our lives so much easier. Instead of the classic mixup of technologies to test Ajax interfaces, there is just a javascript test engine and some html files to test things up (including some static JSON response examples). Before this, something like &lt;a href="http://loom.sourceforge.net/jstests/loom/test/js/validation/validation-test.html"&gt;testing our validation code&lt;/a&gt; was a killing effort. This can still be integrated with ant or maven without requiring a java server in the middle.&lt;br /&gt;&lt;br /&gt;Going static include some additional advantages:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;You can interact with the interface by hand&lt;/strong&gt;. When the tests are done, you still have a working HTML page to pick at.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;You can trace and debug any failing assertion&lt;/strong&gt; using Firebug or the IE debugger.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;There is no roundtrip to the server&lt;/strong&gt;. Things can only be fixed up to the HTML API, so there is no point in thinking in java at this point.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Think Divide-And-Conquer, but separating client from server using HTML. Once all javascript code is tested, you can start integrating things with the java server just by generating the expected HTML.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-8250072958802885714?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/8250072958802885714/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/12/start-testing-your-javascript-server.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/8250072958802885714?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/8250072958802885714?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/12/start-testing-your-javascript-server.html" title="Start testing your Javascript server-less" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_5fcKwF7jItc/ScJvAFPXXNI/AAAAAAAAAWA/c8UIS5uYsFM/s72-c/server-and-browser.gif" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></entry><entry gd:etag="W/&quot;D0EMRns-fip7ImA9WxJWGUk.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-4394735638085498102</id><published>2009-02-09T12:04:00.003+01:00</published><updated>2009-06-25T18:01:27.556+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-25T18:01:27.556+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="talks" /><title>My Devoxx slides: 15 minutes about improving your users cache experience</title><content type="html">I finally managed to save some time to upload my slides about the browser cache at &lt;a href="http://devoxx.com/"&gt;Devoxx 2008&lt;/a&gt;, in what seems like a life ago (long story short, converting to Powerpoint XP back and forth is not a nice idea if you plan to upload it sometime later).&lt;br /&gt;&lt;br /&gt;Most people missed it since it was scheduled as a quickie, which is fine since I'm a lame speaker (specially when throwing foreign languages into the mix). The slides were OK though, so I wanted to share. And here they are.&lt;br /&gt;&lt;br /&gt;&lt;div style="width: 425px; text-align: left;" id="__ss_999134"&gt;&lt;a style="margin: 12px 0pt 3px; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; display: block; text-decoration: underline;" href="http://www.slideshare.net/icoloma/caching-web-contents-in-the-browser-999134?type=powerpoint" title="Caching web contents in the browser"&gt;Caching web contents in the browser&lt;/a&gt;&lt;object style="margin: 0px;" height="355" width="425"&gt;&lt;param name="movie" value="http://static.slideshare.net/swf/ssplayer2.swf?doc=devoxx3-1233954709612026-3&amp;amp;stripped_title=caching-web-contents-in-the-browser-999134"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=devoxx3-1233954709612026-3&amp;amp;stripped_title=caching-web-contents-in-the-browser-999134" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="355" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;All in all, devoxx was an awesome experience. I'm looking forward to repeat this year!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-4394735638085498102?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/4394735638085498102/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2009/02/my-devoxx-slides-15-minutes-about.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4394735638085498102?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4394735638085498102?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2009/02/my-devoxx-slides-15-minutes-about.html" title="My Devoxx slides: 15 minutes about improving your users cache experience" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry gd:etag="W/&quot;DEMEQn4-fyp7ImA9WxRUFkU.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-2273502664838923962</id><published>2008-11-26T09:00:00.000+01:00</published><updated>2008-11-26T09:00:03.057+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-26T09:00:03.057+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><title>Loom 1.0 final is out. Let there be AWESOME.</title><content type="html">&lt;img border="0" title="Now, get out there and start kicking ass" src="http://photos1.blogger.com/blogger/4415/852/320/Attitude.jpg" style="margin: 0pt 10px 10px 0pt; float: left; "/&gt;&lt;br /&gt;After two years of work, 700+ java classes, 400+ tests, 35 javascript test pages and a hell of a week polishing rough edges, we finally got the release out the door. &lt;br /&gt;&lt;br /&gt;(I enjoy an entire night of pure insomnia once a month, which looking behind is a great boost for productivity :)&lt;br /&gt;&lt;br /&gt;With lots of &lt;em&gt;bottled awesomeness&lt;sup&gt;tm&lt;/sup&gt;&lt;/em&gt; inside, the following is a selection of the new features that come included in Loom 1.0:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;This release includes a scaffolding tool&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;We finally got tired of starting projects from scratch, so Rafa got a week dedicated to it and prepared a tool that can kick start a project in five minutes, assuming a decent internet connection. Just download scaffolding.zip from &lt;a href="https://sourceforge.net/project/showfiles.php?group_id=200887"&gt;the download page&lt;/a&gt;, unzip and run:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;# Create a project named myapp &lt;br /&gt;scaffolding create myapp&lt;br /&gt;&lt;br /&gt;# Create the eclipse project &lt;br /&gt;# (requires GRADLE_HOME defined)&lt;br /&gt;cd myapp&lt;br /&gt;gradle eclipse&lt;br /&gt;&lt;br /&gt;# Deploy on your favorite web server &lt;br /&gt;# (requires CATALINA_HOME or JETTY_HOME defined)&lt;br /&gt;gradle deploy-tomcat&lt;br /&gt;gradle deploy-jetty&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Sometimes gradle timeouts while waiting for maven repositories; in that case, just wait a little and repeat the command.&lt;br /&gt;&lt;br /&gt;The created template will be based on &lt;a href="http://www.gradle.org/"&gt;gradle&lt;/a&gt;, which is (some artistic license, here) a combination of ivy + groovy + steroids. Notice that Loom does not use groovy but as a substitute for ant (and a great one at it!), the rest is 100% java.&lt;br /&gt;&lt;br /&gt;Open the project using eclipse, where compiled java files go directly into WEB-INF/classes. Open "Run as" and select "myapp - run tomcat" or "myapp - run jetty" (again, CATALINA_HOME or JETTY_HOME are required). No plugins are needed, no build process, nothing. Just compile and open a browser at http://localhost:8080/myapp&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; width: 400px; height: 369px;" src="http://2.bp.blogspot.com/_5fcKwF7jItc/SSw7yXo-xoI/AAAAAAAAAVQ/dADtJM2m8ZI/s400/scaffolding.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5272655000269407874" /&gt;&lt;br /&gt;&lt;br /&gt;Now, create a new JPA entity with some attributes using eclipse (Loom does not require JPA, but this is easier this way) and add your own CRUD interface:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;# execute this in your project home dir after MyPersistentClass has been compiled&lt;br /&gt;scaffolding crud com.acme.model.MyPersistentClass&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it. Restart the server and test your interface. The form should validate your JPA constraints, which can be modified at any time without re-creating the interface or anything.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;This release includes Javascript and CSS repositories&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Your javascript and CSS files can be concatenated, minified and gzipped in production without involving any ant or maven script.&lt;br /&gt;&lt;br /&gt;This is similar to what &lt;a href="https://jawr.dev.java.net/"&gt;JAWR&lt;/a&gt; does, but easier to configure and use. Its main features are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Configured using spring&lt;/strong&gt;: it does not require external files.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;All files (javascript, CSS, referenced images) can be &lt;strong&gt;loaded from the classpath&lt;/strong&gt;, which means that they can be provided by other jars. &lt;strong&gt;You can still use images inside CSS files&lt;/strong&gt;, and they will be served - even from the classpath.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;All files (again - css, javascript AND image files!) are &lt;strong&gt;aggressively cached based on their MD5 checksums&lt;/strong&gt;. If you have ever worked with GWT, you may be familiar with the concept - if not, drop by our &lt;a href="http://www.devoxx.com/display/JV08/Caching+web+contents+in+the+browser"&gt;Devoxx talk&lt;/a&gt; where we will be talking about this as long as time allows.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can choose between &lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUICompressor&lt;/a&gt; (javascript and CSS) and &lt;a href="http://javascript.crockford.com/jsmin.html"&gt;JSMin&lt;/a&gt; (javascript) just by dropping them on the lib folder. They are included in the /lib/compressors folder of the &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=200887"&gt;Loom distribution&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;This should be easier to digest using an example. The spring file:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;bean class="org.loom.resources.WebResourceBundleRepository"&gt;&lt;br /&gt;&amp;lt;property name="bundles"&gt;&lt;br /&gt;&amp;lt;bean class="org.loom.config.SpringResourceMapFactory"&gt;&lt;br /&gt;&amp;lt;property name="resources"&gt;&lt;br /&gt;&amp;lt;value&gt;&lt;br /&gt;    css=/css/base.css /css/basemod.css &lt;br /&gt;    ie=/css/yaml-3.0.5/core/iehacks.css /css/my_iehacks.css&lt;br /&gt;    demo=classpath:/js/prototype/prototype-1.6.0.3.js classpath:/js/loom/core.js \&lt;br /&gt; classpath:/js/loom/format.js &lt;br /&gt;&amp;lt;/value&gt;&lt;br /&gt;&amp;lt;/property&gt;&lt;br /&gt;&amp;lt;/bean&gt;&lt;br /&gt;&amp;lt;/property&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The JSP file:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;l:css resource="css"/&gt;&lt;br /&gt;&amp;lt;l:css resource="ie" ie="any"/&gt;&lt;br /&gt;&amp;lt;l:script resource="demo"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The generated html with config.development=true:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;link href="/myapp/resources/css/0/2008a6c2ba1c6009f5f84db99e6a76e0/base.css" rel="stylesheet" type="text/css"&gt;&amp;lt;/link&gt;&lt;br /&gt;&amp;lt;link href="/myapp/resources/css/1/f6c4b21a2f6c9d265d14081243c2da40/basemod.css" rel="stylesheet" type="text/css"&gt;&amp;lt;/link&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!--[if IE]&gt;&lt;br /&gt;&amp;lt;link href="/myapp/resources/ie/0/658801c193faa8e7173e2cd220093e67/iehacks.css" rel="stylesheet" type="text/css"&gt;&amp;lt;/link&gt;&lt;br /&gt;&amp;lt;link href="/myapp/resources/ie/1/658801c193faa8e7173e2cd220093e67/my_iehacks.css" rel="stylesheet" type="text/css"&gt;&amp;lt;/link&gt;&lt;br /&gt;&amp;lt;![endif]--&gt; &lt;br /&gt;&lt;br /&gt;&amp;lt;script src="/myapp/resources/demo/0/b5684120e496c310977713be34be4868/prototype-1.6.0.3.js" type="text/javascript"&gt;&amp;lt;/script&gt;&lt;br /&gt;&amp;lt;script src="/myapp/resources/demo/1/07139c2fd5d77ee9fa619ff335996068/core.js" type="text/javascript"&gt;&amp;lt;/script&gt;&lt;br /&gt;&amp;lt;script src="/myapp/resources/demo/2/bd7026fb11a52e8fe8c671e894885e78/format.js" type="text/javascript"&gt;&amp;lt;/script&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/SSMg9JnOYNI/AAAAAAAAAUc/fd0i7rg8QOI/s1600-h/screenshot-debug-on.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 253px;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/SSMg9JnOYNI/AAAAAAAAAUc/fd0i7rg8QOI/s400/screenshot-debug-on.gif" border="0" title="Generated HTML with development=true" id="BLOGGER_PHOTO_ID_5270092223878160594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The generated html with config.development=false:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;link href="/myapp/resources/css/all/7c66dcfb53bb87201e74aef4f1211a63/" rel="stylesheet" type="text/css"&gt;&amp;lt;/link&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!--[if IE]&gt;&lt;br /&gt;&amp;lt;link href="/myapp/resources/ie/all/da188973e7712595d03da0bbbfa059f4/" rel="stylesheet" type="text/css"&gt;&amp;lt;/link&gt;&lt;br /&gt;&amp;lt;![endif]--&gt; &lt;br /&gt;&lt;br /&gt;&amp;lt;script src="/myapp/resources/demo/all/1c004c851402d83a84e3d7a2fb73a375/" type="text/javascript"&gt;&amp;lt;/script&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/SSMg8kJ4xnI/AAAAAAAAAUU/uTjUANDa3u4/s1600-h/screenshot-debug-off.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 115px;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/SSMg8kJ4xnI/AAAAAAAAAUU/uTjUANDa3u4/s400/screenshot-debug-off.gif" border="0" alt="The same page, with development=false" id="BLOGGER_PHOTO_ID_5270092213822998130" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;These resources have been automatically concatenated, gzipped, MD5-hashed (just once!) and set to expire ten years from now. The browser will never ask for them again, as long as they do not get modified or the user hits the "refresh" button. During normal navigation only the html file will be retrieved.&lt;br /&gt;&lt;br /&gt;But there's more: during development, your changes will be detected on real time, but the browser will only ask for the resources that have changed. The typical 304 traffic between server and client will just not happen.&lt;br /&gt;&lt;br /&gt;Go test the feature live on &lt;a href="http://loom.extrema-sistemas.com/loomdemo/"&gt;the new demo&lt;/a&gt;, just turn on / off the debug switch (at the top right corner) and open firebug to see the number of files involved for each request. Now, navigate between pages and keep an eye on your firebug net tab. No files are being requested.&lt;br /&gt;&lt;br /&gt;Wait, there's more.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;This release includes partial JAX-RS support&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;We have added support for the mapping annotations of the JAX-RS spec. In fact, the CRUD interface created by scaffolding does just that:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;public class MortgagesAction extends AbstractAction {&lt;br /&gt;&lt;br /&gt; @RetrieveEntity(on={"save", "edit"})&lt;br /&gt; private Mortgage mortgage;&lt;br /&gt;&lt;br /&gt; @GET @Path("/")&lt;br /&gt; public Resolution list() { ... }&lt;br /&gt;&lt;br /&gt; @GET @Path("create")&lt;br /&gt; public Resolution create() { ... }&lt;br /&gt; &lt;br /&gt; @GET @Path("/{mortgage.id}")&lt;br /&gt; public Resolution edit() { ... }&lt;br /&gt; &lt;br /&gt; @POST @Path("/{mortgage.id?}")&lt;br /&gt; public Resolution save() { ... }&lt;br /&gt; &lt;br /&gt; @DELETE @Path("/{mortgage.id?}")&lt;br /&gt; public Resolution delete() { ... }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Combine with &lt;a href="http://www.zeroturnaround.com/javarebel/"&gt;JavaRebel&lt;/a&gt; to enjoy &lt;em&gt;The Extra Cool Development Experience&lt;/em&gt;. Open your Action, modify your mappings, refresh the browser - and only your Action class gets reloaded. New Actions cannot be added on-the-fly and there are other limitations, but this first release is really promising.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;This release includes some serious pagination components.&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;I have tested almost every pagination solution in the wild since 1999, checking them off with my old list of (almost) reasonable requirements:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Pagination should be done by the database&lt;/strong&gt; if possible. Having said that, sometimes I don't have a database, and the web framework should do its work.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There is almost no difference between a &lt;strong&gt;paged list&lt;/strong&gt; (&amp;lt;ul&gt;) and a &lt;strong&gt;paged table&lt;/strong&gt; (&amp;lt;table&gt;).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Pagination and sorting should be transparent&lt;/strong&gt;, or almost. That is tricky when it involves the database.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It should include a &lt;strong&gt;professional look and feel&lt;/strong&gt; by default. This is not my merit, kudos go to &lt;a href="http://woork.blogspot.com/2008/03/perfect-pagination-style-using-css.html"&gt;Antonio Lupetti&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;l:pagedTable data="${action.mortgages}" id="mortgages"&gt;&lt;br /&gt;  &amp;lt;l:column sortable="false"&gt;&lt;br /&gt;     &amp;lt;l:inputCheckbox name="selectedRows" class="selectRow checkbox" value="${row.id}" renderLabel="false"/&gt;&lt;br /&gt;  &amp;lt;/l:column&gt;&lt;br /&gt;  &amp;lt;l:column property="id" /&gt;&lt;br /&gt;  &amp;lt;l:column property="name" action="Mortgages" event="edit"&gt;&lt;br /&gt;     &amp;lt;l:param name="mortgage.id" value="${row.id}"/&gt;&lt;br /&gt;  &amp;lt;/l:column&gt;&lt;br /&gt;  &amp;lt;l:column property="address" /&gt;&lt;br /&gt;  &amp;lt;l:column property="principalLoanBalance" title="Loan" /&gt;&lt;br /&gt;  &amp;lt;l:column property="creationDate" class="date" /&gt;&lt;br /&gt;&amp;lt;/l:pagedTable&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_5fcKwF7jItc/SSMiwCSnkiI/AAAAAAAAAUk/90j4X6zTkbw/s1600-h/paged-example.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 150px;" src="http://1.bp.blogspot.com/_5fcKwF7jItc/SSMiwCSnkiI/AAAAAAAAAUk/90j4X6zTkbw/s400/paged-example.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5270094197597639202" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Again, you can check examples with tables and lists in &lt;a href="http://loom.extrema-sistemas.org/loomdemo/"&gt;the demo application&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;h3&gt;This release includes LocaleAwareException and HttpException&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;This is just a pet feature, but I love it. You can throw exceptions that will get to the user as any normal input validation errors, or throw HTTP errors like 404 at any point of the code. It's handy.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;This release includes extensibility&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Two years are a lot of time, in framework years. We kept the original goal of a lightweight and focused web framework, and moved all the server and javascript components that started cropping everywhere to the &lt;a href="http://loom.extrema-sistemas.org/addonsdemo/"&gt;addons project&lt;/a&gt; (take it easy - it's not that mature yet). &lt;br /&gt;&lt;br /&gt;The point is not about the addons project but about this whole new approach to extensibility: just drop a META-INF/loom-descriptor.properties in your jar file like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;# The list of annotation processors provided by this jar&lt;br /&gt;annotation-processors=org.loom.addons.multiupload.MultiUploadAnnotationProcessor, \&lt;br /&gt; org.loom.addons.confirmation.RequiresConfirmationAnnotationProcessor, \&lt;br /&gt;&lt;br /&gt;# The list of Converter factories provided by this jar &lt;br /&gt;converter-factories=org.loom.addons.autocompleter.AutocompletedConverterFactory&lt;br /&gt;&lt;br /&gt;# The list of messages locations provided by this jar&lt;br /&gt;messages=classpath:resources/addons-messages&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Loom will load it from the classpath and the application developer may use its contents:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Java annotations&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Validators and Converters&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Javascript components, including CSS and image files&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Internationalized messages&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;And, of course, its own JSP tags as usual.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;And that's it?&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Damn, no. This is the framework we are using to build applications, every day. We expect it to keep growing with a steady pace with all the know-how in our way for years to come. &lt;br /&gt;&lt;br /&gt;I only wish "steady" could mean "slower" in this context. I can live without the stress of the last few weeks :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-2273502664838923962?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/2273502664838923962/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/11/loom-10-final-is-out-let-there-be.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2273502664838923962?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2273502664838923962?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/11/loom-10-final-is-out-let-there-be.html" title="Loom 1.0 final is out. Let there be AWESOME." /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_5fcKwF7jItc/SSw7yXo-xoI/AAAAAAAAAVQ/dADtJM2m8ZI/s72-c/scaffolding.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></entry><entry gd:etag="W/&quot;CEINRnszeCp7ImA9WxRWE0s.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-5124761244713189688</id><published>2008-10-30T13:00:00.003+01:00</published><updated>2008-10-30T11:29:57.580+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-10-30T11:29:57.580+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><title>My top list of java tools</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_5fcKwF7jItc/SQmGXTCe0mI/AAAAAAAAAQA/uVV5C3Wb2tM/s1600-h/right-tool-for-the-job.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: right; cursor: pointer; width: 590px; height: 109px;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/SQmGXTCe0mI/AAAAAAAAAQA/uVV5C3Wb2tM/s800/right-tool-for-the-job.jpg" alt="A thousand spoons when all I need is a keyboard" title="A thousand spoons when all I need is a keyboard" id="BLOGGER_PHOTO_ID_5262885374364930658" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Lack of imagination is one of our worst sins as software developers. We do the same things over and over again, but we rarely modify our ways - me at least. After some years, these are the tools that made it into my tricks box for everyday tasks. Tiresome operations are not my thing.&lt;br /&gt;&lt;br /&gt;Chances are you are already using at least some of these, but here we go anyways:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;StringUtils&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;The bread and butter of the commons-lang library, this utility class includes some methods that should seriously have been included in String long time ago.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;StringUtils.isEmpty(null) &amp;amp;&amp;amp; StringUtils.isEmpty(""); // true&lt;br /&gt;StringUtils.isBlank("   \n\t");                       // true&lt;br /&gt;StringUtils.substringAfterLast("foo.bar.baz", ".");   // "baz"&lt;br /&gt;StringUtils.substringBeforeLast("foo.bar.baz", ".");  // "foo.bar"&lt;br /&gt;StringUtils.split("foo.bar.baz", '.');                // { "foo", "bar", "baz" }&lt;br /&gt;StringUtils.split("foo,  bar,baz", ", ");             // { "foo", "bar", "baz" }&lt;br /&gt;StringUtils.leftPad("1", 3, '0');                     // "001"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;IOUtils and FileUtils&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;A must-have for the rare occasions where you need to manipulate files by hand. Both are pretty much alike (FileUtils for File, IOUtils for InputStream and Reader classes) and come bundled in commons-io.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;File file1;&lt;br /&gt;File file2;&lt;br /&gt;InputStream inputStream;&lt;br /&gt;OutputStream outputStream;&lt;br /&gt;&lt;br /&gt;// copy one file into another&lt;br /&gt;FileUtils.copyFile(file1, file2);&lt;br /&gt;IOUtils.copy(inputStream, outputStream);&lt;br /&gt;&lt;br /&gt;// read a file into a String&lt;br /&gt;String s1 = FileUtils.readFileToString(file1);&lt;br /&gt;String s2 = IOUtils.toString(inputStream);&lt;br /&gt;&lt;br /&gt;// read a file into a list of Strings, one item per line&lt;br /&gt;List&amp;lt;String&gt; l1 = FileUtils.readLines(file1);&lt;br /&gt;List&amp;lt;String&gt; l2 = IOUtils.readLines(inputStream);&lt;br /&gt;&lt;br /&gt;// put this in your finally() clause after manipulating streams&lt;br /&gt;IOUtils.closeQuietly(inputStream);&lt;br /&gt;&lt;br /&gt;// return the list of xml and text files in the specified folder and any subfolders&lt;br /&gt;Collection&amp;lt;File&gt; c1 = FileUtils.listFiles(file1, { "xml", "txt" }, true);&lt;br /&gt;&lt;br /&gt;// copy one folder and its contents into another&lt;br /&gt;FileUtils.copyDirectoryToDirectory(file1, file2);&lt;br /&gt;&lt;br /&gt;// delete one folder and its contents&lt;br /&gt;FileUtils.deleteDirectory(file1);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Google collections&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;This is the best implementation of a collections extension that I know of. Some of these are shouting to be included in the JDK:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;// create an ArrayList with three arguments&lt;br /&gt;List&amp;lt;String&gt; list = Lists.newArrayList("foo", "bar", "baz");&lt;br /&gt;&lt;br /&gt;// notice that there is no generics or class cast,&lt;br /&gt;// and still this line does not generate a warning.&lt;br /&gt;Set&amp;lt;String&gt; s = Sets.newConcurrentHashSet();&lt;br /&gt;&lt;br /&gt;// intersect and union are basic features of a Set, if you ask me&lt;br /&gt;Set&amp;lt;String&gt; s = Sets.intersect(s1, s2);&lt;br /&gt;&lt;br /&gt;// Example  of multiple values in a Map&lt;br /&gt;ListMultimap&amp;lt;String, Validator&gt; validators = new ArrayListMultimap&amp;lt;String, Validator&gt;();&lt;br /&gt;validators.put("save", new RequiredValidator());&lt;br /&gt;validators.put("save", new StringValidator());&lt;br /&gt;validators.put("delete", new NumberValidator());&lt;br /&gt;&lt;br /&gt;validators.get("save"); // { RequiredValidator, StringValidator }&lt;br /&gt;validators.get("foo");  // empty List (not null)&lt;br /&gt;validators.values();    // { RequiredValidator, StringValidator, NumberValidator }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;java.util.concurrent&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Not everybody needs the heavy lifting of java.util.concurrent, but the concurrent collections are handy:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;// a map that may be modified (by the same or different thread) while being iterated&lt;br /&gt;Map&amp;lt;String, Something&gt; repository = new ConcurrentHashMap&amp;lt;String, Something&gt;();&lt;br /&gt;&lt;br /&gt;// same with lists. This one is only available with Java 6&lt;br /&gt;List&amp;lt;Something&gt; list = new CopyOnWriteArrayList&amp;lt;Something&gt;();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hardly a large toolbox, is it? If your favourite library is missing, feel free to add it :)&lt;br /&gt;&lt;br /&gt;I WILL BE AT DEVOXX!! I have a &lt;a href="http://www.devoxx.com/display/JV08/Caching+web+contents+in+the+browser"&gt;15 minute talk about the browser cache&lt;/a&gt; on December 10th. I am really excited about this, this will be my first JavaPolis (now Devoxx) experience.&lt;br /&gt;&lt;br /&gt;It will be fun, I plan to talk about 304, Expires / Cache-control, and maybe have a small demo of MD5-based cache if time permits. If you happen to be there, please drop by and say hi!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-5124761244713189688?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/5124761244713189688/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/10/my-top-list-of-java-tools.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/5124761244713189688?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/5124761244713189688?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/10/my-top-list-of-java-tools.html" title="My top list of java tools" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_5fcKwF7jItc/SQmGXTCe0mI/AAAAAAAAAQA/uVV5C3Wb2tM/s72-c/right-tool-for-the-job.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">7</thr:total></entry><entry gd:etag="W/&quot;C0UEQH08fCp7ImA9WxRQGUo.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-8349494664288251900</id><published>2008-10-14T10:00:00.001+02:00</published><updated>2008-10-14T10:00:01.374+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-10-14T10:00:01.374+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="books" /><title>"Practical API Design", the book</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_5fcKwF7jItc/SOEZmyRehfI/AAAAAAAAAPM/zUe_BDsxgQ0/s1600-h/Still+learning.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_5fcKwF7jItc/SOEZmyRehfI/AAAAAAAAAPM/zUe_BDsxgQ0/s400/Still+learning.jpg" border="0" title="still learning!" alt="still learning!" id="BLOGGER_PHOTO_ID_5251506794611508722" /&gt;&lt;/a&gt;&lt;br /&gt;It got mentioned on the last OpenJavaDay (thanks Ibon!) with such a good review that I got my own copy of &lt;a href="http://www.apress.com/book/view/9781430209737"&gt;Practical API Design&lt;/a&gt; by Jaroslav Tulach. The book describes the set of standards developed by the Netbeans team during the last ten years, from framework novices to their current state of maturity.&lt;br /&gt;&lt;br /&gt;Even if you don't agree with some of the book recommendations (which is ok, since they aim at being a bit controversial) it is still a complete source of good practices when designing - not an application, but - a platform. If your case is not as much about "design" but "design while ignoring how many people are using your API and which parts are found useful", this book is a must-read.&lt;br /&gt;&lt;br /&gt;If I had to complain about something, it spends too much time in the introduction chapters. It sets the foundation stone of good API design and I really needed to grab some perspective about API stability over time, but a bit shorter would have made it perfect.&lt;br /&gt;&lt;br /&gt;I love the freshness in the style, the way it seems to be designed with my case in mind. A quote after several pages describing its intended audience: &lt;br /&gt;&lt;br /&gt;&lt;code&gt;If you are still asking yourself wether you should read the book or not, there is a much shorter answer: "Yes, you should read this book!"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Indeed :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-8349494664288251900?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/8349494664288251900/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/10/practical-api-design-book.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/8349494664288251900?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/8349494664288251900?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/10/practical-api-design-book.html" title="&quot;Practical API Design&quot;, the book" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_5fcKwF7jItc/SOEZmyRehfI/AAAAAAAAAPM/zUe_BDsxgQ0/s72-c/Still+learning.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry gd:etag="W/&quot;CkAMRXg5eCp7ImA9WxVWFEw.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-4135540528142855782</id><published>2008-10-08T08:00:00.003+02:00</published><updated>2009-02-23T18:39:44.620+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-23T18:39:44.620+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><title>The coolest prototype calendar ever</title><content type="html">Roberto has been doing amazing things with calendars these days. With current loom trunk, this java attribute:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;@Temporal(TemporalType.TIMESTAMP)&lt;br /&gt;@DateValidation(minValue="today + 1d", maxValue="today + 1y")&lt;br /&gt;private Date start;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With this javascript code:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;// create calendar&lt;br /&gt;$$('input.date, input.dateTime').each( loom.ui.createCalendar, {});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Renders this absolute piece of awesomeness:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/SOpL0PiDi_I/AAAAAAAAAPU/6eO2XBwzAxQ/s1600-h/calendar-snapshot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/SOpL0PiDi_I/AAAAAAAAAPU/6eO2XBwzAxQ/s400/calendar-snapshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5254095276175756274" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Dates before today and after one year (from today) are disabled, as specified by @DateValidation. As always, the server side will use the same constraints.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The use of &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/persistence/Temporal.html"&gt;@Temporal&lt;/a&gt; (the standard JPA annotation) triggers the interface to ask for date and time. The default would only ask for a date.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It uses the same attributes (min, max) and format (ISO-8601) specified by the HTML 5 draft, which should help replacing it when browser support arrives by 2025 :)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Full implementation of the PHP &lt;a href="http://es.php.net/strftime"&gt;strftime&lt;/a&gt; format.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Other use cases may be tested &lt;a href="http://loom.sourceforge.net/jstests/loom/test/js/calendar/calendar-test.html"&gt;here&lt;/a&gt;, as the javascript library can be used standalone without loom.&lt;br /&gt;&lt;br /&gt;MY PRECIOUSSSSSSSSS...&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: The calendar has been included in the 1.0 release. Just &lt;a href="http://repo2.maven.org/maven2/org/extrema-sistemas/loom/loom/1.0/loom-1.0.jar"&gt;download the jar file&lt;/a&gt;, uncompress, and look inside the js folder.&lt;br /&gt;&lt;strong&gt;UPDATE 2&lt;/strong&gt;: As of loom 1.1 we have added a &lt;a href="https://sourceforge.net/project/showfiles.php?group_id=200887"&gt;loom-javascript.zip&lt;/a&gt; file which includes just the javascript and css files. To use it, just take a look at the included html test files. They are not pretty, but should indicate a clear way to use the components.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-4135540528142855782?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/4135540528142855782/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/10/coolest-prototype-calendar-ever.html#comment-form" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4135540528142855782?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4135540528142855782?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/10/coolest-prototype-calendar-ever.html" title="The coolest prototype calendar ever" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_5fcKwF7jItc/SOpL0PiDi_I/AAAAAAAAAPU/6eO2XBwzAxQ/s72-c/calendar-snapshot.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">8</thr:total></entry><entry gd:etag="W/&quot;CkcARnY8eyp7ImA9WxRWE0Q.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-2202164468439061834</id><published>2008-09-25T19:13:00.005+02:00</published><updated>2008-10-30T19:07:27.873+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-10-30T19:07:27.873+01:00</app:edited><title>My take on the SpringSource license change</title><content type="html">I have real problems getting what all this fuss is about:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There is a global economic crisis, in case you didn't notice. It affects SpringSource as much as anyone else.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;They do numbers. They notice that a big share of their costs are related to maintaining legacy versions for non-paying customers.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;They react to minimize impact, while still being open.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I DON'T HAVE THEIR NUMBERS. I cannot have an opinion about if this is a good move or not. Neither do any of you. Please stop saying how you would react if you were the boss of a multimillion dollar company and had to face an unknown problem: "Oh, I would stay on top of the Titanic seeing everything sinking in all its glory, but would not move an inch away from openness", or "I would change the business model and put more stress on training and consulting - no, I don't have a clue about how much they make from consulting, is that important?"&lt;br /&gt;&lt;br /&gt;It's funny how most people agree that they wouldn't do a better job developing a java integration framework, but they do know better when talking about management. I frankly prefer this move than some kind of Lehman Brothers thing happening to a framework that is at the core of all the systems I have designed in the last few years. &lt;br /&gt;&lt;br /&gt;I am only posting this because dissatisfied customers are 4x more visible, and I don't think it's fair to the good work these guys are doing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-2202164468439061834?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/2202164468439061834/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/09/my-take-on-springsource-license-change.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2202164468439061834?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2202164468439061834?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/09/my-take-on-springsource-license-change.html" title="My take on the SpringSource license change" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></entry><entry gd:etag="W/&quot;DkcEQ307fip7ImA9WxRREkg.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-8889312811471327689</id><published>2008-09-24T13:00:00.000+02:00</published><updated>2008-09-24T13:00:02.306+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-24T13:00:02.306+02:00</app:edited><title>Recycle your HTML-fu</title><content type="html">&lt;img style="float:left; margin:0 10px 10px 0;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/SNoSra4tU6I/AAAAAAAAAO8/gvwntps9Nqg/s1600/your-ideas-may-be-wrong.jpg" border="0" title="Your ideas may be wrong" alt="Your ideas may be wrong" id="BLOGGER_PHOTO_ID_5249528852814582690" /&gt;&lt;br /&gt;Recycling is good. &lt;br /&gt;&lt;br /&gt;If you look around right now, you have high chances of being the oldest piece of furniture in the room. Maybe it's time to consider some recycling, and this post is trying to help by telling things you may already know - or maybe not.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Little did you know, &amp;lt;META name="keywords"&gt; &lt;a href="http://code.google.com/webstats/2005-12/metadata.html"&gt;is being ignored&lt;/a&gt; by Google and others. Google has &lt;a href="http://en.wikipedia.org/wiki/Meta_element#cite_ref-12"&gt;confirmed&lt;/a&gt; that they do  not trust this information.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;On your &lt;a href="http://www.infoq.com/news/2008/07/ie6_on_its_way_out"&gt;way out of ie6&lt;/a&gt;, you may consider using Dean Edwards' &lt;a href="http://code.google.com/p/ie7-js/"&gt;ie7.js&lt;/a&gt;. It's an excellent library that provides transparent PNGs, advanced CSS selectors like first-child and last-child, multiple CSS classes... It also includes some missing features for ie7.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I already mentioned this a couple of times: &lt;a href="http://loom.extrema-sistemas.org/doc/1.x/ref/xhtml-and-html-5"&gt;you are not using XHTML&lt;/a&gt;. It's nice to use well-formed HTML pages, but don't take it too seriously.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When using &lt;a href="http://www.w3.org/html/wg/html5/#script"&gt;script&lt;/a&gt; tags, you may &lt;a href="http://code.google.com/webstats/2005-12/scripting.html"&gt;omit the language and type attributes&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;All HTML validators will complain if you try to follow any of these practices, but it is time they get properly updated. For example, according to the standard a label with a nested input field &lt;a href="http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.9.1"&gt;does not need to provide a "for" attribute&lt;/a&gt;, which is  something that validators tend to forget.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Closely related to the previous point, the only up-to-date (beta) HTML validators I know of are &lt;a href="http://validator.nu/"&gt;validator.nu&lt;/a&gt; and the &lt;a href="http://qa-dev.w3.org/wmvs/HEAD/"&gt;W3C HTML 5 validator&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When developing your next REST web portal, keep in mind that GET links must be idempotent, which is a cool way to say that they should not modify anything in the database. For anything else, consider using &lt;a href="http://ajaxian.com/archives/ruby-on-rails-uses-ajax-to-simulate-post-links"&gt;POST links&lt;/a&gt; or forms.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Speaking of which, POST is the only safe way to prevent web crawlers from visiting links. Do not use &lt;a href="http://en.wikipedia.org/wiki/Nofollow"&gt;rel="nofollow"&lt;/a&gt; for this purpose.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;These are random thoughts that were raised to the table in our last web training week in Zurich. As the funny note of the week, I took a break to buy a &lt;a href="http://www.printfection.com/retro-future/Certifiable-Mad-Genius-T-Shirts/_s_59953"&gt;t-shirt&lt;/a&gt; for my sister, who is obtaining her Ph.D. with the very same CERN guys that are working on the &lt;a href="http://en.wikipedia.org/wiki/Safety_of_the_Large_Hadron_Collider"&gt;Large Hadron Collider&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Talk about twisted sense of humor :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-8889312811471327689?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/8889312811471327689/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/09/recycle-your-html-fu.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/8889312811471327689?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/8889312811471327689?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/09/recycle-your-html-fu.html" title="Recycle your HTML-fu" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_5fcKwF7jItc/SNoSra4tU6I/AAAAAAAAAO8/gvwntps9Nqg/s72-c/your-ideas-may-be-wrong.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;A0QNQ30_fCp7ImA9WxdaGU0.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-3174303251095720464</id><published>2008-08-25T16:15:00.003+02:00</published><updated>2008-08-28T09:43:12.344+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-08-28T09:43:12.344+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><category scheme="http://www.blogger.com/atom/ns#" term="xss" /><title>XSS patch for tomcat, jetty and glassfish</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/SK6SVQXajdI/AAAAAAAAALE/qc8a2gdHFOQ/s1600-h/security2.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/SK6SVQXajdI/AAAAAAAAALE/qc8a2gdHFOQ/s400/security2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5237284310546550226" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In case you didn't know, any application with a JSP page that uses ${foobar} with user-introduced data is inherently insecure. The &lt;em&gt;10-seconds Intro To The Joy Of XSS&lt;/em&gt; goes a little like this:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Go to some data input form.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Enter "&amp;lt;script&gt;window.location.href='http://badguys.com'&amp;lt;/script&gt;" in any field&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Wait until my entry shows up in somebody else's authenticated browser, typically as part of some listing.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;This technique can be used for practically anything, from deleting your data to inserting a hidden iframe that points to the Black Hat version of the Yellow Brick Road. This will happen in all java-based app servers, since the standard says &lt;a href="http://raibledesigns.com/rd/entry/java_web_frameworks_and_xss"&gt;not to escape XML contents&lt;/a&gt; inside EL expressions (that ${...} thingy).&lt;br /&gt;&lt;br /&gt;After the loom 1.0-rc1 release there were lots of people testing the &lt;a href="http://loom.extrema-sistemas.org/loomdemo"&gt;demo application&lt;/a&gt; - well, on July 1st we had the same amount of traffic of the entire month of June. The server stats showed many smart attempts at XSS security: special characters, scripts, images and iframes were the general note.&lt;br /&gt;&lt;br /&gt;Loom itself escape XSS attacks using an optimized version of the &lt;a href="http://code.google.com/docreader/#p(doctype)s(doctype)t(ArticlesXSS)"&gt;Google doctype recommendation&lt;/a&gt;. But two of our favorite servers (tomcat and jetty) were not doing it: these tags got printed wherever a "${customer.name}" was used, so we decided to do something about it. And here is were Rafa Serrano (werkins) took the lead.&lt;br /&gt;&lt;br /&gt;Rafa is the smart, not-yet-thirty-year-old guy that works at our Madrid office (three meters to my right, to be exact), that for some reason I have yet to understand has got two university degrees and is working on the third - I can only suppose the TV has been broken at his house for, like, years. Since I had a great summer totally stuck with my tasks, he decided to take this one out of my stack and implement it himself.&lt;br /&gt;&lt;br /&gt;The results are two patches filed for both &lt;a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=45652"&gt;Tomcat&lt;/a&gt; and &lt;a href="https://jsp.dev.java.net/issues/show_bug.cgi?id=9"&gt;Glassfish&lt;/a&gt;, the last one valid for jetty too. Please, if you have a voice in the Tomcat or Glassfish communities cast a vote for these, as they are a step towards a more secure web.&lt;br /&gt;&lt;br /&gt;In case you need security NOW and cannot wait for the new release of your server (yep - that sounds definitely as me), we have included a patched jar for our rc2 release, which can be grabbed &lt;a href="http://loom.svn.sourceforge.net/viewvc/loom/trunk/loom/src/lib/xss_patch/"&gt;from subversion&lt;/a&gt; right now. The folder includes a jar that should be dropped into your server lib folder, a test JSP page, and instructions to use. Note that if you still want unescaped HTML content you can use &amp;lt;c:out escapeXml="false"&gt; but that should be the &lt;em&gt;exception&lt;/em&gt;, not the &lt;em&gt;norm&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;Well, have a secure summer - or something. Cheers!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-3174303251095720464?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/3174303251095720464/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/08/xss-patch-for-tomcat-jetty-and.html#comment-form" title="9 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/3174303251095720464?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/3174303251095720464?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/08/xss-patch-for-tomcat-jetty-and.html" title="XSS patch for tomcat, jetty and glassfish" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_5fcKwF7jItc/SK6SVQXajdI/AAAAAAAAALE/qc8a2gdHFOQ/s72-c/security2.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total></entry><entry gd:etag="W/&quot;D0IESX4_cSp7ImA9WxdWF0U.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-4795441894197934666</id><published>2008-07-01T07:01:00.008+02:00</published><updated>2008-07-11T16:11:48.049+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-07-11T16:11:48.049+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="web" /><title>A way to get rid of IE 6</title><content type="html">This got posted at &lt;a href="http://www.webappers.com"&gt;webappers&lt;/a&gt; yesterday, a script to hint your IE 6 users to upgrade:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.savethedevelopers.org/"&gt;http://www.savethedevelopers.org/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It's still buggy, but I love the idea. It's also good timing, as our team was considering dropping IE 6 support for some of our widgets and render javascript-less HTML instead.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; It's a fact. 37signals will also drop support of IE6 &lt;a href="http://37signals.blogs.com/products/2008/07/basecamp-phasin.html"&gt;starting August 15&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-4795441894197934666?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/4795441894197934666/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/07/way-to-get-rid-of-ie-6.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4795441894197934666?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4795441894197934666?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/07/way-to-get-rid-of-ie-6.html" title="A way to get rid of IE 6" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry gd:etag="W/&quot;D0ENRXY7fCp7ImA9WxJWGUk.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-6010069892294231618</id><published>2008-06-30T09:15:00.002+02:00</published><updated>2009-06-25T18:01:34.804+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-25T18:01:34.804+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><category scheme="http://www.blogger.com/atom/ns#" term="talks" /><category scheme="http://www.blogger.com/atom/ns#" term="web" /><title>Lessons learned on the web layer</title><content type="html">I am leaving an intense week behind: first, I just posted my &lt;a href="http://www.slideshare.net/icoloma/lessons-learned-on-the-web-layer"&gt;Lessons learned on the web layer&lt;/a&gt; slides for &lt;a href="http://javahispano.org/openjavaday/index.html"&gt;Open Java Day&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div style="width: 425px; text-align: left;" id="__ss_492271"&gt;&lt;object style="margin: 0px;" height="355" width="425"&gt;&lt;param name="movie" value="http://static.slideshare.net/swf/ssplayer2.swf?doc=openjavaday-1214803055074421-9"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=openjavaday-1214803055074421-9" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="355" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size: 11px; font-family: tahoma,arial; height: 26px; padding-top: 2px;"&gt;&lt;a href="http://www.slideshare.net/?src=embed"&gt;&lt;img src="http://static.slideshare.net/swf/logo_embd.png" style="border: 0px none ; margin-bottom: -5px;" alt="SlideShare" /&gt;&lt;/a&gt; | &lt;a href="http://www.slideshare.net/icoloma/lessons-learned-on-the-web-layer?src=embed" title="View Lessons learned on the web layer on SlideShare"&gt;View&lt;/a&gt; | &lt;a href="http://www.slideshare.net/upload?src=embed"&gt;Upload your own&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This has been my first, and it has been an amazing experience. Great conversation during lunch, and some Ribeiro and Albariño drinks on Friday that left some photos I surely will regret when they get online... At the end, I got out with a few contacts and a lot of new friends. Totally worth it.&lt;br /&gt;&lt;br /&gt;On related news:&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0px auto 10px;" src="http://2.bp.blogspot.com/_5fcKwF7jItc/SFqvxUEI97I/AAAAAAAAAJg/ThydVLSWwSQ/s1600/party2.jpg" alt="PARTY TIME!!!" id="BLOGGER_PHOTO_ID_5213672780369295282" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;LOOM 1.0 IS OUT!&lt;/strong&gt; (well, it's the first release candidate, but bear with me anyway). I have invested a couple of full-time months getting everything in place before the talk, and though there is still room for improvement you have to cut the release somewhere. So this is it.&lt;br /&gt;&lt;br /&gt;I am not suggesting that anybody should _use_ it, not until it grows some community, but I would be grateful for any kind of feedback: what are you missing in it, what may be cut out, and why. If you feel like giving it a try, go to &lt;a href="http://loom.extrema-sistemas.org/loomdemo/ebanking/Mortgages.action?event=preEdit"&gt;the new demo&lt;/a&gt; - which, I must say, does not look like java at all.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;P.S.&lt;/strong&gt;: You cannot possibly imagine how we are living the Euro Cup results here in Spain. It's crazy ^_^&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-6010069892294231618?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/6010069892294231618/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/06/lessons-learned-on-web-layer.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/6010069892294231618?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/6010069892294231618?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/06/lessons-learned-on-web-layer.html" title="Lessons learned on the web layer" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_5fcKwF7jItc/SFqvxUEI97I/AAAAAAAAAJg/ThydVLSWwSQ/s72-c/party2.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry gd:etag="W/&quot;DU8EQ38-eSp7ImA9WxJVE0k.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-3343343723149748476</id><published>2008-06-03T10:00:00.008+02:00</published><updated>2009-06-30T09:43:22.151+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-30T09:43:22.151+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><category scheme="http://www.blogger.com/atom/ns#" term="html" /><title>Extend HTML with your own metadata</title><content type="html">&lt;img style="margin: 0pt 0pt 10px 20px; float: right; cursor: pointer;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/SEO3_pUkoPI/AAAAAAAAAJY/ESqWxpTR380/s400/HavingSoMuchFun.jpg" alt="Ah, the endless joy of specs" title="Ah, the endless joy of specs" id="BLOGGER_PHOTO_ID_5207207898222207218" border="0" /&gt;&lt;br /&gt;You know that thing about space shuttle rockets being &lt;a href="http://www.astrodigital.org/space/stshorse.html"&gt;two-horse-asses-wide&lt;/a&gt;. If you want to save some time, the motto of the story is &lt;strong&gt;Specifications and bureaucracies live forever&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;I do love HTML despite the fact that we are still tied to the HTML 4 spec (1999). I would find use for a couple more attributes, call it semantic sugar or whatever.&lt;br /&gt;&lt;br /&gt;Let's talk about an example use case to validate some number between 0 (inclusive) and 100 (exclusive).&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Option 1: inject JSON attributes&lt;/h2&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;input type="text" name="foo" id="foo"/&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script&gt;&lt;br /&gt;// one of these must be added per input field&lt;br /&gt;Object.extend($('foo'), {&lt;br /&gt;   min: 0,&lt;br /&gt;   max: 100,&lt;br /&gt;   excludeMin: false,&lt;br /&gt;   excludeMax: true   &lt;br /&gt;});&lt;br /&gt;&amp;lt;/script&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;pros&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Even my grandma can get this code.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;cons&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;With any mid-size form there will be like a ton of script tags opening and closing all the time. Even if you manage to put them all together, you will not feel very proud of it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Every input field must have a &lt;em&gt;unique&lt;/em&gt; ID attribute to locate it for extension.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You should NOT generate javascript code from your server side. Ever.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The most important: the DOM node must exist prior to extending it. This is an uncomfortable restriction about where to put this javascript snippet, most probably after a &amp;lt;/form&gt; closing tag which is a serious problem if you plan to develop a tag library where you do not know the surrounding HTML code.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Inject attributes using CSS&lt;/h2&gt;&lt;br /&gt;Lots of people do this (&lt;a href="http://plugins.jquery.com/"&gt;jQuery plugin&lt;/a&gt; authors, for example):&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;input type="text" name="field" class="extended __min_0 __max_100 __excludemin_false __excludemax_true"/&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script&gt;&lt;br /&gt;// this must be called just once for the entire page&lt;br /&gt;document.observe('dom:loaded', function() {&lt;br /&gt; $$('.extended').each(function(element) {&lt;br /&gt;   element.className.scan(/__(\w+)_(\w+)/, function(p) { element[p[1]] = p[2] });&lt;br /&gt; });&lt;br /&gt;});&lt;br /&gt;&amp;lt;/script&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;pros&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It does not have any of the JSON problems.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It is a valid use of the CSS class. From &lt;a href="http://www.w3.org/TR/REC-html40/struct/global.html#h-7.5.2"&gt;the HTML spec&lt;/a&gt;: "The class attribute has several roles in HTML: [...] For general purpose processing by user agents."&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;cons&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;UGLY. Again. Sure, for simple cases it just seems right, but try to explain that to your web designer next time he asks about a "required date __min_2005-10-20T10:00:00Z __something_ortheother". When the real world challenges your application, your HTML gets a legacy reek.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You are mixing up style and behavior metadata in the same attribute.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You will have to UrlEncode values that contain special characters (double quotes, spaces, etc).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Namespaced attributes&lt;/h3&gt;&lt;br /&gt;I like this extensibility thing about XHTML:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;html&lt;br /&gt;   xmlns:v="http://myhost/schemas/validation"&lt;br /&gt;   xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;br /&gt;&amp;lt;input type="text" name="field" v:min="0" v:max="100" v:excludemin="false" v:excludemax="true"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is used by &lt;a href="http://www.w3.org/TR/2008/WD-wai-aria-20080204/"&gt;W3C ARIA&lt;/a&gt; and &lt;a href="http://cwiki.apache.org/WICKET/wickets-xhtml-tags.html"&gt;wicket&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;pros&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;At last, something I can look at.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;cons&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You must develop and maintain your own XML schema for everything you extend.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You think you are going XML? &lt;a href="http://www.loom.extrema-sistemas.com/doc/0.8.x/ref/xhtml-and-html-5"&gt;you are not&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Long story short, your browser is parsing your page as HTML (not XML) unless you include an "application/xhtml+xml" http header. Once you do, everything is fine: any subtle mistake in your XML document will render a blank page in the browser, javascript snippets get picky about their contents, and oh, it will not work at all in any IE browser. That is what "fine" means in my book.&lt;br /&gt;&lt;br /&gt;The word out is that browser vendors are focusing in supporting HTML 5 instead of XHTML 1 and 2, but as long as you know what you are doing, schemas work just fine.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;HTML attributes&lt;/h3&gt;&lt;br /&gt;The alternative to XHTML is HTML tag soup, which is more or less similar to XHTML. Anything that is not recognized by the browser is valid, and as long as they belong to valid tags they can be reached from your javascript code. This is exploited by &lt;a href="http://dojotoolkit.org/"&gt;the dojo toolkit&lt;/a&gt; and &lt;a href="http://www.w3.org/html/wg/html5/#embedding"&gt;HTML 5&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;input type="text" name="field" min="0" max="100" data-exclude-min="false" data-exclude-max="true"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this example I have included some HTML 5 attributes and &lt;a href="http://www.w3.org/html/wg/html5/#embedding"&gt;preceded the rest with "data-"&lt;/a&gt;. It would be nice if the HTML group takes some advice from &lt;a href="http://ajaxian.com/archives/ajax-pioneer-week-john-resig-of-jquery"&gt;John Resig&lt;/a&gt; and listen to the community about what is needed in the spec. Out of the ten validation parameters I use frequently, HTML 5 only supports three - min, max, and pattern.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;pros&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Here goes my grandma again, lecturing me about how much she loves the simplicity in this code.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;cons&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You may not put cool buzzwords in your resume.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;And Loom?&lt;/h3&gt;&lt;br /&gt;Which one have we tried? All of them. Well, mostly.&lt;br /&gt;&lt;br /&gt;It's been a long year, and after having our own cup of pain we ended up  settling for CSS classes to indicate the field type (number, string, date, required) since they can be associated to style, and HTML 5 for everything else. Other alternatives are fine, just make sure you get the whole picture before trying.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Drop by Madrid to enjoy some deeper insight&lt;/h3&gt;&lt;br /&gt;Do not miss the &lt;a href="http://javahispano.org/openjavaday/index.html"&gt;Open Java Day&lt;/a&gt; event organized by JavaHispano and Sun on June 26-27. I'll be talking about practical lessons learned from the web tier: CSS frameworks, charset handling, javascript tests, accessibility, etc. I don't know how much I can fit into one hour, but it will be fun.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;UPDATE&lt;/span&gt;: John Resig has added further insight on this subject &lt;a href="http://ejohn.org/blog/html-5-data-attributes/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-3343343723149748476?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/3343343723149748476/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/06/extend-html-with-your-own-metadata.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/3343343723149748476?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/3343343723149748476?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/06/extend-html-with-your-own-metadata.html" title="Extend HTML with your own metadata" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_5fcKwF7jItc/SEO3_pUkoPI/AAAAAAAAAJY/ESqWxpTR380/s72-c/HavingSoMuchFun.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></entry><entry gd:etag="W/&quot;DU8CQn87cCp7ImA9WxRTEkg.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-6123362586653754527</id><published>2008-04-24T13:00:00.004+02:00</published><updated>2008-09-01T10:31:03.108+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-01T10:31:03.108+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="spring" /><title>Mock Your Spring Config for Fun and Profit</title><content type="html">&lt;img style="float:right; margin:0 0 10px 10px;" src="http://2.bp.blogspot.com/_5fcKwF7jItc/R-gXmIUjzeI/AAAAAAAAAJA/Mso4vTZAK98/s400/eeeye_of_the_tigeeer.jpg" border="0" alt="eeeeeeye of the tigeeeeeeer!!!" title="eeeeeeye of the tigeeeeeeer!!!" id="BLOGGER_PHOTO_ID_5181417315125939682" /&gt;&lt;br /&gt;&lt;br /&gt;"Nemesis: An opponent that cannot be beaten or overcome." (&lt;a href="http://www.answers.com/main/ntquery?s=nemesis&amp;gwp=13"&gt;Answers.com&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;I used to be quite proud of our spring config structure, which is the result of some trial-and-error game while trying to achieve the perfect pluggable system. I would much prefer avoiding the typical LDAP / Oracle / BankingHost-with-potatoes combo just to debug the web interface while my laptop screams wildly. Besides, I'm green: I love saving power by keeping the system startup under 15 seconds. &lt;br /&gt;&lt;br /&gt;Note that I said "green", not "impatient". Green. Ecological, say. &lt;br /&gt;&lt;br /&gt;But last month I met &lt;em&gt;The Adversary&lt;/em&gt;: twelve systems to integrate, a different environment for test, production and disaster recovery, four different web applications that communicate using web services, up to four different databases... Our spring mojo was not cutting it, so we had to take it to the next level. &lt;br /&gt;&lt;br /&gt;What follows are all the alternatives that we tried, what worked and what didn't:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;PropertyPlaceHolderConfigurer&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;The bread and butter of application configuration, this class gets into your spring file and you start injecting values anywhere using the classic ant-like syntax we all know and love:&lt;br /&gt;&lt;br /&gt;config.properties:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;mail.user=foo&lt;br /&gt;mail.password=bar&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;spring-config.xml:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;&lt;br /&gt;&amp;lt;property name="location" value="classpath:config.properties"/&gt;&lt;br /&gt;&amp;lt;/bean&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;bean class="com.acme.SomeClass" p:username="${mail.user}" p:password="${mail.password}"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;PropertyPlaceHolderConfigurer&lt;/code&gt; includes a &lt;code&gt;systemPropertiesMode&lt;/code&gt; attribute to override configuration using the command line. For example, this example could be overriden adding &lt;code&gt;-Dmail.user=baz&lt;/code&gt; to the server startup script.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Spring imports&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;This is an old habit of mine, a "main index file" that imports everything else. It is a bit cleaner than specifying every file in the web.xml descriptor:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;br /&gt;&amp;lt;beans&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;import resource="spring-properties.xml"/&gt;&lt;br /&gt; &amp;lt;import resource="spring-mail.xml"/&gt;&lt;br /&gt; &amp;lt;import resource="spring-web.xml"/&gt;&lt;br /&gt; &amp;lt;import resource="spring-persistence.xml"/&gt;&lt;br /&gt; &amp;lt;import resource="spring-security.xml"/&gt;&lt;br /&gt; &amp;lt;import resource="spring-scheduler.xml"/&gt;&lt;br /&gt; &lt;br /&gt;&amp;lt;/beans&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Say I want to work at home, and for the time being I would prefer to replace "spring-mail.xml" with "mock-mail.xml". Well, &lt;code&gt;PropertyPlaceHolderConfigurer&lt;/code&gt; cannot be used for this because substitution is applied &lt;em&gt;after&lt;/em&gt; the imports are executed, instead of &lt;em&gt;before&lt;/em&gt;. &lt;br /&gt;&lt;br /&gt;Our first idea was to process this file using ant to replace placeholders like ${spring.mail.file} with the actual file location. It works, but launching ant after every change in the environment is time-consuming, anti-agile, and plain ugly. You also end up manually inspecting files to see which mocks are being applied, which sucks.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Spring JavaConfig&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.springframework.org/javaconfig"&gt;Spring JavaConfig&lt;/a&gt; is a promising little project still in its alpha stage. It aims at providing a more flexible way to configure spring using your favorite JVM language (java, groovy, etc). Definitely a good idea.&lt;br /&gt;&lt;br /&gt;At first sight it may cut it:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;@Configuration&lt;br /&gt;public abstract class MockConfiguration {&lt;br /&gt;&lt;br /&gt;  /** set to true to activate mock */&lt;br /&gt;  private boolean mockMail;&lt;br /&gt;&lt;br /&gt;  @Bean&lt;br /&gt;  public MailInterface mail() {&lt;br /&gt;    return mockMail? mockMail() : realMail();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // The main xml file should define both real and mock beans,&lt;br /&gt;  // we just proxy them&lt;br /&gt;  @ExternalBean&lt;br /&gt;  public abstract MailInterface realMail();&lt;br /&gt;&lt;br /&gt;  @ExternalBean&lt;br /&gt;  public abstract MailInterface mockMail();&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This class is like advanced maths courses at my University: it is correct, works, is kinda cute, and is totally useless.&lt;br /&gt;&lt;br /&gt;This solution involves too much code &lt;em&gt;per mocked system&lt;/em&gt;. With about a dozen external systems, &lt;code&gt;MockConfiguration&lt;/code&gt; can get quite huge, and your (integration) tests can get quite complicated since they will include parts of the global system, where no "mail" bean will be defined.&lt;br /&gt;&lt;br /&gt;In my experience, Spring JavaConfig is a great solution looking for an appropriate problem, which mine is not. We moved on.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Placeholders in ApplicationContext&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;I found this while scanning some spring sources. Suppose your web.xml file includes several spring files:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;context-param&gt;&lt;br /&gt; &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;&lt;br /&gt; &amp;lt;param-value&gt;classpath:spring/myconfig.xml,classpath:spring/otherconfig.xml&amp;lt;/param-value&gt;&lt;br /&gt;&amp;lt;/context-param&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Well, according to &lt;a href="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.html#resolvePath(java.lang.String)"&gt;AbstractRefreshableConfigApplicationContext.resolvePath&lt;/a&gt;, you could also have used placeholders:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&amp;lt;context-param&gt;&lt;br /&gt; &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;&lt;br /&gt; &amp;lt;param-value&gt;classpath:spring/${SPRING_MAIL},classpath:spring/${SPRING_PERSISTENCE}&amp;lt;/param-value&gt;&lt;br /&gt;&amp;lt;/context-param&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then define them as system properties (using -D) or environment variables:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;export SPRING_MAIL=mock-mail.xml&lt;br /&gt;export SPRING_PERSISTENCE=mock-mail.xml&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;- or -&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;java -DSPRING_PERSISTENCE=mock-persistence.xml -DSPRING_MAIL=mock-mail.xml ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The only problem with this is that I don't like solutions that involve the system administrators too much: the war file is not fully configured by itself, which could be a concern, and involves an amount of work proportional to the number of servers to deploy. It is also a bit hard to automatize.&lt;br /&gt;&lt;br /&gt;Damn. This one has been long, and I hate long posts. Well, here it is in the hope that someone else finds it useful, or decides to contribute with his/her own experience. Back to work!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: You will see a comment by Mark Fisher that explains how to use PropertyPlaceHolderConfigurer with import statements, which renders the last part of this post useless so I decided to remove it (again: I hate long posts).&lt;br /&gt;&lt;strong&gt;UPDATE(2)&lt;/strong&gt;: For Yet Another Approach at spring config, check out &lt;a href="http://robertmarkbramprogrammer.blogspot.com/2008/02/nested-property-placeholders-in-spring.html#nested_c"&gt;this post&lt;/a&gt; by Robert Mark Bram.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-6123362586653754527?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/6123362586653754527/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/01/mock-your-spring-config-for-fun-and.html#comment-form" title="9 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/6123362586653754527?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/6123362586653754527?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/01/mock-your-spring-config-for-fun-and.html" title="Mock Your Spring Config for Fun and Profit" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_5fcKwF7jItc/R-gXmIUjzeI/AAAAAAAAAJA/Mso4vTZAK98/s72-c/eeeye_of_the_tigeeer.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total></entry><entry gd:etag="W/&quot;CkQNR34zeSp7ImA9WxZQEEU.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-3132243454818883967</id><published>2008-02-15T01:00:00.001+01:00</published><updated>2008-02-15T13:59:56.081+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-15T13:59:56.081+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><category scheme="http://www.blogger.com/atom/ns#" term="design" /><title>Design your products with real-life experience in mind</title><content type="html">&lt;img border="0" title="You sure got your requirements right?" style="float:right" src="http://1.bp.blogspot.com/_5fcKwF7jItc/R3ofN6ANWQI/AAAAAAAAAH4/CaGn3bVfOBc/s1600/seafood.jpg"/&gt;&lt;br /&gt;Real-life requirements are strokes of a bigger landscape. Designing for a partial set of requirements is bad practice because you do not have the photo where your application is expected to fit. In GoF patterns jargon we are talking about the &lt;em&gt;forces&lt;/em&gt; thing that prevents any pattern from being applicable to Each and Every Case. &lt;br /&gt;&lt;br /&gt;Things are much easier when you are developing customized software, but with products there are many more decisions to make. The old habit of &lt;a href="http://icoloma.blogspot.com/2007/05/give-me-implementations-not-specs.html"&gt;designing standards from scratch&lt;/a&gt; is stepping aside for experience-driven architecture, where existing use cases are the basement of further design. Even doing so, you will not get the whole picture until users start flooding your system, giving you feedback about how well your system fits The Real Thing and how many cases you left unresolved. &lt;br /&gt;&lt;br /&gt;&lt;h3&gt;My way (well, sorta)&lt;/h3&gt; &lt;br /&gt;When talking about products, I follow a very simple design process. Hey, I am not saying this is top-notch approach to product design or anything, just "there are many like it, but this one is mine".&lt;br /&gt;&lt;br /&gt;First of all, I always start &lt;span style="font-style:italic;"&gt;refusing to code&lt;/span&gt;. If you don't yet own a copy of &lt;a href="https://gettingreal.37signals.com/"&gt;Getting Real&lt;/a&gt;, I cannot recommend it enough (even more if you live in Europe, since they only charge you 2 &amp;euro; for shipping). From the book,&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;"Each time you say yes to a feature, you're adopting a child. You have to take your baby through a whole chain of events (e.g. design, implementation, testing, etc.) [...] Don't be a yes-man." - Getting Real, 37signals&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I like to call that &lt;a href="http://icoloma.blogspot.com/2006/10/antipattern-of-month-3-suicide.html"&gt;suicide requirements&lt;/a&gt;: every time a customer calls with a feature request, first try to apply standard YAGNI judo techniques, or give a workaround with existing features. When the same request appears frequently, accept the defeat and add it to the schedule. You lost.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;“…the cost of adding a feature isn't just the time it takes to code it. The cost also includes the addition of an obstacle to future expansion [...] The trick is to pick the features that don't fight each other.” - John Carmack.&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Do NOT develop anything without a real use case first (more than one would be better, but don't be picky). By doing this you are buying some certainty of avoiding features never needed by real life, and as a side effect helps prioritizing features and bugfixes.&lt;br /&gt;&lt;br /&gt;Delay the implementation as much as a reasonable schedule permits. Start talking to your Java hens, giving them time to lay the idea that will save the day, improve sexual life and alleviate global warming (&lt;a href="http://www.bugbash.net/comic/17.html"&gt;or not&lt;/a&gt;). I cannot stress this enough: I have lost entire months for not giving it enough pencil time.&lt;br /&gt;&lt;br /&gt;When implementation time comes, apply the best brewed design and cross fingers; if you are lucky, you won't have to change it THAT much.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-3132243454818883967?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/3132243454818883967/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/02/design-your-products-with-real-life.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/3132243454818883967?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/3132243454818883967?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/02/design-your-products-with-real-life.html" title="Design your products with real-life experience in mind" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_5fcKwF7jItc/R3ofN6ANWQI/AAAAAAAAAH4/CaGn3bVfOBc/s72-c/seafood.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;CUcHRn8_fSp7ImA9WxZREkQ.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-4047595737117294515</id><published>2008-02-05T01:00:00.000+01:00</published><updated>2008-02-06T11:17:17.145+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-02-06T11:17:17.145+01:00</app:edited><title>Integrate your framework with JavaRebel - NOW</title><content type="html">&lt;img title="See new beans coming" style="float:right; margin:0 0 10px 10px" src="http://4.bp.blogspot.com/_5fcKwF7jItc/R6XFBaPP5iI/AAAAAAAAAIw/2S6k94QwfZ4/s1600/coffee%2Bbeans.jpg"/&gt;&lt;br /&gt;In case you missed it, on-the-fly reloading is the hot thing right now.&lt;br /&gt;&lt;br /&gt;A great argument in the everything-vs-Java flame war says that you can miss your children growing while waiting for the change-redeploy-test cycle to complete. Matt Raible and Tim Fennell expressed it as a reasonable idea: &lt;a href="http://raibledesigns.com/rd/entry/all_web_frameworks_should_support"&gt;web frameworks should redeploy actions on the run&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Changing JVM bytecode has been possible since JVM 1.4: your IDE will send updated bytecode to the debugged VM, given that no changes affect the class structure (attributes or methods), and &lt;a href="http://www.zeroturnaround.com/javarebel/"&gt;JavaRebel&lt;/a&gt; was born to fill that niche. Starting with JavaRebel 1.1M1, there is a &lt;a href="http://groups.google.com/group/zeroturnaround-community/browse_thread/thread/b908fc0c546e1b41"&gt;SDK&lt;/a&gt; to be notified about class reloading events. After seeing how Jevgeni successfully connected JavaRebel with Google Guice, I reserved a weekend to peek at the promising new release (isn't that what weekends are for? no? really?).&lt;br /&gt;&lt;br /&gt;Three hours. That's all it took.&lt;br /&gt;&lt;br /&gt;Within three hours, Loom was detecting and redeploying modified actions. Configure your eclipse project to use &lt;a href="http://icoloma.blogspot.com/2007/10/howto-uncompressed-web-projects-in.html"&gt;uncompressed folders&lt;/a&gt; (which you should have done already), add the javarebel agent ("-noverify -javaagent:${REBEL_HOME}/javarebel.jar"), and that's all. Changes to action classes will be applied on the next browser refresh.&lt;br /&gt;&lt;br /&gt;The point you should remember is the following: it took me a couple of hours to get it working. Start making noise in your favorite forums: hibernate, struts, wicket, whatever - there is no such concept as "loud enough". Reclaim your hot-redeploy features. &lt;br /&gt;&lt;br /&gt;I would love to see this feature included in the JDK some day :)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;P.S.&lt;/strong&gt;: To be fair, getting the first action reloaded took was really fast, but a real polished behavior in jetty and tomcat, reloading as the affected JPA classes were modified took a bit more.&lt;br /&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; ZeroTurnaround has issued an &lt;a href="http://www.theserverside.com/news/thread.tss?thread_id=48340"&gt;open letter to the Java Community&lt;/a&gt; expressing their intention of supporting Java frameworks that plan to integrate on-the-fly class reloading.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-4047595737117294515?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/4047595737117294515/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2008/02/integrate-your-framework-with-javarebel.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4047595737117294515?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/4047595737117294515?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2008/02/integrate-your-framework-with-javarebel.html" title="Integrate your framework with JavaRebel - NOW" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_5fcKwF7jItc/R6XFBaPP5iI/AAAAAAAAAIw/2S6k94QwfZ4/s72-c/coffee%2Bbeans.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;AkEAQnczeip7ImA9WB9UFU4.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-8331486618131769144</id><published>2007-12-13T11:00:00.000+01:00</published><updated>2007-12-13T10:37:23.982+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-13T10:37:23.982+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="accessibility" /><title>A brief note about accessibility</title><content type="html">A note to all those who think: "everyone has flash installed and javascript enabled. We are not supporting Amish browser 1.0".&lt;br /&gt;&lt;br /&gt;I happened to install ubuntu 64 bits at work some months ago. Installing flash in a 64 bits linux distro is not straightforward and I haven't dedicated the time yet - after all, I seldom need to browse a flash site. But every now and then I happen to browse a website with a big white hole in the middle of the screen and a big NOTHING filling it, and have to guess what I am missing. It's okay (and expected) if I cannot see the last YouTube thing, but requiring flash to see a city map seems like a misfortunate decision. I ended up annotating the address and going to Google Maps instead.&lt;br /&gt;&lt;br /&gt;Think about accessibility like this: a little amount of possible customers that are going to move to other sites because your site is thought, designed and oriented for &lt;a href="http://en.wikipedia.org/wiki/White_Anglo-Saxon_Protestant"&gt;WASPs&lt;/a&gt;. Cell phone browsers do not have a mouse, the elderly may prefer bigger fonts, some users cannot have flash installed because his sysadmin is a security freak concerned about demons and aliens invading this dimension (not my case, again; I'm lazy). &lt;br /&gt;&lt;br /&gt;Your environment is not THE environment. There is a consistent percentage of your market that is going elsewhere - not because they don't like your website, just because you don't care.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-8331486618131769144?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/8331486618131769144/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2007/12/brief-note-about-accessibility.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/8331486618131769144?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/8331486618131769144?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2007/12/brief-note-about-accessibility.html" title="A brief note about accessibility" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;CUYMRnk8fyp7ImA9WxRUFUs.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-7534512647765959405</id><published>2007-12-11T10:30:00.003+01:00</published><updated>2008-11-24T22:46:27.777+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-24T22:46:27.777+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="jpa" /><category scheme="http://www.blogger.com/atom/ns#" term="loom" /><title>Validate web forms using JPA annotations</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" title="Something unexpected" href="http://loom.extrema-sistemas.com/"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/R09F4r4vEpI/AAAAAAAAAHo/03mxfE6_7QU/s400/something-unexpected.jpg" id="BLOGGER_PHOTO_ID_5138402540008706706" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;How many times I have repeated my database constraints to validate user input, I don't know. I don't like that. The RoR crowd nailed this with the ActiveRecord implementation and for the last two years the Java community has tried to imitate them.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;quote&gt;&lt;br /&gt;"The workman of today works every day in his life at the same tasks, and this fate is no less absurd. But it is tragic only at the rare moments when it becomes conscious." - Albert Camus, &lt;em&gt;The Myth of Sisyphus&lt;/em&gt;&lt;br /&gt;&lt;/quote&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;What about instead of trying to imitate the ActiveRecord strategy (which is counterintuitive since java classes are not intended to be modified on-the-fly) focus on our strengths: since java 5 &lt;em&gt;two different programming languages can be used in the same source file&lt;/em&gt;. Use one for  behavior (java) and another to define data structure (annotations) that can be used to generate the database tables and validate user input at the same time:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_5fcKwF7jItc/SSsgWCabNDI/AAAAAAAAAVI/T_gASzkDhbI/s1600-h/a-better-world.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_5fcKwF7jItc/SSsgWCabNDI/AAAAAAAAAVI/T_gASzkDhbI/s800/a-better-world.jpg" border="0" title="A better world" id="BLOGGER_PHOTO_ID_5272343351744214066" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We first tried extending the existing libraries (stripes, struts, jsf) and ended up developing a new web framework entirely from scratch. I am not going to replicate the whole &lt;a href="http://loom.extrema-sistemas.org/doc/1.x/ref/getting-started"&gt;getting started chapter&lt;/a&gt; here, but give a 10-sec introduction. With a source file like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;Mortgage.java:&lt;br /&gt;&lt;br /&gt;@Entity&lt;br /&gt;public class Mortgage extends AbstractProduct {&lt;br /&gt;&lt;br /&gt;@Basic(optional=false)&lt;br /&gt;@Column(length=80)&lt;br /&gt;private String address;&lt;br /&gt;&lt;br /&gt;@Basic(optional=false)&lt;br /&gt;@NumberValidation(maxValue="100", excludeMax=true)&lt;br /&gt;private BigDecimal interestRate;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;editMortgage.jsp:&lt;br /&gt;&lt;br /&gt;&amp;lt;l:form action="Mortgages" event="save"&gt;&lt;br /&gt; &amp;lt;l:inputText name="mortgage.address"/&gt;&lt;br /&gt; &amp;lt;l:inputText name="mortgage.interestRate"/&gt;&lt;br /&gt;&amp;lt;/l:form&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The framework will generate the following HTML:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;label for="mortgage.address"&gt;&amp;lt;span&gt;Dirección:&amp;lt;/span&gt;&lt;br /&gt;&amp;lt;input type="text" size="40" maxlength="80" name="mortgage.address" id="mortgage.address"&lt;br /&gt;  class="required string validate"/&gt;&amp;lt;/label&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;label for="mortgage.interestRate"&gt;&amp;lt;span&gt;Interés:&amp;lt;/span&gt;&lt;br /&gt;&amp;lt;input type="text" size="3" maxlength="3" name="mortgage.interestRate" id="mortgage.interestRate"&lt;br /&gt;  class="_options-excludeMax-true _options-maxValue-100 _options-minValue-0 _options-scale-0 number required validate"/&gt;&amp;lt;/label&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;For the curious, you can &lt;a href="http://loom.extrema-sistemas.org/loomdemo/ebanking/mortgages/create"&gt;see this form in action&lt;/a&gt; or &lt;a href="http://loom.extrema-sistemas.com/loomdemo/view-source/org.loom.demo.action.MortgagesAction?jspFilename=/WEB-INF/jsp/mortgages/edit.jsp"&gt;view the source files&lt;/a&gt;. There are some things you should notice:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Javascript validations are generated from JPA annotations:&lt;/strong&gt; try to introduce invalid input and see what happens.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The maxlength and size attributes have been generated &lt;strong&gt;from the same annotations used to generate the database&lt;/strong&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Javascript messages are internationalized&lt;/strong&gt;. Change from english to spanish (beware: decimal point changes from '.' to ','). Note that the property name is also translated.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;The validation CSS classes can define a different CSS style&lt;/strong&gt; for required, disabled, numeric fields and so on.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;The browser and the server use the same validation messages&lt;/strong&gt;. Javascript and Java share the same message templates.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Notice the size of the JSP file&lt;/strong&gt;, because real-life applications do not get much bigger than that&lt;/li&gt;&lt;br /&gt;&lt;li&gt;And &lt;strong&gt;the entire demo site will still work with javascript turned off&lt;/strong&gt;. Not that it matters that much,  but ain't it cool :)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The demo application also includes examples of &lt;a href="http://icoloma.blogspot.com/2007/02/listing-with-displaytag-stripes-and.html"&gt;paginated queries&lt;/a&gt; and an entire JPA + Spring configuration.&lt;br /&gt;&lt;br /&gt;I am writing this after returning from a vacation week in Zurich. I have the incredible luck of combining one of our best clients and one of my best friends in the same city. Periodically I get the chance to rediscover the city again: five-stories music shops, the Rhine Falls, charming people and oh-my-gawd-the-food with lots, lots of cheese... I definitely should lose some weight before the biggest and greatest of Christmas arrive. &lt;br /&gt;&lt;br /&gt;Wish you all the same. Merry Christmas everyone!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-7534512647765959405?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/7534512647765959405/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2007/12/validate-web-forms-using-jpa.html#comment-form" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/7534512647765959405?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/7534512647765959405?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2007/12/validate-web-forms-using-jpa.html" title="Validate web forms using JPA annotations" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_5fcKwF7jItc/R09F4r4vEpI/AAAAAAAAAHo/03mxfE6_7QU/s72-c/something-unexpected.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total></entry><entry gd:etag="W/&quot;DEQMRXk8fCp7ImA9WB9aGEU.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-2109091798849838906</id><published>2007-11-19T11:30:00.000+01:00</published><updated>2008-01-09T14:46:24.774+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-01-09T14:46:24.774+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><title>Avoid Flash Of Unstyled Content using javascript</title><content type="html">&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_5fcKwF7jItc/Rz4cir4vEoI/AAAAAAAAAHg/vBl-Y7Lz4AQ/s1600/flash-of-unpretty-html.jpg" border="0" alt="" /&gt;Don't hate it when the browser displays ugly HTML for a second, then thinks it twice and reformats it as expected?&lt;br /&gt;&lt;br /&gt;A Flash Of Unstyled Content (from now on FOUC) happens when the browser shows unstyled content before applying the CSS style sheet (&lt;a href="http://en.wikipedia.org/wiki/Flash_of_Unstyled_Content"&gt;wikipedia&lt;/a&gt;). It used to be associated to old versions of IE but also with accessible interfaces that offer richer javascript alternatives. Consider the following:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;List menu: the bare HTML should show all options expanded (in case javascript is off, the visitor is a search engine... &lt;a href="http://loom.extrema-sistemas.com/doc/0.8.x/ref/accessibility-and-508-compliance"&gt;pick your choice&lt;/a&gt;) and use javascript to collapse unselected options.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Tabs: same thing, show all tab contents and hide the unselected ones.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Any kind of collapsible interface, Blogger for instance: if the connection is slow you can glimpse the collapsed components while the browser downloads the entire document.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Solutions are not straightforward: components that do not yet exist in the DOM tree cannot be hidden using javascript so they must be created (and maybe displayed) before they can be manipulated. &lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Register a "DOM document loaded" event handler&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;An example using prototype:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;document.observe("dom:loaded", new function() {&lt;br /&gt;   $('myDiv').hide();&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This waits until the document is fully downloaded and will flash if the page is big enough or the connection crappy enough.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Place a &lt;&gt;script&gt; tag after the element&lt;/h3&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt; &amp;lt;div id="myDiv"&gt;&lt;br /&gt; ...&lt;br /&gt; &amp;lt;/div&gt;&lt;br /&gt; &amp;lt;script&gt;&lt;br /&gt;   $('myDiv').hide();&lt;br /&gt; &amp;lt;/script&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Yikes. You are desperate, right? This script requires that the DOM node already exists, and that sometimes means that the script tag must be placed somewhere else (next to the nearest ending DIV tag, for example). This would not work as a generic approach for a JSP tag (TabTag, MenuTag) that does not know how the surrounding HTML looks like.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;CSS sleight-of-hand&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Create a CSS style on the fly &lt;strong&gt;before&lt;/strong&gt; the affected HTML content:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;script&gt;&lt;br /&gt; document.write('&amp;lt;style type="text/css"&gt;.hide-fouc { display:none; }&amp;lt;/style&gt;');&lt;br /&gt; document.observe("dom:loaded", function() {&lt;br /&gt;   $$('.hide-fouc').invoke('removeClassName', 'hide-fouc');&lt;br /&gt; });&lt;br /&gt;&lt;br /&gt;&amp;lt;script&gt;&lt;br /&gt;&amp;lt;div id="myDiv" class="hide-fouc"&gt;&lt;br /&gt;  ...&lt;br /&gt;&amp;lt;div&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;A browser with javascript enabled will not see the component, but javascript-less browsers will. The CSS class is removed after the entire document has been loaded to allow the element to reappear later. &lt;br /&gt;&lt;br /&gt;Note that this snippet should be included inside the HTML (not as a external javascript file) and that you should achieve better performance by removing the display:none attribute from the CSS class instead of using a selector, but as said before I am a lazy bastard.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-2109091798849838906?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/2109091798849838906/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2007/11/avoid-flash-of-unstyled-content-using.html#comment-form" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2109091798849838906?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2109091798849838906?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2007/11/avoid-flash-of-unstyled-content-using.html" title="Avoid Flash Of Unstyled Content using javascript" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_5fcKwF7jItc/Rz4cir4vEoI/AAAAAAAAAHg/vBl-Y7Lz4AQ/s72-c/flash-of-unpretty-html.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">8</thr:total></entry><entry gd:etag="W/&quot;Ak8FQH4-cSp7ImA9WB9QFkU.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-2572259736270454955</id><published>2007-10-29T17:03:00.000+01:00</published><updated>2007-10-29T21:13:31.059+01:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-10-29T21:13:31.059+01:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="eclipse" /><title>HOWTO: Uncompressed web projects in eclipse without plugins</title><content type="html">In case you are still redeploying war files on a daily basis, it is surprisingly easy to configure your eclipse project for deployment with tomcat/jetty without any need for plugins. I'm writing this down because every now and then I have to explain it from scratch, and in case you are of the impatient kind who prefers code to instructions a complete web project download has been included at the end of this post.&lt;br /&gt;&lt;br /&gt;First, create a vanilla project structure:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_5fcKwF7jItc/Rx28Ga5WGnI/AAAAAAAAAGg/gnNaL1Z40Bc/s1600-h/eclipse-step1.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/Rx28Ga5WGnI/AAAAAAAAAGg/gnNaL1Z40Bc/s400/eclipse-step1.gif" alt="" id="BLOGGER_PHOTO_ID_5124458769502575218" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In this example all java classes will be stored at src/java and the web root folder will be src/web. Now, configure the compiled classes folder at WEB-INF/classes:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_5fcKwF7jItc/Rx29GK5WGoI/AAAAAAAAAGo/Lf19jFKMTZs/s1600-h/eclipse-step2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_5fcKwF7jItc/Rx29GK5WGoI/AAAAAAAAAGo/Lf19jFKMTZs/s400/eclipse-step2.png" alt="" id="BLOGGER_PHOTO_ID_5124459864719235714" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;There is now a complete uncompressed web application in src/web: just add any third party jars to WEB-INF/lib, your java classes will be compiled to WEB-INF/classes and any properties/xml file that should be in the classpath will be copied there.&lt;br /&gt;&lt;br /&gt;Now to the deployment part:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Jetty&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Copy the following descriptor to your $JETTY_HOME/contexts folder (better using an ant task):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"  encoding="ISO-8859-1"?&gt;&lt;br /&gt;&amp;lt;!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd"&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Configure class="org.mortbay.jetty.webapp.WebAppContext"&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Set name="contextPath"&gt;/started&amp;lt;/Set&gt;&lt;br /&gt;&amp;lt;Set name="resourceBase"&gt;${basedir}/src/web&amp;lt;/Set&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/Configure&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Where "basedir" is the root folder of your eclipse project.&lt;br /&gt;&lt;br /&gt;Jetty will recognize your webapp at startup, but we want to launch it inside eclipse so let's give it another turn. Create a normal java application launcher in your project:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_5fcKwF7jItc/Rx2_vK5WGpI/AAAAAAAAAGw/5o-g39tNXIY/s1600-h/jetty-launch-1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_5fcKwF7jItc/Rx2_vK5WGpI/AAAAAAAAAGw/5o-g39tNXIY/s400/jetty-launch-1.png" alt="" id="BLOGGER_PHOTO_ID_5124462768117127826" border="0" /&gt;&lt;/a&gt;Check that the working directory is the jetty home folder. You will need a  JETTY_HOME environment variable for this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/Rx3Byq5WGqI/AAAAAAAAAG4/_iyOwTYm-Ec/s1600-h/jetty-launch-2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/Rx3Byq5WGqI/AAAAAAAAAG4/_iyOwTYm-Ec/s400/jetty-launch-2.png" alt="" id="BLOGGER_PHOTO_ID_5124465027269925538" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;It may seem counterintuitive but the only jar that must be included is the jetty startup jar, the rest will be discovered by jetty. It is important that you remove the project from the classpath before launching:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/Rx3B4q5WGrI/AAAAAAAAAHA/50_euFsndds/s1600-h/jetty-launch-3.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/Rx3B4q5WGrI/AAAAAAAAAHA/50_euFsndds/s400/jetty-launch-3.png" alt="" id="BLOGGER_PHOTO_ID_5124465130349140658" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you intend to debug your java classes (and you do), you have to add your source files to the launcher. Add the project as well as any sources zip file that you may need (here I have included a couple of my own)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_5fcKwF7jItc/Rx3B465WGsI/AAAAAAAAAHI/4Y8si_-NDoY/s1600-h/jetty-launch-4.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_5fcKwF7jItc/Rx3B465WGsI/AAAAAAAAAHI/4Y8si_-NDoY/s400/jetty-launch-4.png" alt="" id="BLOGGER_PHOTO_ID_5124465134644107970" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;That's all. Launch the jetty instance with the Run/Debug toolbar buttons just as you would with any normal java application. Any changes done while debugging will be detected on the fly by the server, given that they do not affect the class structure (method signatures or number of attributes/methods).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Tomcat&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Deploying to tomcat is almost the same process as jetty. The application descriptor should be copied to $CATALINA_HOME/conf/Catalina/localhost (you may have to create some folders). Be aware that the file name is the web prefix that the deployed application will have:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"  encoding="ISO-8859-1"?&gt;&lt;br /&gt;&amp;lt;Context&lt;br /&gt;docBase="${basedir}/src/web"&lt;br /&gt;reloadable="false"&lt;br /&gt; antiResourceLocking="false"&lt;br /&gt; antiJARLocking="false"&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/Context&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;(You may have noticed that this application is not reloadable in order to make changes to classes detected by the runtime while debugging).&lt;br /&gt;&lt;br /&gt;The rest is the same as the jetty example, using org.apache.catalina.startup.Bootstrap as &lt;span style="font-style: italic;"&gt;Main Class&lt;/span&gt; and ${env_var:CATALINA_HOME} as &lt;span style="font-style: italic;"&gt;Working Directory&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;The classpath is different, though. You probably won't need all the java security files unless you are using SSL but hey, having them surely won't hurt:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_5fcKwF7jItc/Rx3Ioa5WGtI/AAAAAAAAAHQ/mXk_F9H18-g/s1600-h/tomcat-launch-1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/Rx3Ioa5WGtI/AAAAAAAAAHQ/mXk_F9H18-g/s400/tomcat-launch-1.png" alt="" id="BLOGGER_PHOTO_ID_5124472547757660882" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The following environment variables are also needed:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_5fcKwF7jItc/Rx3JzK5WGuI/AAAAAAAAAHY/Nk_y4lCyvHU/s1600-h/tomcat-launch-2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_5fcKwF7jItc/Rx3JzK5WGuI/AAAAAAAAAHY/Nk_y4lCyvHU/s400/tomcat-launch-2.png" alt="" id="BLOGGER_PHOTO_ID_5124473831952882402" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;That's all. I recommend preparing several installations of jetty and tomcat with different ports if you switch projects very often to avoid having like a ton of applications in the same server and wasting hours of startup time.&lt;br /&gt;&lt;br /&gt;To download an example web project file with all these changes included, click  &lt;a href="http://loom.extrema-sistemas.com/download/started.tar.gz"&gt;here&lt;/a&gt;. It includes the main project structure, ant targets to deploy the web application, and eclipse launchers included by default in the run/debug drop-down lists. It's Christmas before Christmas. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-2572259736270454955?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/2572259736270454955/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2007/10/howto-uncompressed-web-projects-in.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2572259736270454955?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2572259736270454955?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2007/10/howto-uncompressed-web-projects-in.html" title="HOWTO: Uncompressed web projects in eclipse without plugins" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_5fcKwF7jItc/Rx28Ga5WGnI/AAAAAAAAAGg/gnNaL1Z40Bc/s72-c/eclipse-step1.gif" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></entry><entry gd:etag="W/&quot;AkMGSXs5cSp7ImA9WB9SFEQ.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-895508996319393361</id><published>2007-10-03T23:08:00.000+02:00</published><updated>2007-10-04T13:47:08.529+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-10-04T13:47:08.529+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="antipattern" /><title>About Bug Ranking and Why You Should Care</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_5fcKwF7jItc/RwNMU65WGlI/AAAAAAAAAGQ/sx76kSLg94k/s1600-h/all-hail-the-bug.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/RwNMU65WGlI/AAAAAAAAAGQ/sx76kSLg94k/s400/all-hail-the-bug.jpg" alt="" id="BLOGGER_PHOTO_ID_5117017523913890386" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;There is a whole ranking of bug types that drive a big share of my decisions on framework selection and code design given that I spend &lt;a href="http://icoloma.blogspot.com/2007/01/antipattern-of-month-6-code-generation.html"&gt;much more time working on bugs than developing new code&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;By ascending difficulty or chronological solving, the order is the same:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Compile time&lt;/span&gt;: the easiest, because compilers shout them out loud: IDEs highlight offending code, ant/maven will refuse to build your artifacts, etc. Eclipse will even replace the buggy code with a runtime error ("A typo in line 239, &lt;a href="http://xkcd.com/323/"&gt;you been drinkin' again&lt;/a&gt;?") producing a working class with a runtime exception that can be used by a testcase. This  allows you to execute things that don't even compile.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Deployment time&lt;/span&gt;: configuration mistakes noticed when the application gets deployed or the testsuite launched (spring xml errors, web.xml, ejbs, persistence...). These are quite easy to fix.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Reproducible execution time&lt;/span&gt; (emphasis on 'reproducible'): errors that happen each and every time. Once you get the right scenario you can reproduce it in a testcase and leave it as a regression test for generations to come. Memory leaks are the hardest bugs in this category.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-weight:bold;"&gt;Not reproducible execution time&lt;/span&gt;: happens only in production/preproduction environments with no recognizable scenario (jvm cores, concurrency, problems without a stack trace such as blank pages, etc). Your only tools are your wits, your favorite caffeinated beverage and truckloads of patience.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;It makes sense that applications should be designed to displace bugs to the easiest (think generics, for example) but too often I see designs that move features from java to XML or optimization tips that rely heavily on singletons, introducing concurrency issues.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/_5fcKwF7jItc/RwQAFK5WGmI/AAAAAAAAAGY/Tx_4YR1b64k/s1600/bugs-ranking.gif" alt="" style="text-align: center; width: 700px; height: 392px;" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Duck typing languages&lt;/span&gt; (ruby and javascript) have by design little support included in the interpreter/IDE. The language itself may help the first writer but hurts the rest that have to use that code - again, developing code from scratch is not the primary concern. I prefer strongly-typed languages that compile and review my code as I type, helping me to introduce fewer bugs.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Switching your spring XML file to Ruby/java/whatever&lt;/span&gt; does not achieve real improvements as you are only changing one syntactic sugar with another, which has null influence over bug locations or productivity.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Delegating features from JSP scriptlets to Java classes&lt;/span&gt; move deployment bugs to compile time, with extra testcases as a bonus. JSP compilation can also be executed at deployment time instead of the first call by tuning your server (like the &lt;code&gt;precompile&lt;/code&gt; parameter of weblogic), but I would only recommend this with testing and production environments to avoid penalizing the whole development team (with real applications, the startup time can become one of those "come back later" things).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;The scope of your web application objects&lt;/span&gt; is also related with bug types: &lt;code&gt;request&lt;/code&gt;, &lt;code&gt;flash&lt;/code&gt;, &lt;code&gt;conversation&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt; and &lt;code&gt;application&lt;/code&gt; scopes all have concurrency issues bigger from left to  right. Classes stored in anything but the &lt;code&gt;request&lt;/code&gt; scope should be aware of possible multiple tabs on the same browser, multiple browsers on the same station, and multiple users on the same aplication. Keep your objects with a scope as narrow as possible and you will be fine.&lt;br /&gt;&lt;br /&gt;There is a Bug Policy Nazi Role in my team, assumed by the person responsible of reviewing each framework looking for things that make life easier. To me, this is one of the biggest concerns. You should care, too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-895508996319393361?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/895508996319393361/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2007/10/about-bug-ranking-and-why-you-should.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/895508996319393361?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/895508996319393361?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2007/10/about-bug-ranking-and-why-you-should.html" title="About Bug Ranking and Why You Should Care" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_5fcKwF7jItc/RwNMU65WGlI/AAAAAAAAAGQ/sx76kSLg94k/s72-c/all-hail-the-bug.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;CEQFR3Y8fSp7ImA9WB5XEk8.&quot;"><id>tag:blogger.com,1999:blog-10814242.post-2122946003321162757</id><published>2007-07-12T08:27:00.000+02:00</published><updated>2007-07-12T08:38:36.875+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-07-12T08:38:36.875+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="fun" /><category scheme="http://www.blogger.com/atom/ns#" term="trends" /><title>Stupid conclusions using google trends</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/Ro9-IKk6ieI/AAAAAAAAAGI/4uh2bvdDreM/s1600-h/its-funny-laugh.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/Ro9-IKk6ieI/AAAAAAAAAGI/4uh2bvdDreM/s400/its-funny-laugh.jpg" alt="" id="BLOGGER_PHOTO_ID_5084421183067818466" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This whole thing started because of a project I had to integrate with Oracle BPM. Well, If you search for &lt;a href="http://www.google.es/search?q=oracle+bpel"&gt;Oracle BPM&lt;/a&gt;, there is only the standard marketing stuff and no technical article, which is weird.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;There are three kind of lies: Lies, Damned Lies, and Statistics - Benjamin Disraeli&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;That (and an idle Friday afternoon) was what got this started. With so much hype about SOA (coining such awesome terms like "an SOA" by people without anything better to do) I decided to separate Signal from Noise launching some BPM-related queries to Google Trends. &lt;br /&gt;&lt;br /&gt;Any product adoption curve has an &lt;a href="http://icoloma.blogspot.com/2006/02/about-crossing-chasm-and-reflexive.html"&gt;S-shaped form&lt;/a&gt;, which means that by now (the market starting and the products anything but mature yet) the BPM graph should be rising. Instead, you get &lt;a href="http://www.google.com/trends?q=bpel%2C+bpm%2C+workflow&amp;ctab=0&amp;amp;geo=all&amp;date=all&amp;amp;sort=0"&gt;a flat line&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/Ro9vWKk6iZI/AAAAAAAAAFg/vYs4gpXJouU/s1600-h/ouch-ouch-ouch.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/Ro9vWKk6iZI/AAAAAAAAAFg/vYs4gpXJouU/s400/ouch-ouch-ouch.gif" alt="" id="BLOGGER_PHOTO_ID_5084404930911570322" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ouch. The tools are buggy, there is close to none documented success stories or best practices, and there seems to be the same interest today than two years ago - in fact, a little LESS (considering actual people searching instead of news coverage). &lt;br /&gt;&lt;br /&gt;The next natural question is how well the major application servers are doing these days. Even with all the buzz about spring and lightweight and the no-more-EJBs-thank-you trend, I wasn't expecting &lt;a href="http://www.google.com/trends?q=glassfish%2C+jboss%2C+weblogic%2C+websphere&amp;ctab=0&amp;amp;geo=all&amp;date=all&amp;amp;sort=3"&gt;this&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_5fcKwF7jItc/Ro9xLKk6iaI/AAAAAAAAAFo/IrmAm4cke-c/s1600-h/all-your-servers-are-belong-to-the-museum.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_5fcKwF7jItc/Ro9xLKk6iaI/AAAAAAAAAFo/IrmAm4cke-c/s400/all-your-servers-are-belong-to-the-museum.gif" alt="" id="BLOGGER_PHOTO_ID_5084406940956264866" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Re-ouch. It seems that glassfish is a good product with bad timing, since any other provider is ramming downhill. As an interesting side note, glassfish may be the only server that is not used by India, India, India, Japan, and India (to see the detailed localized stats, use the combo box at the bottom of the page, named "rank by")&lt;br /&gt;&lt;br /&gt;Being such a &lt;a href="http://icoloma.blogspot.com/2006/10/myfaces-emperor-has-no-clothes.html"&gt;JSF lover&lt;/a&gt;, I really believe that no stupid conclusions list would be complete without a &lt;a href="http://www.google.com/trends?q=spring+mvc%2C+java+server+faces%2C+struts+2%2C+myfaces&amp;ctab=0&amp;amp;geo=all&amp;date=all&amp;amp;sort=0"&gt;web framework survey&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_5fcKwF7jItc/Ro9046k6ibI/AAAAAAAAAFw/MWMUfkLOW1E/s1600-h/a-well-deserved-death.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/Ro9046k6ibI/AAAAAAAAAFw/MWMUfkLOW1E/s400/a-well-deserved-death.gif" alt="" id="BLOGGER_PHOTO_ID_5084411025470163378" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Somewhere between 2005 and 2006, We The Crowd must have noticed that life with JSF sucked big time. The JSF top is still monopolized by India, which may imply that it's the outsourcing technology of choice or not mean anything at all.&lt;br /&gt;&lt;br /&gt;At this point I started searching for Really Funny Stuff, since that is what Fridays are for. If you feel curious about the relationship between &lt;a href="http://www.google.com/trends?q=ubuntu%2C+windows+vista&amp;ctab=0&amp;amp;geo=all&amp;date=all&amp;amp;sort=1"&gt; Windows Vista&lt;/a&gt; and a regular souffle:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_5fcKwF7jItc/Ro9376k6icI/AAAAAAAAAF4/Zg32eE0aqrw/s1600-h/rats-leaving-the-ship.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_5fcKwF7jItc/Ro9376k6icI/AAAAAAAAAF4/Zg32eE0aqrw/s400/rats-leaving-the-ship.gif" alt="" id="BLOGGER_PHOTO_ID_5084414375544654274" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Which has a curious resemblance with sex, something that sounds logical if you care about how much Microsoft is charging for Vista.&lt;br /&gt;&lt;br /&gt;That was all about my Friday. Do you have any other query to launch? Alcoholic beverages? God vs Sex? Go ahead and share!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10814242-2122946003321162757?l=icoloma.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://icoloma.blogspot.com/feeds/2122946003321162757/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://icoloma.blogspot.com/2007/07/stupid-conclusions-using-google-trends.html#comment-form" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2122946003321162757?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10814242/posts/default/2122946003321162757?v=2" /><link rel="alternate" type="text/html" href="http://icoloma.blogspot.com/2007/07/stupid-conclusions-using-google-trends.html" title="Stupid conclusions using google trends" /><author><name>Nacho Coloma</name><uri>http://www.blogger.com/profile/02874466783900738920</uri><email>icoloma@gmail.com</email><gd:extendedProperty name="OpenSocialUserId" value="01659762649636884015" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_5fcKwF7jItc/Ro9-IKk6ieI/AAAAAAAAAGI/4uh2bvdDreM/s72-c/its-funny-laugh.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total></entry></feed>
