<?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" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;CkMGSX84fSp7ImA9WhRUFkU.&quot;"><id>tag:blogger.com,1999:blog-4110180</id><updated>2012-01-27T08:07:08.135-08:00</updated><category term="documentation" /><category term="funny" /><category term="clojure" /><category term="derby" /><category term="spock" /><category term="pancake mix" /><category term="netkernel" /><category term="books" /><category term="bugs" /><category term="javadoc" /><category term="cappuccino" /><category term="firebug" /><category term="annotations" /><category term="maven" /><category term="formos" /><category term="selenium" /><category term="google application engine" /><category term="wtf" /><category term="lion" /><category term="linkedin" /><category term="objective-c" /><category term="firefox" /><category term="grails" /><category term="iphone" /><category term="travel" /><category term="testng" /><category term="css" /><category term="git" /><category term="code coverage" /><category term="easymock" /><category term="spring" /><category term="rss" /><category term="spam" /><category term="haskell" /><category term="video" /><category term="javassist" /><category term="servlet" /><category term="performance" /><category term="science fiction" /><category term="eclipse" /><category term="closures" /><category term="cloure" /><category term="training" /><category term="safari" /><category term="cascade" /><category term="scala" /><category term="jsf" /><category term="threads" /><category term="java" /><category term="seam" /><category term="security" /><category term="vmware" /><category term="webinar" /><category term="licenses" /><category term="aop" /><category term="hivedoc" /><category term="monads" /><category term="inform" /><category term="intellij" /><category term="web flow" /><category term="pdx" /><category term="slf4j" /><category term="apachecon" /><category term="spring web flow" /><category term="tynamo" /><category term="watchmen" /><category term="android" /><category term="javazone" /><category term="groovy" /><category term="mac" /><category term="dsl" /><category term="hackergarten" /><category term="ides" /><category term="configurations" /><category term="release" /><category term="testing" /><category term="ubuntu" /><category term="extjs" /><category term="third-party components" /><category term="conferences" /><category term="json" /><category term="prototype" /><category term="svn" /><category term="subversion" /><category term="google" /><category term="screencast" /><category term="ioc" /><category term="ruby" /><category term="articles" /><category term="couchdb" /><category term="trails" /><category term="support" /><category term="javascript" /><category term="vienna" /><category term="nfjs" /><category term="appfuse" /><category term="refcard" /><category term="tomcat" /><category term="codemash" /><category term="concurrence" /><category term="error reporting" /><category term="gorm" /><category term="customizing" /><category term="gradle" /><category term="guice" /><category term="feedback" /><category term="agile" /><category term="javaforge" /><category term="wicket" /><category term="leopard" /><category term="tapestry5" /><category term="jetty" /><category term="javaone" /><category term="devoxx" /><category term="consulting" /><category term="antlr" /><category term="skillsmatter" /><category term="live reloading" /><category term="off topic" /><category term="mindstorms" /><category term="jmx" /><category term="hivemind" /><category term="tapestry" /><category term="arduino" /><category term="hibernate" /><category term="idea" /><category term="ant" /><category term="jug" /><category term="oscon" /><category term="tss" /><category term="ajax" /><category term="tutorial" /><category term="mac os x" /><category term="scriptaculous" /><category term="meta-programming" /><category term="netnewswire" /><category term="hani" /><category term="ie" /><category term="archetype" /><category term="kindle" /><category term="jquery" /><category term="ruby on rails" /><category term="captcha" /><category term="blogger" /><category term="world tour" /><category term="scrum" /><category term="twitter" /><category term="portland" /><category term="functional programming" /><category term="jboss" /><category term="tapx" /><category term="https" /><category term="macports" /><category term="type coercion" /><category term="streambank" /><title>Tapestry Central</title><subtitle type="html">The inside scoop on what's happening with Tapestry ... from the creator of the Apache Tapestry framework.  Plus all the normal, random thoughts on coding and technology.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://tapestryjava.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>741</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/TapestryCentral" /><feedburner:info uri="tapestrycentral" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;CkMGSX89eyp7ImA9WhRUFkU.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-3214990755545065639</id><published>2012-01-27T08:07:00.000-08:00</published><updated>2012-01-27T08:07:08.163-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-27T08:07:08.163-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="linkedin" /><title>LinkedIn Etiquette</title><content type="html">&lt;p&gt;I've used &lt;a href="http://linkedin.com"&gt;LinkedIn&lt;/a&gt; for many years now, long before I joined Facebook ... I liked the concept of never losing contact information with business contacts and technologist. It just seemed like a good idea (though I do sometimes wonder &lt;a href="http://www.youtube.com/watch?v=NcfXij6t4LA"&gt;if LinkedIn has any particular purpose&lt;/a&gt;).
&lt;p&gt;I tend to only connect with people I've met in person, or at least talked to on the phone.
&lt;p&gt;One thing that drives me crazy about LinkedIn is that you aren't forced to customize the message. As far as I'm concerned, the default message is like no message at all, and its a sign that you are just trolling for contacts. Just like you should always write a cover letter for a resume.
&lt;p&gt;So the first rule of LinkedIn is: &lt;strong&gt;always customize the message&lt;/strong&gt;. You chances of a connection go up significantly (at least with me) ... and with the default message, your chances drop down to near zero.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-3214990755545065639?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/7DjI04zp4n0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/3214990755545065639/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=3214990755545065639" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3214990755545065639?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3214990755545065639?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/7DjI04zp4n0/linkedin-etiquette.html" title="LinkedIn Etiquette" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2012/01/linkedin-etiquette.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEMFQXo8eyp7ImA9WhRUFk0.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-3557656971524285189</id><published>2012-01-26T10:26:00.000-08:00</published><updated>2012-01-26T10:26:50.473-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-26T10:26:50.473-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><title>Tapestry Advantages</title><content type="html">&lt;p&gt;A summary of a &lt;a href="http://tapestry.1045711.n5.nabble.com/Stuts-VS-Tapestry-I-need-some-ammo-td5432282.html"&gt;discussion about the advantages of Tapestry over Struts&lt;/a&gt;:
&lt;ul&gt;
&lt;li&gt;Exceptional exception reporting&lt;/li&gt;
&lt;li&gt;Significantly less code&lt;/li&gt;
&lt;li&gt;Live class reloading&lt;/li&gt;
&lt;li&gt;Sensible defaults, especially for &lt;a href="http://en.wikipedia.org/wiki/SEO"&gt;SEO&lt;/a&gt;-friendly URLs&lt;/li&gt;
&lt;li&gt;Great community&lt;/li&gt;
&lt;li&gt;Flexibility and customizability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Interestingly, the quality of Tapestry's documentation was mentioned ... favorably! Between the revised &lt;a href="http://tapestry.apache.org"&gt;home page&lt;/a&gt;, and &lt;a href="http://jumpstart.doublenegative.com.au/home.html"&gt;Tapestry JumpStart&lt;/a&gt; (and &lt;a href="http://blog.tapestry5.de/index.php/2011/09/23/publishing-tapestry-5-in-action-book-on-my-own/"&gt;Igor's coming book&lt;/a&gt;), I think we're headed in the right direction in terms of documentation going from a liability to an asset.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-3557656971524285189?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/Rw7zKtUFgjI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/3557656971524285189/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=3557656971524285189" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3557656971524285189?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3557656971524285189?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/Rw7zKtUFgjI/tapestry-advantages.html" title="Tapestry Advantages" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2012/01/tapestry-advantages.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEQCQ3g_fip7ImA9WhRUFU0.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-7447306489952674714</id><published>2012-01-24T16:57:00.000-08:00</published><updated>2012-01-25T07:46:02.646-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-25T07:46:02.646-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><title>Tapestry 5.4: Focus on JavaScript</title><content type="html">&lt;div&gt;
  &lt;p&gt;
       Tapestry 5.3.1 is out in the wild ... and if Tapestry is to
       stay relevant, Tapestry 5.4 is going to need to be something
       quite (r)evolutionary.
  &lt;/p&gt;

&lt;blockquote&gt;
  There was some confusion on the Tapestry developer mailing list in
  advance of this blog post; I'd alluded that it was coming, and some
  objected to such pronouncements coming out fully formed, without
  discussion. In reality, this is just a distillation of ideas, a
  starting point, and not a complete, finalized solution. If it's more
  detailed than some discussions of Tapestry's evolution in the past,
  that just means that the mailing list discussion and eventual
  implementation will be that much better informed.
&lt;/blockquote&gt;
  &lt;p&gt;
    In posts and other conversations, I've alluded to my vision for
    Tapestry 5.4. As always, the point of Tapestry is to allow
    developers to &lt;em&gt;code less, deliver more&lt;/em&gt;, and that has been
    the focus of Tapestry on the server side: everything drives that
    point: terseness of code and templates, live class reloading, and
    excellent feedback are critical factors there. Much of what went
    into Tapestry 5.3 strengthened those points ... enhancements to
    Tapestry's meta-programming capabilities, improvements to the IoC
    container, and reducing Tapestry's memory footprint in a number of
    ways. I have one client reporting a 30% reduction in memory
    utilization, and another reporting a 30 - 40% improvement in
    execution speed.
  &lt;/p&gt;

  &lt;p&gt;
    Interestingly, I think that for Tapestry to truly stay relevant,
    it needs to shift much, much, more of the emphasis to the client
    side. For some time, Tapestry has been walking a fine line with
    regards to the critical question of &lt;em&gt;where does the application
    execute?&lt;/em&gt;  Pre-Ajax, that was an easy question: the
    application runs on the server, with at most minor JavaScript
    tricks and validations on the client.  As the use of Ajax has
    matured, and customer expectations for application behavior in the
    browser have expanded, it is no longer acceptable to say that
    Tapestry is page based, with limited Ajax
    enhancements. Increasingly, application flow and business logic
    need to execute in the browser, and the server-side's role is to
    orchestrate and facilitate the client-side application, as well as
    to act as a source and sink of data ultimately stored in a
    database.
  &lt;/p&gt;

  &lt;p&gt;
    As Tapestry's server-side has matured, the client side has not
    kept sufficient pace. Tapestry does include some excellent
    features, such as how it allows the server-side to drive
    client-side JavaScript in a modular and efficient way. However,
    that is increasingly insufficient ... and the tension caused by
    give-and-take between client-side and server-side logic has grown
    with each release.
  &lt;/p&gt;

  &lt;p&gt;
    Nowhere is this more evident than in how Tapestry addresses HTML
    forms. This has always been a tricky issue in Tapestry, because
    the dynamic rendering that can occur needs to be matched by
    dynamic form submission processing. In Tapestry, the approach is
    to serialize into the form instructions that will be used when the
    form is submitted (see the &lt;code&gt;store()&lt;/code&gt; method of the
    &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/FormSupport.html"&gt;FormSupport&lt;/a&gt;
    API). These instructions are used during the processing of the
    form submission request to re-configure the necessary components,
    and direct them to read their query parameters, perform
    validations, and push updated values back into server-side objects
    properties. If you've ever wondered what
    the &lt;code&gt;t:formdata&lt;/code&gt; hidden input field inside every
    Tapestry forms is about ... well, now you know: it's a serialized
    stream of Java objects, GZipped and MIME encoded.

  &lt;/p&gt;
  &lt;p&gt;
    However, relative to many other things in Tapestry, this is a bit
    clumsy and limited. You start to notice this when you see the
    tepid response to questions on the mailing list such as "&lt;em&gt;how
    to do cross-field validation?&lt;/em&gt;" Doing more complicated things,
    such as highly dynamic form layouts, or forms with even marginal
    relationships between fields, can be problematic (though still
    generally possible) ... but it requires a bit too much internal
    knowledge of Tapestry, and the in-browser results feel a bit
    kludgy, a bit clumsy. Tapestry starts to feel like it is getting
    in the way, and that's never acceptible.
  &lt;/p&gt;
  &lt;p&gt;
    Simply put, Tapestry's abstractions on forms and fields is
    both &lt;a href="http://en.wikipedia.org/wiki/Leaky_abstraction"&gt;leaky&lt;/a&gt;
    and insufficient. Tapestry is trying to do too much, and simply
    can't keep up with modern, reasonable demands in terms of
    responsiveness and useability inside the client. We've become used
    to pages rebuilding and reformatting themselves even while we're
    typing.  For Tapestry to understand how to process the form
    submission, it needs a model of what the form looks like on the
    client-side, and it simply doesn't have it. There isn't an
    effective way to do so without significantly restricting what is
    possible on the client side, or requiring much more data to be
    passed in requests, or stored server-side in the session.
  &lt;/p&gt;
  &lt;p&gt;
    The primary issue here is that overall form submission cycle,
    especially combined with Tapestry's need to serialize commands
    into the form (as the hidden &lt;code&gt;t:formdata&lt;/code&gt; field). Once
    you add Ajax to this mix, where new fields and rules are created
    dynamically (on the server side) and installed into the
    client-side DOM ... well, it gets harder and harder to manage.
    Add in a few more complications (such as a mix of transient and
    persistent Hibernate entities, or dynamic creation of sub-entities
    and relationships) into a form, it can be a brain burner getting
    Tapestry to do the right thing when the form is submitted: you
    need to understand exactly how Tapestry processes
    that &lt;code&gt;t:formdata&lt;/code&gt; information, and how to add your own
    callbacks into the callback stream to accomplish just exactly the
    right thing at just exactly the right time. Again, this is not the
    Tapestry way, where things are expected to &lt;em&gt;just work&lt;/em&gt;.
  &lt;/p&gt;
  &lt;p&gt;
    Further, there is some doubt about even the desirability of the
    overall model. In many cases, it makes sense to batch together a
    series of changes to individual properties ... but in many more,
    it is just as desirable for individual changes to filter back to
    the server (and the database) as the user
    navigates. Form-submit-and-re-render is
    a &lt;a href="http://en.wikipedia.org/wiki/IBM_3270"&gt;green screen&lt;/a&gt;
    style of user interaction. Direct interaction is the expectation
    now, and that's something Tapestry should embrace.
  &lt;/p&gt;
  &lt;p&gt;
    What's the solution, then? Well, it's still very much a moving
    target. The goal is to make creating client-side JavaScript
    libraries easier, to make it easier to integrate with libraries such
    as &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; (and its vast library
    of extensions), make things simpler and more efficient on the
    client side, and not sacrifice the features that make Tapestry fun
    and productive in the first place.&lt;/p&gt;

  &lt;h3&gt;Overall Vision&lt;/h3&gt;

  &lt;p&gt;The overall vision breaks down into a number of steps:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;Reduce or remove outside dependencies&lt;/li&gt;
    &lt;li&gt;Modularize JavaScript&lt;/li&gt;
    &lt;li&gt;Change page initializations to use modules&lt;/li&gt;
    &lt;li&gt;Embrace client-side controller logic&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;
    Of course, all of these steps depend on the others, so there isn't
    a good order to discuss them.
  &lt;/p&gt;

  &lt;h3&gt;Reducing and removing outside dependencies&lt;/h3&gt;

  &lt;p&gt;
    Tapestry's client-side strength has always been lots of "out of
    the box" functionality: client-side validation, Zones and other
    Ajax-oriented behaviors, and a well-integrated system for
    performing page-level initializations.
  &lt;/p&gt;
  &lt;p&gt;
    However, this strength is also a weakness, since that out of the
    box behavior is too tightly tied to the &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt; and
    &lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt; libraries ... reasonable choices in 2006, but
    out-of-step with the industry today. Not just in terms of the
    momentum behind jQuery, but also in terms of very different
    approaches, such
    as &lt;a href="http://www.sencha.com/"&gt;Sencha/ExtJS&lt;/a&gt; and
    others.&lt;/p&gt;

  &lt;p&gt;
    It was a conscious decision in 2006 to not attempt to create an
    abstraction layer before I understood all the abstractions. I've
    had the intermediate time to embrace those abstractions. Now the
    big problem is momentum and backwards compatibility.&lt;/p&gt;

  &lt;p&gt;
    Be removing unnecessary behaviors, such as animations, we can
    reduce Tapestry's client-side needs. Tapestry needs to be able
    to &lt;strong&gt;attach event handlers&lt;/strong&gt; to elements. It needs to
    be able to easily &lt;strong&gt;locate elements&lt;/strong&gt; via unique ids,
    or via &lt;strong&gt;CSS selectors&lt;/strong&gt;. It needs to be able
    to &lt;strong&gt;run Ajax requests&lt;/strong&gt; and handle the responses,
    including &lt;strong&gt;dynamic updates to elements&lt;/strong&gt;.&lt;/p&gt;
  &lt;p&gt;

    &lt;p&gt;All of these things are reasonable to abstract, and by making
    it even easier to execute JavaScript as part of a page render or
    page update
    (&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ajax/AjaxResponseRenderer.html"&gt;something
    already present in Tapestry 5.3&lt;/a&gt;), currently built-in features
    (such as animations) can be delegated to the application, which is
    likely a better choice in any case.&lt;/p&gt;

  &lt;h3&gt;Modularizing JavaScript&lt;/h3&gt;

  &lt;p&gt;
    Tapestry has always been careful about avoiding client-side
    namespace polution.  Through release 5.2, most of Tapestry's
    JavaScript was encapulated in the &lt;code&gt;Tapestry&lt;/code&gt; object. In
    Tapestry 5.3, a second object, &lt;code&gt;T5&lt;/code&gt; was introduced with
    the intention that it gradually replace the
    original &lt;code&gt;Tapestry&lt;/code&gt; object (but this post represents a
    change in direction).&lt;/p&gt;
  &lt;p&gt;
    However, that's not enough. Too often, users have created in-line
    JavaScript, or JavaScript libraries that defined "bare" variables
    and functions (that are ultimately added to the
    browser's &lt;code&gt;window&lt;/code&gt; object). This causes problems,
    including collisions between components (that provide competing
    definitions of objects and functions), or behavior that varies
    depending on whether the JavaScript was added to the page as part
    of a full-page render, or via an Ajax partial page render.&lt;/p&gt;
  &lt;p&gt;
    The right approach is to encourage and embrace some form of
    &lt;a href="http://yuiblog.com/blog/2007/06/12/module-pattern/"&gt;JavaScript
      module architecture&lt;/a&gt;, where there are no explicit global
    variables or functions, and that all JavaScript is evaluated
    inside a function, allowing for private variables and
    functions.&lt;/p&gt;
  &lt;p&gt;
    Currently, I'm thinking in terms of &lt;a href="http://requirejs.org/"&gt;RequireJS&lt;/a&gt; as
    the way to organize the JavaScript. Tapestry would faciliate
    organizing its own code into modules, as well as
    application-specific (or even page-specific) JavaScript
    modules. This would mean that de-referencing the &lt;code&gt;T5&lt;/code&gt;
    object would no longer occur (outside of some kind of temporary
    compatibility mode).&lt;/p&gt;
  &lt;p&gt;
    For example, clicking a button inside some container element
    might, under 5.3, publish an event using Tapestry's client-side
    publish/subscribe system. In the following example, the click
    events bubble up from the buttons (with the &lt;code&gt;button&lt;/code&gt;
    CSS class name) to a container element, and are then published
    under the topic name &lt;code&gt;button-clicked&lt;/code&gt;.
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1603920.js?file=53pubsubexample.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Consider this an abbreviated example, as it doesn't explain where
    the &lt;code&gt;element&lt;/code&gt; variable is defined or initialized; the
    important part is the interaction with Tapestry's client-side
    library: the reference to the &lt;code&gt;T5.pubsub.publish&lt;/code&gt; function.
  &lt;/p&gt;
  &lt;p&gt;
    Under 5.4, using the RequireJS &lt;code&gt;require&lt;/code&gt; function, this
    might be coded instead as:
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1603930.js?file=54pubsubexample.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Here, the &lt;code&gt;t5/pubsub&lt;/code&gt; module will be loaded by RequireJS and
    passed as a parameter into the function, which is automatically
    executed. So, this supports JavaScript modularization, and
    leverages RequireJS's ability to load modules on-the-fly, 
    as needed.
  &lt;/p&gt;
  &lt;p&gt;
    Notice the difference between the two examples; in the first
    example, coding as a module was &lt;em&gt;optional&lt;/em&gt; (but
    recommended), since the necessary &lt;code&gt;publish()&lt;/code&gt; function
    was accessible either way. In the 5.4 example, coding using
    JavaScript modules is virtually &lt;em&gt;required&lt;/em&gt;: the anonymous
    function passed to &lt;code&gt;require()&lt;/code&gt; is effectively a module,
    but its only through the use of &lt;code&gt;require()&lt;/code&gt; (or
    RequireJS's &lt;code&gt;define()&lt;/code&gt;) that the &lt;code&gt;publish()&lt;/code&gt;
    function can be accessed. &lt;/p&gt;
  &lt;p&gt;
    This is both the carrot and the stick; the carrot is how easy it
    is to declare dependencies and have them passed in to your
    function-as-a-module. The stick is that (eventually)
    the &lt;em&gt;only&lt;/em&gt; way to access those dependencies is by providing
    a module and declaring dependencies.
  &lt;/p&gt;
  &lt;h3&gt;Change page initializations to use modules&lt;/h3&gt;

  &lt;p&gt;
    Tapestry has a reasonably sophisticated system for allowing
    components to describe their JavaScript requirements as they
    render, in the form of
    the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html"&gt;JavaScriptSupport&lt;/a&gt;
    environmental (an environmental is a kind of
    per-thread/per-request service object).  Methods on
    JavaScriptSupport allow a component to request that a JavaScript
    library be imported in the page (though this is most commonly
    accomplished using
    the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Import.html"&gt;Import&lt;/a&gt;
    annotation), and to request the &lt;em&gt;initialization functions&lt;/em&gt;
    get executed.&lt;/p&gt;
  &lt;p&gt;
    Part of Tapestry's Ajax support is that in an Ajax request, the
    JavaScriptSupport methods can still be invoked, but a completely
    different implementation is responsible for integrating those
    requests into the overall reply (which in an Ajax request is a JSON object, rather
    than a simple stream of HTML).
  &lt;/p&gt;
  &lt;p&gt;
    Here's an example component from the TapX library:&lt;/p&gt;

&lt;script src="https://gist.github.com/1673763.js?file=Expando.java"&gt;&lt;/script&gt;

  &lt;p&gt;
    The @Import annotation directs that a stack (a set of related
    JavaScript libraries, defined elsewhere) be imported into the
    page; alternately, the component could import any number of
    specific JavaScript files, located either in the web application
    context folder, or on the classpath.&lt;/p&gt;
  &lt;p&gt;
    Inside the &lt;code&gt;afterRender()&lt;/code&gt; method, the code constructs
    a &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/json/JSONObject.html"&gt;JSONObject&lt;/a&gt;
    of data needed on the client side to perform the operation. The
    call to &lt;code&gt;addInitializerCall&lt;/code&gt; references a function by
    name: this function must be added to
    the &lt;code&gt;T5.Initializers&lt;/code&gt; namespace object.  Notice the
    naming: &lt;code&gt;tapxExpando&lt;/code&gt;: a prefix to identify the
    library, and to prevent collisions with any other application or
    library that also added its own functions to
    the &lt;code&gt;T5.initializers&lt;/code&gt; object.
  &lt;/p&gt;
  &lt;p&gt;
    The JavaScript library includes the function that will be invoked:
  &lt;/p&gt;
  &lt;script src="https://gist.github.com/1673810.js?file=tapx-core.js"&gt;&lt;/script&gt;
  &lt;p&gt;
    Under 5.4, this would largely be the same except:
  &lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;There will be a specific Java package for each library (or the
      application) to store library modules.&lt;/li&gt;
    &lt;li&gt;The JavaScriptSupport environmental will have new methods to
      reference a function, inside a module, to invoke.&lt;/li&gt;
      &lt;li&gt;Stacks will consist not just of individual libraries, but
 also modules, following the naming and packaging
 convention.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;Embrace client-side controller logic&lt;/h3&gt;

&lt;p&gt;
  The changes discussed so far only smooth out a few rough edges;
  they still position Tapestry code, running on the server, as driving
  the entire show. 
 &lt;/p&gt;

&lt;p&gt;
  As alluded to earlier; for any sophisticated user interface, the
  challenge is to coordinate the client-side user interface (in terms
  of form fields, DOM elements, and query parameters) with the
  server-side components; this is encoded into the
  hidden &lt;code&gt;t:formdata&lt;/code&gt; field. However, it is my opinion that
  for any dynamic form, Tapestry is or near the end of the road for
  this approach.&lt;/p&gt;
&lt;p&gt;Instead, it's time to embrace client-logic, written in JavaScript,
  in the browser. Specifically, break away from HTML forms, and
  embrace a more dynamic structure, one where "submitting" a form
  always works through an Ajax update ... and what is sent is not a
  simple set of query parameters and values, but a JSON representation
  of what was updated, changed, or created.
&lt;/p&gt;
&lt;p&gt;
  My specific vision is to
  integrate &lt;a href="http://documentcloud.github.com/backbone/"&gt;Backbone.js&lt;/a&gt;
  (or something quite similar), to move this logic solidly to the
  client side. This is a fundamental change: one where the client-side
  is free to change and reconfigure the UI in any way it likes, and is
  ultimately responsible for packaging up the completed data and
  sending it to the server.
&lt;/p&gt;
&lt;p&gt;
  When you are used to
  the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/BeanEditForm.html"&gt;BeanEditForm&lt;/a&gt;
  component, this might feel like a step backwards, as you end up
  responsible for writing a bit more code (in JavaScript) to
  implement the user interface, input validations, and relationships
  between fields. However, as fun as BeanEditForm is, the declarative
  approach to validation on the client and the server has proven to be
  limited and limiting, especially in the face of cross-field
  relationships. We could attempt to extend the declarative nature,
  introducing rules or even scripting languages to establish the
  relationships ... or we could move in a situation that puts the
  developer back in the driver's seat.&lt;/p&gt;
&lt;p&gt;
  Further, there are some that will be concerned that this is a
  violation of
  the &lt;a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY
  pricipal&lt;/a&gt;; however I subscribe to different philosophy that
  client-side and server-side validation are fundamentally different
  in any case; this is discussed in an excellent
  &lt;a href="http://blog.ianbicking.org/2011/03/30/js-on-server-and-client-is-not-a-big-deal/"&gt;blog
  post by Ian Bickling&lt;/a&gt;.

&lt;p&gt;
  Certainly there will be components and services to assist with this
  process, in term of extracting data into JSON format, and converting
  JSON data into a set of updates to the server-side objects. There's
  also a number of security concerns that necessitate careful
  validation of what comes up from the client in the Ajax request.
  Further, there will be new bundled libraries to make it easier to
  build these dynamic user interfaces.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;
  In this vision of Tapestry's future, the server-side framework
  starts to shift from the focus of all behavior to
  the &lt;em&gt;facilitator&lt;/em&gt;: it paints the broad stokes on the server,
  but the key interactions end up working exclusively on the
  client. &lt;/p&gt;
&lt;p&gt;
  I'm sure this view will be controversial: after all, on the surface,
  what the community really wants is just "jQuery instead of
  Prototype". However, all of the factors described in the above
  sections are, I feel, critical to keeping Tapestry relevant by
  embracing the client-side in the way that the client-side
  demands.&lt;/p&gt;

&lt;p&gt;
  I think this change in focus is a big deal; I think it is also
  necessary for Tapestry to stay relevant in the medium to long
  term. I've heard from many individual developers (not necessarily
  Tapestry users) that what they really want is "just jQuery and a
  restful API"; I think Tapestry can be that restful API, but by
  leveraging many of Tapestry's other strengths, it can be a lot more.
  Building something right on the metal feels empowering ... until you
  hit all the infrastructure that Tapestry provides, including
  best-of-class exception reporting, on-the-fly JavaScript aggregation
  and minimization, and (of course) live class reloading during
  development.
java&lt;/p&gt;  
  &lt;p&gt;
    I'm eager to bring Tapestry to the forfront of web application
  development ... and to deliver it fast!  Monitor the Tapestry
  developer mailing list to see how this all plays out.&lt;/p&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-7447306489952674714?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/mlpJ4i8ShSA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/7447306489952674714/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=7447306489952674714" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/7447306489952674714?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/7447306489952674714?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/mlpJ4i8ShSA/tapestry-54-focus-on-javascript.html" title="Tapestry 5.4: Focus on JavaScript" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/11/tapestry-54-focus-on-javascript.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A04HSXo9fyp7ImA9WhRUFE8.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-4121218058413204699</id><published>2012-01-24T10:32:00.000-08:00</published><updated>2012-01-24T10:32:18.467-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-24T10:32:18.467-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gradle" /><title>Review: Gradle Class with Luke Daley</title><content type="html">&lt;p&gt;Last week, Luke Daly arrived in Portland to teach a three day &lt;a href="http://gradle.org/"&gt;Gradle&lt;/a&gt; class; the folks at Gradleware were nice enough let me audit the class (so it only cost me a couple of thousand dollars of lost billing revenue to attend).  My goals for the class was to gain a deeper understanding of how Gradle works, so that I could write more efficient builds, diagnose problems, and write my own plugins. The class scored very high on all of those counts!&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Much of the first day was spent on basics, including a very useful introduction to the Groovy programming language (which is the basis of the &lt;a href="http://gradle.org/docs/current/dsl/index.html"&gt;Gradle DSL&lt;/a&gt;). Even though I have used Groovy pretty extensively for testing purposes over the last couple of years, there were features I've glossed over in the past that turned out to be very useful.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The class is taught largely bottom up: from the basics of declaring tasks, then actions on tasks, and gradually working up towards defining tasks inputs and outputs. Compiling Java came pretty late, which seems curious since that's the primary job of Gradle, but this makes sense from a bottom-up approach as &lt;a href="http://gradle.org/docs/current/dsl/org.gradle.api.Project.html#org.gradle.api.plugins.JavaPluginConvention:sourceSets(groovy.lang.Closure)"&gt;SourceSets&lt;/a&gt; can then be described correctly. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The class alternates between lecture and discussion, and short focused labs. At the end of the third day, we worked on tuning up our own builds, passing the video projector cable around ... we had a good time making the Tapestry build more efficient and readable, as well as adding new features to it.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;I'm feeling very good about my Gradle skills after attending the course; I've already been able to make further improvements to the Tapestry build subsequently, and I have plans for more involving things going forward. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The return on investment for this class is a bit tricky; Gradle simply does so much straight out of the box, and even without a strong understanding of its structure, you can &lt;em&gt;kind of&lt;/em&gt; get it to do what you want just by guesswork and Googling. If you've been using Gradle for a while, I give this a cautious recommendation: getting back the three days of invested time may take a while to pay off. On the other hand, if you are currently dependent on Ant or Maven ... &lt;a href="http://gradle.org/training"&gt;sign up for the next class&lt;/a&gt; and get yourself switched over, today!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-4121218058413204699?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/stulP-4bay8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/4121218058413204699/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=4121218058413204699" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/4121218058413204699?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/4121218058413204699?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/stulP-4bay8/review-gradle-class-with-luke-daley.html" title="Review: Gradle Class with Luke Daley" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2012/01/review-gradle-class-with-luke-daley.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkANR3w-fCp7ImA9WhRVFEo.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-8652247449868717675</id><published>2012-01-13T08:06:00.000-08:00</published><updated>2012-01-13T08:06:36.254-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-13T08:06:36.254-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="pdx" /><category scheme="http://www.blogger.com/atom/ns#" term="hackergarten" /><title>Hackergarten in PDX - Friday January 20th</title><content type="html">&lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://hackergarten.net/hackergarten_b_and_w_small.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="150" width="392" src="http://hackergarten.net/hackergarten_b_and_w_small.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;p&gt;Merlyn Albery-Speyer is organizing a &lt;a href="http://hackergarten.net/"&gt;Hackergarten&lt;/a&gt; while &lt;a href="http://ldaley.com/"&gt;Luke Daley&lt;/a&gt; (creator of &lt;a href="http://www.gebish.org/"&gt;Geb&lt;/a&gt;, and &lt;a href="http://gradle.org/"&gt;Gradle&lt;/a&gt; committer) is in town to run an &lt;a href="http://gradleware.com/training"&gt;in-depth Gradle training&lt;/a&gt;.  I'll be there, working on Tapestry, or Gradle, or a video game, or something.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Please see &lt;a href="http://www.curiousattemptbunny.com/2012/01/hackergarten-in-pdx-friday-january-20th.html"&gt;Meryln's blog&lt;/a&gt; to RSVP.  I look forward to meeting and coding with more PDX peeps!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-8652247449868717675?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/_TBpJwaKHvM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/8652247449868717675/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=8652247449868717675" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/8652247449868717675?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/8652247449868717675?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/_TBpJwaKHvM/hackergarten-in-pdx-friday-january-20th.html" title="Hackergarten in PDX - Friday January 20th" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2012/01/hackergarten-in-pdx-friday-january-20th.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0MGRXo5eip7ImA9WhRWEkw.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-8082440333108958553</id><published>2011-12-29T17:40:00.000-08:00</published><updated>2011-12-29T18:17:04.422-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-29T18:17:04.422-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="ajax" /><title>Adding "Ajax Throbbers" to Zone updates</title><content type="html">&lt;p&gt;A common desire in Tapestry is for Zone updates to automatically include a throbber (or "spinner") displayed while the Ajax update is in process.  This is, unfortunately, a space where the built-in Tapestry 5.3 Zone functionality is a bit lacking.  Fortunately, it's not too hard to hard it in after the fact.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;This solution involves a JavaScript library, two CSS stylesheet files (one is IE specific), plus the "throbber" image. Typically, you'll bind all of these things together in your application's Layout component.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;First, the JavaScript. We need to intercept links and forms that update a Zone. When such a request starts, we add a &amp;lt;div&amp;gt; to the top of the Zone's client-side element. When the update from the server arrives, the entire content of the Zone's element will be replaced (so we don't have to worry about clearing the &amp;lt;div&amp;gt; explicitly).&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1537029.js?file=zone-overlay.js"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;When a form is submitted with Ajax, to update a Zone, Tapestry fires a client-side event on the Form; the &lt;code&gt; Tapestry.FORM_PROCESS_SUBMIT_EVENT&lt;/code&gt; constant provides the event name. The primary handler for this event is the code that actually performs the XmlHTTPRequest and sets up a handlers for the response; the above code adds a second handler that adds the Ajax overlay.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Likewise, when a link is used to update a Zone, there's a second client-side event; again, the primary handler for the event does the actual Ajax work, but the same logic allows the Zone to be decorated with the overlay.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The overlay consists of a &amp;lt;div&amp;gt; that will visually mark the entire zone's content and consume any mouse clicks during the Ajax update.  The CSS associated with the zone-ajax-overlay CSS class sets up a translucent background color and the spinning Ajax throbber.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Next up is the CSS:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1537062.js?file=zone-throbber.css"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;This little bit of CSS is doing quite a bit. Firstly, if the Ajax request is very quick, then there will be an annoying flicker; to combat this, we've set up a &lt;a href="https://developer.mozilla.org/en/CSS/CSS_animations"&gt;simple CSS animation&lt;/a&gt; to delay the animation momentarily, long enough that fast requests will just see the new content pop into place. There's probably a bit of room here to tweak the exact timing.  &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Alas, in the current world, we need to do a bit of work to support both Firefox (the -moz prefix) and WebKit (Safari, Chrome, the -webkit prefix).  This is really calling out for a &lt;a href="http://sass-lang.com/"&gt;SASSy solution&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;You'll also see an animated image for the throbber. I used &lt;a href="http://www.ajaxload.info/"&gt;ajaxload.info&lt;/a&gt; to create one.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;But what about Internet Explorer?  It doesn't understand the animation logic, and it does CSS opacity differently from the others. Fortunately, we can segregate those differences in a separate CSS file.&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1537101.js?file=zone-throbber-ie.css"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Lastly, we put all this together inside the application's Layout component:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1537114.js?file=Layout.java"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The &lt;code&gt;@Import&lt;/code&gt; annotation does the easy imports of the main CSS and JavaScript.&lt;br /&gt;
Tapestry 5.3 supports IE conditional stylesheets ... but this requires just a bit of code as the &lt;code&gt;@Import&lt;/code&gt; annotation doesn't support adding a condition, as this is a fairly rare requirement.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Instead, the IE-specific CSS is injected into the page as an Asset object; this can be combined with StylesheetOptions to form a StylesheetLink, which can be imported into the page.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;With this in place, every page will include both CSS stylesheets (one as an IE-only conditional comment) and the necessary client-side logic ... and every Zone update will get this uniform treatment.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;There's some limitations here; in Tapestry it's possible for the server-side to push updates into multiple Zones. The client-side doesn't even know that's happening until it gets the reply, so there's no universal way to add overlays to multiple zones when the request is initiated.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Secondly, in rare cases, a Zone update may &lt;em&gt;only&lt;/em&gt; update other Zones, and leave the initiating Zone's content unchanged. In that case, you may find that the Zone's throbber is still in place after the response is handled!  I'll leave it as an exercise to the reader on how to deal with that.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-8082440333108958553?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/cYkGpxaTh0g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/8082440333108958553/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=8082440333108958553" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/8082440333108958553?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/8082440333108958553?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/cYkGpxaTh0g/adding-ajax-throbbers-to-zone-updates.html" title="Adding &quot;Ajax Throbbers&quot; to Zone updates" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/12/adding-ajax-throbbers-to-zone-updates.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMCSH0_cSp7ImA9WhRXFkU.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-8082482643441252077</id><published>2011-12-22T11:43:00.000-08:00</published><updated>2011-12-23T14:47:49.349-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-23T14:47:49.349-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="feedback" /><category scheme="http://www.blogger.com/atom/ns#" term="spock" /><title>Dissecting a Tapestry Operation Trace</title><content type="html">&lt;p&gt;I'm helping out a client who is having a problem using &lt;a href="http://code.google.com/p/spock/"&gt;Spock&lt;/a&gt; and Tapestry 5.3 together. The &lt;a href="http://code.google.com/p/spock/wiki/TapestryExtension"&gt;Spock/Tapestry integration&lt;/a&gt; was created for Tapestry 5.2, and some subtle change in the Tapestry 5.3 IoC container has boned the integration, so running even a simple test results in an exception, with a &lt;em&gt;very big&lt;/em&gt; stacktrace:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1511300.js?file=gistfile1.txt"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The part at the top (the numbered entries) are the &lt;em&gt;operation trace&lt;/em&gt;: Tapestry tracks what its doing at all times (using a stack stored inside a ThreadLocale) just so it can report this information in case there's an error. That's part of Tapestry's commitment to useful &lt;a href="http://tapestryjava.blogspot.com/2011/10/tapestry-feedback.html"&gt;feedback&lt;/a&gt;. The operation tracing was enhanced a bit in Tapestry 5.3 to be a bit more verbose and all-reaching. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The operation trace is providing a wealth of information about how Tapestry got to the point where an exception was thrown. This is much more useful than just the huge stack trace (about 400 frames!) since Tapestry, by design, tends to call through the very same methods repeatedly; stack traces are less useful when what counts are the parameters to the methods, rather than the methods themselves.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;It takes a while to figure out, but the key operations are:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1511330.js?file=gistfile1.txt"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;That operation corresponds to invoking this method:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1511334.js?file=gistfile1.java"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Translated to English, this code says:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;When starting up the Registry (the odd name for the Tapestry IoC container), execute this block of code, that checks to see if early startup of Hibernate is desired and, if so, forces the initialization of Hibernate by invoking the &lt;code&gt;getConfiguration()&lt;/code&gt; method (otherwise, the initialization would happen lazily the first time a request needed to use the Hibernate Session).&lt;br /&gt;
&lt;/blockquote&gt;&lt;br /&gt;
&lt;p&gt;The &lt;code&gt;@&lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/annotations/Symbol.html"&gt;Symbol&lt;/a&gt;&lt;/code&gt; annotation means that the parameter's value is derived from a Tapestry &lt;a href="http://tapestry.apache.org/tapestry-ioc-configuration.html"&gt;configuration symbol&lt;/a&gt;, which is a flexible, late-binding way to configure services as they are instantiated.  In other words, because of the use of a symbol, rather than a constant, the actual value passed in can't be determined until runtime ... which is a good thing; it means a developer can configure the symbol's value locally, but a different default value is in effect for the production application. Sometimes you want early startup, sometimes you don't.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;In order to resolve the value of a symbol, Tapestry must instantiate the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/SymbolSource.html"&gt;SymbolSource&lt;/a&gt; service; it has its own configuration that depends on other services, including ApplicationDefaults, FactoryDefaults, as well as a few other simple objects that implement the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/SymbolProvider.html"&gt;SymbolProvider&lt;/a&gt; interface, but are not services.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;There's also a hidden dependency here: Starting in Tapestry 5.3, Tapestry will attempt to type-coerce contributed values (in this case, symbol values contributed to ApplicationDefaults or FactoryDefaults) from their actual type, to the expected type. That shows up in operation 14:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1511505.js?file=gistfile1.java"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Those integers and booleans need to be converted to Strings; Tapestry 5.3 invokes the full machinery of the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/TypeCoercer.html"&gt;TypeCoercer&lt;/a&gt; service to do this coersion, seen as operation 15. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;At operations 21 - 23, Tapestry sees the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/ThreadLocale.html"&gt;ThreadLocale&lt;/a&gt; service (which stores the active Locale used during processing of the request; something that can vary on a request-by-request basis).  The ThreadLocale service uses a special service lifecycle that enforces that the instance is stored as a per-thread singleton, not a per-Registry singleton, and will be discarded at the end of each request. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/ServiceLifecycleSource.html"&gt;ServiceLifecycleSource&lt;/a&gt; service is the source for these &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/ServiceLifecycle.html"&gt;ServiceLifecycle&lt;/a&gt; objects. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;At operation 28 - 31, the Spock/Tapestry integration code is getting involved. It adds a special service lifecycle just for values that are part of a Spock specification ... and we're finally reaching the problem point!&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1511409.js?file=gistfile1.java"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;The Spock/Tapestry integration is using the &lt;code&gt;addInstance()&lt;/code&gt; method, which instantiates a class &lt;em&gt;with dependencies&lt;/em&gt;; this is operation 30.  &lt;strong&gt;This is the problem point&lt;/strong&gt;, but it's not obvious why its causing an eventual exception.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Because of the use of &lt;code&gt;addInstance()&lt;/code&gt;, Tapestry must locate and inject the dependencies of the PerIterationServiceLifecycle class, including the IPerIterationManager service (operation 31).&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;In Tapestry, there is a mechanism to replace services with overrides; this is the &lt;a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ioc/services/ServiceOverride.html"&gt;ServiceOverride&lt;/a&gt; service and its configuration. It's super handy for extending Tapestry in completely unexpected ways.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;That brings us to some code, new in Tapestry 5.3, at operation 38:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1511443.js?file=gistfile1.java"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;And that brings us to the actual cause.  Notice the @Symbol annotation ... remember way back to operation 7, that required the TypeCoercer (operation 15) ... well, we're not done with that yet, but this production override code has a @Symbol annotation that requires the TypeCoercer ... which is still in the middle of being instantiated.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Yes, this takes a lot of detective work ... this is something of an Achilles' Heel of Tapestry's IoC container; since much of the functionality of the container is defined in terms of &lt;em&gt;other&lt;/em&gt; functionality of the container, you can get into these hidden dependency cycles when tweaking some of the more fundamental aspects of Tapestry, such as TypeCoercer contributions, or adding new service lifecycles.  This is unfortunate, since so much else in Tapestry's web framework and IoC container &lt;em&gt;Just Works&lt;sup&gt;TM&lt;/sup&gt;&lt;/em&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;In terms of fixing this ... turns out the Spock/Tapestry integration has some other dependencies on 5.2, making use of internal classes and constructors that no longer exist in 5.3.  I'll be forking their code shortly to produce a 5.3 compatible version.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;However, my take-away here is: &lt;strong&gt;the system works&lt;/strong&gt;, the emphasis on feedback, and the generation of useful operation traces, makes this detective work even possible. The alternative would have taken far, far longer ... using the debugger to try and work backwards to what Tapestry was trying to do. It's &lt;em&gt;so&lt;/em&gt; much better to have Tapestry simply &lt;em&gt;tell&lt;/em&gt; you what you need to know!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-8082482643441252077?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/gnF4oCO8f3U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/8082482643441252077/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=8082482643441252077" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/8082482643441252077?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/8082482643441252077?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/gnF4oCO8f3U/dissecting-tapestry-operation-trace.html" title="Dissecting a Tapestry Operation Trace" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/12/dissecting-tapestry-operation-trace.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUAR305fip7ImA9WhRREEU.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-1938305035859672530</id><published>2011-11-23T14:22:00.001-08:00</published><updated>2011-11-23T14:27:26.326-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-23T14:27:26.326-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="mac" /><category scheme="http://www.blogger.com/atom/ns#" term="android" /><category scheme="http://www.blogger.com/atom/ns#" term="lion" /><title>Mac Tips: Preventing Aperture from Launching</title><content type="html">&lt;p&gt;Since I tend to connect and disconnect my Android phone to my Mac pretty often, I got frustrated that it kept launching Aperture every time (it used to do the same with iPhoto, before I switched).&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;In any case, the solution for this is easy enough; with the phone connected, launch the ImageCapture.app: select the phone, and choose "No application" from the drop down list in the bottom left corner:&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://2.bp.blogspot.com/-ePN3aL3YwDg/Ts1yobmRIDI/AAAAAAAAAOc/GL0L45Vadww/s1600/image-capture.png" imageanchor="1" style="clear:both; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="144" width="200" src="http://2.bp.blogspot.com/-ePN3aL3YwDg/Ts1yobmRIDI/AAAAAAAAAOc/GL0L45Vadww/s200/image-capture.png" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p style="clear:both;"&gt;This is under &lt;strong&gt;Lion&lt;/strong&gt;; your mileage may vary under earlier versions.  Hope it helps!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-1938305035859672530?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/el79u3sFMMI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/1938305035859672530/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=1938305035859672530" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/1938305035859672530?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/1938305035859672530?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/el79u3sFMMI/mac-tips-preventing-apeture-from.html" title="Mac Tips: Preventing Aperture from Launching" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-ePN3aL3YwDg/Ts1yobmRIDI/AAAAAAAAAOc/GL0L45Vadww/s72-c/image-capture.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/11/mac-tips-preventing-apeture-from.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0UHQ346fyp7ImA9WhRREE0.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-8632456392156144932</id><published>2011-11-22T08:40:00.001-08:00</published><updated>2011-11-22T15:40:32.017-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-22T15:40:32.017-08:00</app:edited><title>Gradle Training in Portland</title><content type="html">&lt;p&gt;&lt;a href="http://gradle.org/img/gradle_logo.gif" imageanchor="1" style="float:right; margin-left:1em; margin-bottom:1em"&gt;&lt;img border="0" height="77" width="280" src="http://gradle.org/img/gradle_logo.gif" /&gt;&lt;/a&gt;It's no mystery that I'm a big &lt;a href="http://gradle.org"&gt;Gradle&lt;/a&gt; fan ... it's by far the best build tool I've even seen. Build scripts are concise and readable, and Gradle the easiest build tool to configure and extend, as well.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;I fought an uphill battle with the other Tapestry developers to replace Maven with Gradle.  Yes, we had an existing Maven build and it worked (most of the time, until it didn't). Now past the switchover, we're really reaping the benefits: faster builds, fewer headaches, and it's much easier to add new sub-modules. Despite the achievements I've made with Gradle, there's a lot about this powerful tool I still need to learn.  So I was surprised and pleased when &lt;a href="http://www.augusttechgroup.com/tim/about/"&gt;Tim Berglund&lt;/a&gt; contacted me to help find a venue in Portland, Oregon for Gradle training. &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;They're now advertising that the three-day training is coming January 17-19, 2012; &lt;a href="http://gradleware.com/training"&gt;details here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;I've seen Tim speak at a number of No Fluff Just Stuff and Uberconf sessions; he's &lt;em&gt;very&lt;/em&gt; good. If you are frustrated using Ant or Maven, you need to learn Gradle, and I can't see how getting an intensive brain dump won't do anything but save you time and money.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-8632456392156144932?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/sTdbSavl81o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/8632456392156144932/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=8632456392156144932" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/8632456392156144932?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/8632456392156144932?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/sTdbSavl81o/gradle-training-in-portland.html" title="Gradle Training in Portland" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/11/gradle-training-in-portland.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYCRXw6eip7ImA9WhdaEU4.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-1379109030870708778</id><published>2011-10-20T11:42:00.000-07:00</published><updated>2011-10-20T11:42:44.212-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-20T11:42:44.212-07:00</app:edited><title>Some Tapestry Stories</title><content type="html">&lt;p&gt;A ways back, I published a call for anyone interested in &lt;a href="http://tapestryjava.blogspot.com/2011/08/tapestry-5-laptop-stickers.html"&gt;free Tapestry 5 Laptop stickers&lt;/a&gt; (that call is still open!). You get the stickers, I get a story. Here's a few highlights, in no particular order:&lt;br /&gt;
&lt;br /&gt;
&lt;dl&gt;&lt;dt&gt;Robert B., USA&lt;/dt&gt;
&lt;dd&gt;The SC Medicaid Web Portal enables doctor's offices and hospitals, using the Web, to enter and submit claims for patients enrolled in Medicaid in South Carolina.&lt;/dd&gt;
&lt;dt&gt;Steve E., Singapore&lt;/dt&gt;
&lt;dd&gt;We're producing an on-line matching system for Non Deliverable Forwards (NDFs) in the currency finance market for a brokerage firm. (Or I guess in layman's terms, a gambling system!) &lt;/dd&gt;
&lt;dt&gt;Kejo S., Switzerland&lt;/dt&gt;
&lt;dd&gt;We are working now since almost 3 years with T5 and we created an application platform for ABB with it and really a lot of other projects. I think we wrote already far more than 1 million lines around your framework! Often your framework is fun and sometimes pain, but it's always amazing how lean and clean your source is! Overall I think it is the best frontend framework in the java ecosystem right now! Great Work! &lt;/dd&gt;
&lt;dt&gt;Ivan K., Belarus&lt;/dt&gt;
&lt;dd&gt; We just started new project on t5 - online collectible card game.&lt;/dd&gt;
&lt;dt&gt;Michael L., Germany&lt;/dt&gt;
&lt;dd&gt;We started a Tapestry5 project to build our extranet application, that links into the ERP to provide realtime information to stake holdersaand supporting internal workflows. We're just at the beginning and implementing more and more stuff. However, we looked at different Web-Frameworks but Tapestry5 simply rocked! &lt;/dd&gt;
&lt;dt&gt;Greg P., Australia&lt;/dt&gt;
&lt;dd&gt;I'm using Tapestry to create &lt;a href="http://liftyourgame.com"&gt;liftyourgame.com&lt;/a&gt;. A site that allows people to achieve their goals.&lt;/dd&gt;
&lt;dt&gt;Szymon B., Poland&lt;/dt&gt;
&lt;dd&gt; We use Tapestry 5 in our economic information service neurobiz.pl. It gives users access to the information about businesses operating in Poland and registered in National Court Registry.&lt;/dd&gt;
&lt;dt&gt;Dominik Hurnaus, Austria&lt;/dt&gt;
&lt;dd&gt;Working on a large CRM system for an automotive customer.&lt;/dd&gt;
&lt;dt&gt;James S., USA&lt;/dt&gt;
&lt;dd&gt; My team and I are working on a web-based interface for a fuzzy lookup and matching engine we've developed.  I've also started messing around Tapestry for a few of my personal projects.  I started using T5 a couple months ago, and so far I'm loving it. &lt;/dd&gt;
&lt;dt&gt;Nenad N., Serbia&lt;/dt&gt;
&lt;dd&gt;I am working with 5 other developers on mobile portals developed with Tapestry for multiple clients.&lt;/dd&gt;
&lt;dt&gt;Dragan S., Macedonia&lt;/dt&gt;
&lt;dd&gt;I was a GSOC developer and now I'm trying to do new cool stuff with Tapestry like websocket integration with node.js and rabbitmq. &lt;/dd&gt;
&lt;dt&gt;Volker B., Austria&lt;/dt&gt;
&lt;dd&gt;Our project is a dealer management system which supports dealers and workshops of the VW Group's brands and the Porsche sports car brand in all sorts of operational processes in a modern and flexible way ...  in our company I think there are about 80-100 people that are using Tapestry. &lt;/dd&gt;
&lt;dt&gt;Daniel J., Canada&lt;/dt&gt;
&lt;dd&gt; Assessment dashboards for schools in southwest SK, Canada&lt;/dd&gt;
&lt;dt&gt;William O., USA&lt;/dt&gt;
&lt;dd&gt;We are working on a number of cool Facebook apps using Tapestry. One's called My Social Rankings ( mysocialrankings.com ), and the other is called Blingville (blingville.com). &lt;/dd&gt;
&lt;dt&gt;Peter P., Slovakia&lt;/dt&gt;
&lt;dd&gt; We are developing web applications for broker companies using Tapestry 5, and its great to develop with Tapestry.  &lt;/dd&gt;
&lt;dt&gt;Pablo N., Argentina&lt;/dt&gt;
&lt;dd&gt;We are using Tapestry for http://www.squidjob.com (migrating out of GWT). The site is THE place for finding service providers for anything. &lt;/dd&gt;
&lt;dt&gt;Joost S., the Netherlands&lt;/dt&gt;
&lt;dd&gt;Yanomo is time tracking, project management and invoicing software for the rest of us. Use it and "You know more" :) &lt;/dd&gt;
&lt;dt&gt;Alexander G., Belarus&lt;/dt&gt;
&lt;dd&gt;We have been using Tapestry for about 6 years in our projects. Our current project is web administration console for RadiumOne Display (www.radiumone.com) platform. We are very happy with our stack consisting from Tapestry5+Spring+Hibernate+jQuery. &lt;/dt&gt;
&lt;/dl&gt;&lt;br /&gt;
&lt;p&gt;As usual, I see a lot more Tapestry adoption outside the US; I wonder if its about programming culture ... or about Tapestry localization support? I tend to see the Europeans developers as having more freedom to work with less mainstream technologies ... but when I ask them about this, they always seem to think that it's the US developers who have that freedom.  I guess the grass is always greener.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-1379109030870708778?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/hvIg-GkrdTs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/1379109030870708778/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=1379109030870708778" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/1379109030870708778?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/1379109030870708778?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/hvIg-GkrdTs/some-tapestry-stories.html" title="Some Tapestry Stories" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/10/some-tapestry-stories.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkAGSH8_fSp7ImA9WhdbFU8.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-3140297238313799489</id><published>2011-10-13T10:55:00.000-07:00</published><updated>2011-10-13T10:58:49.145-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-13T10:58:49.145-07:00</app:edited><title>Tapestry 5.3 Maven Archetype</title><content type="html">&lt;p&gt;After struggling most of yesterday day, I finally have the Maven archetype working.  &lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Use the command:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&lt;br /&gt;
mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org/&lt;br /&gt;
&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;This points Maven at the Tapestry-specific Archetype Catalog, you want the first option ("Tapestry 5 Quickstart Project"), then choose the fourth option ("5.3-beta-20").&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;I need to fix something broken in the CSS of the archetype; otherwise it's working nicely, and demonstrates a bunch of new features in Tapestry 5.3; it also makes use of up-to date features, such as using the @Import annotation (instead of the now removed @IncludeStylesheet annotation), and some Ajax features on the first page.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;I may switch things around to make use of Twitter's &lt;a href="http://twitter.github.com/bootstrap//"&gt;Bootstrap CSS&lt;/a&gt; stylesheets.&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Here's a full session of creating the application and getting it running; it took less than 30 seconds:&lt;br /&gt;
&lt;br /&gt;
&lt;script src="https://gist.github.com/1284946.js?file=gistfile1.txt"&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-3140297238313799489?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/MGIKVqLLgB0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/3140297238313799489/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=3140297238313799489" title="11 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3140297238313799489?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3140297238313799489?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/MGIKVqLLgB0/tapestry-53-maven-archetype.html" title="Tapestry 5.3 Maven Archetype" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>11</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/10/tapestry-53-maven-archetype.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0AHR307eSp7ImA9WhdbFUk.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-3046372514163157913</id><published>2011-10-12T11:08:00.000-07:00</published><updated>2011-10-13T16:48:56.301-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-13T16:48:56.301-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><title>Things You Didn't Know About Tapestry 5.3</title><content type="html">&lt;p&gt;I missed the JavaOne &lt;a href="http://www.shaunabram.com/javaone-web-frameworks/"&gt;Comparing Web Frameworks&lt;/a&gt; talk and was appalled at some out-of-date information in it ... though reviewing his slides, it looks like he talked about Tapestry 5 but showed out-of-date Tapestry 4 examples. With Tapestry 5.3 ready very soon now (and less than a year after 5.2) it seemed like a good time to share some cool things about Tapestry:&lt;br /&gt;
&lt;br /&gt;
&lt;dl&gt;&lt;dt&gt;Tapestry Release Compatibility&lt;/dt&gt;
&lt;dd&gt; Tapestry 5.3 will be available soon; and for the majority of users, the upgrade is simply a matter of changing the version number in their pom.xml or build.gradle. Release compatibility was certainly a major headache from Tapestry 3 to Tapestry 4; and there is admittedly no direct upgrade path from Tapestry 4 to Tapestry 5 ... but Tapestry 5 was created from the ground up to prevent the kind of pain experienced in prior Tapestry releases.  To wit: &lt;ul&gt;&lt;li&gt;Services and dependency injection allows a fine-grained, not monolithic, approach to evolving the framework&lt;/li&gt;
&lt;li&gt;Metaprogramming of components allows new behaviors to be gradually introduced, bypassing the &lt;a href="http://en.wikipedia.org/wiki/Fragile_base_class_problem"&gt;fragile base class problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tapestry 5 has always carefully separated &lt;em&gt;internal&lt;/em&gt; interfaces (which is not guaranteed to be stable between releases) from stable public interfaces.  Literally, in packages named &lt;code&gt;.internal.&lt;/code&gt; so there's no guesswork about what's public and what's internal&lt;br /&gt;
&lt;/ul&gt;&lt;/dd&gt;
&lt;dt&gt;Tapestry Makes It A Snap To Work With Hibernate and JPA&lt;/dt&gt;
&lt;dd&gt;Tapestry has built-in modules for supporting both Hibernate and JPA. You get lots of stuff for free, including automatically configuring entities (just put them in the right package), easy transaction management, and smart encoding/decoding of entities  ... Tapestry knows how to convert back and forth between entity instances and primary keys when building URLs. &lt;/dd&gt;
&lt;dt&gt;Tapestry Works Great With Spring&lt;/dt&gt;
&lt;dd&gt;Tapestry integrates &lt;a href="http://tapestry.apache.org/integrating-with-spring-framework.html"&gt;very cleanly with Spring&lt;/a&gt;.  You can inject Tapestry services into Spring beans, you inject Spring beans into Tapestry services and components.&lt;/dd&gt;
&lt;dt&gt;Tapestry Plays Well With Others&lt;/dt&gt;
&lt;dd&gt;Tapestry doesn't care if you have other servlets running in the same web application. You can fine-tune how it builds URLs, or even &lt;a href="http://tapestry.apache.org/configuration.html#Configuration-tapestry.applicationfolder"&gt;put Tapestry in a box&lt;/a&gt; so it doesn't collide with other servlets or filters. You can also easily &lt;a href="http://tapestry.apache.org/session-storage.html"&gt;share information with other applications&lt;/a&gt;.&lt;/dd&gt;
&lt;dt&gt;Tapestry Likes HTML5&lt;/dt&gt;
&lt;dd&gt;Starting in Tapestry 5.3, &lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/code&gt; works perfectly in Tapestry.&lt;/dd&gt;
&lt;dt&gt;Tapestry Hot Deploys&lt;/dt&gt;
&lt;dd&gt;Tapestry (since 2006) has had &lt;em&gt;live class reloading&lt;/em&gt;; change a template or a Java file and Tapestry reloads it instantly. And since its integrated into the framework, Tapestry can be very efficient about loading and reloading resources.  Since 5.2, Tapestry has also live reloaded (most) service implementations. So code away!  Tapestry can keep up with you.&lt;/dd&gt;
&lt;dt&gt;Tapestry Loves Ajax And JavaScript&lt;/dt&gt;
&lt;dd&gt;Tapestry (currently) bundles Prototype and Scripaculous, but you can &lt;a href="https://github.com/got5/tapestry5-jquery"&gt;swap that out for jQuery&lt;/a&gt; quite easily. Tapestry has most common Ajax use-cases built in, and uses a uniform approach to rendering full pages, or individual snippets. Tapestry does a lot of other tricks, such as combining your individual JavaScript files into a single JavaScript &lt;em&gt;stack&lt;/em&gt; (on the fly, at runtime). In addition, Tapestry has an extensible framework for organizing your JavaScript and initialization code (partly on the server-side, partly on the client-side). &lt;p&gt;Tapestry 5.3 adds vastly &lt;a href="http://tapestryjava.blogspot.com/2011/08/tapestry-53-ajax-exception-reporting.html"&gt;improved reporting of server-side exceptions&lt;/a&gt;, along with an easy way of presenting alerts to users. &lt;/p&gt;&lt;/dd&gt;
&lt;dt&gt;Tapestry Is Polyglot&lt;/dt&gt;
&lt;dd&gt;Tapestry doesn't care if your classes are written in Java, Scala or Groovy ... if it's bytecode, that's all that counts.&lt;/dd&gt;
&lt;dt&gt;Tapestry Is Fast And Getting Faster&lt;/dt&gt;
&lt;dd&gt;Tapestry has been getting faster and leaner with each release. 5.2 introduced page singletons (where a single page instance can be safely shared across many threads, even though it contains mutable fields) and 5.3 boosts the performance in a bunch of ways large and small. Tapestry &lt;strong&gt;5.2&lt;/strong&gt; scored right at the top of &lt;a href="http://www.jtict.com/blog/rails-wicket-grails-play-lift-jsp/"&gt;this performance comparison&lt;/a&gt;, and Tapestry &lt;strong&gt;5.3&lt;/strong&gt; is around 30% faster. &lt;/dd&gt;
&lt;dt&gt;Tapestry Has The Best Feedback Of Any Framework, Period&lt;/dt&gt;
&lt;dd&gt; Tapestry's approach to &lt;a href="http://tapestryjava.blogspot.com/2011/10/tapestry-feedback.html"&gt;feedback&lt;/a&gt; goes far, far, far beyond any other framework or toolkit; it goes beyond the comprehensive exception report page and extends to small concerns throughout the framework: &lt;ul&gt;&lt;li&gt;Tracking what the framework is doing, and why, at all times&lt;/li&gt;
&lt;li&gt;Including extra checks for common errors and building real messages that identify what went wrong and &lt;em&gt;how to fix it&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Built-in pages to allow simple application monitoring&lt;/li&gt;
&lt;/ul&gt;&lt;/dd&gt;
&lt;dt&gt;Tapestry Really Gets Localization&lt;/dt&gt;
&lt;dd&gt; Localization support isn't an add-on; it's built-in from the ground up.  Tapestry allows templates and other assets to be localized automatically: just follow the naming convention and Tapestry uses the correct file. Tapestry has localized messages for 14 languages and counting.  &lt;/dd&gt;
&lt;dt&gt;Tapestry Is Customizable&lt;/dt&gt;
&lt;dd&gt;Tapestry's architecture, based on lots of individual services and dependency injection, means that almost any service or other logic in Tapestry can be overridden. Don't like how Tapestry builds URLs?  Replace it seamlessly.  Dont' like how Tapestry reports exceptions? Replace it!  Tapestry is designed specifically so that you can augment or replace any behavior in the framework.&lt;/dd&gt;
&lt;dt&gt;Tapestry is a Meta-Programming Monster&lt;/dt&gt;
&lt;dd&gt;And I mean that in a good way; Tapestry has powerful support built-in for &lt;a href="http://tapestryjava.blogspot.com/2010/04/meta-programming-java-with-tapestry.html"&gt;meta-programming at the services layer and at the component layer&lt;/a&gt;. Tapestry lets you get in and modify method invocations and field access, without getting your hands dirty with the ugly bytecode details. All the cool things Tapestry does with naming conventions and annotations is wide open for application-specific things. Meta-programming provides a critical alternate avenue of code reuse. &lt;/dd&gt;
&lt;dt&gt;Tapestry IoC Works Great On Its Own&lt;/dt&gt;
&lt;dd&gt;Tapestry's IoC library works great on its own, separate from the web framework itself. That includes live class reloading, meta-programming capabilities ... even a simple job scheduler.&lt;/dd&gt; &lt;/dl&gt;&lt;p&gt;... that's enough for now. The point is that Tapestry has a &lt;em&gt;lot&lt;/em&gt; going on ... to paraphrase &lt;a href="http://en.wikipedia.org/wiki/Greenspun%27s_Tenth_Rule"&gt; Philip Greenspun&lt;/a&gt;: &lt;blockquote&gt;Any sufficiently complicated servlet or JSP web application contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Tapestry. &lt;/blockquote&gt;&lt;p&gt;... and &lt;strong&gt;half&lt;/strong&gt; is being very generous!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-3046372514163157913?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/AzZLZ8S_tTY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/3046372514163157913/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=3046372514163157913" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3046372514163157913?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3046372514163157913?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/AzZLZ8S_tTY/things-you-didnt-know-about-tapestry-53.html" title="Things You Didn't Know About Tapestry 5.3" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/10/things-you-didnt-know-about-tapestry-53.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0QFSXcycCp7ImA9WhdUFks.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-1265145610054993461</id><published>2011-10-03T10:01:00.000-07:00</published><updated>2011-10-03T10:01:58.998-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-03T10:01:58.998-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="feedback" /><title>Tapestry: Feedback!</title><content type="html">I often say that the three cornerstones of Tapestry are &lt;strong&gt;Simplicity&lt;/strong&gt;, &lt;strong&gt;Consistency&lt;/strong&gt;, &lt;strong&gt;Efficiency&lt;/strong&gt;, and &lt;strong&gt;Feedback&lt;/strong&gt;. Although all four of these concepts work in concert with each other, it's &lt;strong&gt;Feedback &lt;/strong&gt;(keeping the developer informed when things go wrong) that is one of the most distinguishing features of Tapestry, and that's only gotten better in Tapestry 5.3.

&lt;p&gt;

&lt;h3&gt;Exception Reporting&lt;/h3&gt;

&lt;p&gt;
First off, there's Tapestry's default exception report page. When an exception is thrown during a request, most often a coding error in a Tapestry page or template, Tapestry moves heaven and earth to report the exception properly.  For example, in Tapestry it is not allowed for a component sub-class to define a parameter with the same name as a parameter from a base class, as this creates an ambiguity.  When this situation occurs, an exception is thrown from deep in the bowels of Tapestry:

&lt;script src="https://gist.github.com/1259462.js"&gt;
 
&lt;/script&gt;

&lt;p&gt;
That's a start, but it's not great feedback; you'll be doing a lot of work to figure out what was going on in Tapestry leading up to the exception, and from there, figuring out how to fix it; there's lots and lots of noise in the repeated stack traces (caused by nested exceptions).  However, you can see a glimmer of hope in those first few lines, the ones that start &lt;code&gt;Registry [ 1]&lt;/code&gt;, &lt;code&gt;Registry [ 2]&lt;/code&gt;, ...

&lt;p&gt;
Tapestry goes to a lot of trouble to track what is going on during the handling of a request; it keeps a stack of &lt;em&gt;operations&lt;/em&gt; which describe what Tapestry is doing at any particular time.  There's still a lot of internal details, but the gist of it is that Tapestry needed to create an instance of the ParameterConflictDemo page, and hit an error while doing something with the ParameterSubClass component (sorry for the ugly names, I'm using examples from Tapestry's internal test suite).

&lt;p&gt;
However, parsing apart the console output is &lt;strong&gt;NOT&lt;/strong&gt; what a Tapestry developer does; instead they'll get all those details, and more, from the Tapestry exception report page:

&lt;p&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-OArLYXetv84/Tone0rOfK7I/AAAAAAAAAMs/83suYnF7aWs/s1600/feedback-exceptionreport.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="324" src="http://1.bp.blogspot.com/-OArLYXetv84/Tone0rOfK7I/AAAAAAAAAMs/83suYnF7aWs/s400/feedback-exceptionreport.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;
Ah, much better. We're seeing the essential details from the stack of exceptions; we're seeing the associated template snippet that defines the parameter with the exception, we're seeing that operations stack neatly formatted. We see that stack of operations here as well, formatted for readability. Scrolling down, we see the stack trace of the deepest exception, formatted:

&lt;p&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-Pi9OCMMv7nU/TonftPKnGlI/AAAAAAAAAM0/OjzHNvDcJLs/s1600/feedback-stacktraces.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://3.bp.blogspot.com/-Pi9OCMMv7nU/TonftPKnGlI/AAAAAAAAAM0/OjzHNvDcJLs/s400/feedback-stacktraces.png" width="373" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;
The frame in bold blue?  That's a frame in the application's package, rather than code in inside Tapestry. That highlighting is very useful for letting the developer quickly figure out if the cause of the exception is a minor problem inside their code, or something more involved that shows up inside the Tapestry framework code.

&lt;p&gt;Scrolling further down, we start seeing even more relevant information: all the details of the incoming request:

&lt;p&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-iiTueRzMxf8/TongWd24CiI/AAAAAAAAAM8/ilIQTfUsOCQ/s1600/feedback-request.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://1.bp.blogspot.com/-iiTueRzMxf8/TongWd24CiI/AAAAAAAAAM8/ilIQTfUsOCQ/s400/feedback-request.png" width="373" /&gt;&lt;/a&gt;&lt;/div&gt;

... well, you get the idea.  Where a framework that takes feedback less seriously might give you a simple stack trace and leave the process of determining the underlying cause entirely up to you (after all, you have a debugger, right?) Tapestry fully embraces the importance of feedback: giving you all the information you need as soon as you need it (and yes, &lt;a href="http://tapestry.apache.org/error-page-recipe.html"&gt;you don't have to show all that to your end users&lt;/a&gt;). More than that, there's attention to detail throughout Tapestry to provide real exception messages.  For example, if you provide a component type name that doesn't match some component, Tapestry responds with a detailed message, including a list of all the known component types:

&lt;p&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-5yRgyOS6HOk/TonhUuAD4yI/AAAAAAAAANE/TasdEJTfEuo/s1600/feedback-componenttypes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://1.bp.blogspot.com/-5yRgyOS6HOk/TonhUuAD4yI/AAAAAAAAANE/TasdEJTfEuo/s400/feedback-componenttypes.png" width="373" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;
Again, Tapestry doesn't want you to have to put on your detective's hat to figure out what's wrong and how to fix it. It's providing all the details you need right when you need them.


&lt;p&gt;
&lt;h3&gt;Live Application Introspection&lt;/h3&gt;


&lt;p&gt;
What if your problems are more subtle?  How do you track down other issues, like performance problems or memory utilization?  Well, Tapestry provides some introspection to address those questions as well. Tapestry 5.3 adds the &lt;strong&gt;Page Catalog&lt;/strong&gt;, a special page for providing information about what pages have been loaded into memory, and a few details about how big they are, and how long they took to assemble:

&lt;p&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-JTPEkO-LWUk/ToniS7iQuwI/AAAAAAAAANM/P70yWXphH-Y/s1600/feedback-pagecatalog.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://3.bp.blogspot.com/-JTPEkO-LWUk/ToniS7iQuwI/AAAAAAAAANM/P70yWXphH-Y/s400/feedback-pagecatalog.png" width="373" /&gt;&lt;/a&gt;&lt;/div&gt;
Likewise, the &lt;strong&gt;Service Status&lt;/strong&gt; page gives you feedback about the services defined inside the Tapestry Inversion of Control container, helping you determine what services exist, and in what state:

&lt;p&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-4JBniEfW__4/Toni6o-ZuxI/AAAAAAAAANU/EEd5DN9u8jo/s1600/feedback-servicestatus.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://3.bp.blogspot.com/-4JBniEfW__4/Toni6o-ZuxI/AAAAAAAAANU/EEd5DN9u8jo/s400/feedback-servicestatus.png" width="373" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;

&lt;h3&gt;Deep Details&lt;/h3&gt;

&lt;p&gt;And what if you are tracking down something even more subtle?  Well, by enabling some logging, Tapestry will output that operations trace as each operation starts and finishes.  It's an avalance of information, much of it about instantiating services ... below is a log of just what happens when you first startup a Tapestry application, before it even processes it's first request:

&lt;script src="https://gist.github.com/1259528.js"&gt;
 
&lt;/script&gt;

&lt;p&gt;
Because Tapestry operates lazily, instantiating pages and services only as needed, even more happens when the first page request arrives:

&lt;script src="https://gist.github.com/1259540.js"&gt;
 
&lt;/script&gt;

&lt;p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

In any case, the point of all this is that Tapestry provides you with the key tool, information, at all stages of development. This is central to Tapestry as a tool to be &lt;em&gt;used&lt;/em&gt;: a framework that gets in the way, that makes any aspect of development harder or slower than it should be, is a framework that should not be used ... and I feel quite strongly that Tapestry is a framework meant to be used!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-1265145610054993461?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/SMNS2WRikwQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/1265145610054993461/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=1265145610054993461" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/1265145610054993461?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/1265145610054993461?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/SMNS2WRikwQ/tapestry-feedback.html" title="Tapestry: Feedback!" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-OArLYXetv84/Tone0rOfK7I/AAAAAAAAAMs/83suYnF7aWs/s72-c/feedback-exceptionreport.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/10/tapestry-feedback.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYBR3g_cSp7ImA9WhdVE0o.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-4421894186421224527</id><published>2011-09-18T12:27:00.000-07:00</published><updated>2011-09-18T12:29:16.649-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-18T12:29:16.649-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="cascade" /><title>Changes to Cascade, and a cautionary tale about defrecord</title><content type="html">&lt;p&gt;
Since I've been talking more about Clojure lately, I've spent a little more time working on &lt;a href="https://github.com/hlship/cascade"&gt;Cascade&lt;/a&gt;.  I've been stripping out a lot of functionality, so that Cascade will no work &lt;em&gt;with&lt;/em&gt; &lt;a href="https://github.com/mmcgrana/ring"&gt;Ring&lt;/a&gt; and &lt;a href="https://github.com/weavejester/compojure"&gt;Compojure&lt;/a&gt;, rather than being in competition. Its Clojure, after all, ... there's less of a reason to build a full framework since its so easy to simply assemble your functionality from proper libraries.

&lt;p&gt;
It's also been a chance to update some of my code with more modern constructs. For example, the earlier version of Cascade used a &lt;code&gt;(defstruct)&lt;/code&gt; for the DOM nodes; the new code uses &lt;code&gt;(defrecord)&lt;/code&gt;.

&lt;p&gt;
Along the way I discovered something interesting about &lt;code&gt;defrecord&lt;/code&gt;. Consider this code:

&lt;script src="https://gist.github.com/1225415.js"&gt; &lt;/script&gt;

&lt;p&gt;
Technically, this is just an optimized way to define a Clojure Map. If I have an instance, I can &lt;code&gt;(:text node)&lt;/code&gt; to get the text out of the map.

&lt;p&gt;However, &lt;code&gt;(defrecord)&lt;/code&gt; does one other thing that is barely mentioned in the documentation (and not referenced, that I can tell, in Joy of Clojure).  Notice the implementation of the &lt;code&gt;stream&lt;/code&gt; function (part of the NodeStreaming protocol).  It just says &lt;code&gt;text&lt;/code&gt;; not &lt;code&gt;(:text node)&lt;/code&gt;.  Inside a protocol method, the fields of the record are bound to local variables, making them easy to use ... another benefit.

&lt;p&gt;
I actually found this the hard way, when writing a more complicated example, for the Element DOM node:

&lt;script src="https://gist.github.com/1225419.js"&gt; &lt;/script&gt;

&lt;p&gt;Notice the use of &lt;code&gt;clojure.core/name&lt;/code&gt; to convert a keyword to a string; originally this was &lt;code&gt;(name (:name node))&lt;/code&gt; and returned &lt;code&gt;nil&lt;/code&gt;.  This confused me quite a bit!

&lt;p&gt;
What ended up happening was that &lt;code&gt;name&lt;/code&gt; was bound to the keyword from the record's &lt;code&gt;name&lt;/code&gt; field.  However, Clojure keywords can be used as functions,and was incidentally passed itself, which is to say (for an Element node representing a &amp;lt;p&amp;gt; element): &lt;code&gt;(name (:name node)) --&amp;gt; (:p :p) --&amp;gt; nil&lt;/code&gt;.

&lt;p&gt;
So, &lt;code&gt;(defrecord)&lt;/code&gt; giveth, but it also taketh away, at least, the first time. In other words, watch out for name collisions between the names of the record's fields, and the names of functions you want to reference from your protocol method implementations.

&lt;p&gt;
Back to Cascade; I don't have any metrics available about performance changes with the new code (using records and protocols), but I suspect its faster and more efficient.

&lt;p&gt;
A lot of the features that were in Cascade are gone and will come back soon. Ultimately, I'll have Cascade flavors of context and classpath assets from Tapestry, as well as mechanisms similar to Tapestry for adding JavaScript libraries and CSS stylesheets, along with a mechanism similar to Tapestry for organizing them into stacks.

&lt;p&gt;
Looking further forward, adding support for &lt;a href="https://github.com/cgrand/enlive"&gt;Enlive&lt;/a&gt;, both reading parsed XML templates in as DOM structure and allowing Enlive transformations onto the DOM structure, seems like a good direction.

&lt;p&gt;
When will all this happen? I'm not certain, but I hope that Cascade will become a "must-have" layer on top of Compojure, adding some of the industrial strength concepts from Tapestry into the fast-and-loose world of Clojure web applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-4421894186421224527?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/30YjnEYSBsY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/4421894186421224527/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=4421894186421224527" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/4421894186421224527?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/4421894186421224527?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/30YjnEYSBsY/changes-to-cascade-and-cautionary-tale.html" title="Changes to Cascade, and a cautionary tale about defrecord" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/09/changes-to-cascade-and-cautionary-tale.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0ACQHw7eSp7ImA9WhRRGEk.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-6698587573518960143</id><published>2011-08-31T11:54:00.000-07:00</published><updated>2011-12-02T10:16:01.201-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-02T10:16:01.201-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><title>Tapestry 5 Laptop Stickers</title><content type="html">&lt;p&gt;I'm the kind of guy who does a lot of self-expression on my laptop:  &lt;p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-HNa4xLImnOc/Tl6CKS7AVfI/AAAAAAAAALc/1G0B5PyiDNM/s1600/content___media_external_images_media_193.jpeg" imageanchor="1" style=""&gt;&lt;img border="0" height="291" width="400" src="http://2.bp.blogspot.com/-HNa4xLImnOc/Tl6CKS7AVfI/AAAAAAAAALc/1G0B5PyiDNM/s400/content___media_external_images_media_193.jpeg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;Hey, what's that at the bottom?  A Tapestry 5 laptop sticker?  Cool!  &lt;p&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-98pE_6XtdZ4/TtkVtM5qaVI/AAAAAAAAAOo/E5qJiOhhp2g/s1600/t5-laptop-sticker.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="210" width="400" src="http://2.bp.blogspot.com/-98pE_6XtdZ4/TtkVtM5qaVI/AAAAAAAAAOo/E5qJiOhhp2g/s400/t5-laptop-sticker.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Want one?  Drop me a line at &lt;code&gt;hlship at gmail dot com&lt;/code&gt; with the subject "T5 STICKER". Provide me with a quick (one-line summary) of your project and a snail-mail address (outside the US is OK) and how many laptop stickers you need (keep it &lt;em&gt;reasonable&lt;/em&gt;, as in, how many of your project developers will actually put one on their laptop).  &lt;p&gt;First come, first serve, supplies are limited, offer may be rescinded at any time!  &lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I'm really enjoying seeing all the stories of people using Tapestry; if it's OK, I'd like to generate a list of people (just first name and last initial), country, and a one-line project summary, to post on this blog, or on tapestry.apache.org. &lt;p&gt;&lt;br /&gt;
&lt;strong&gt;If you do NOT want to be publicized, please indicate that in your email.&lt;/strong&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-6698587573518960143?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/hJtoVeK1oF0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/6698587573518960143/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=6698587573518960143" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/6698587573518960143?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/6698587573518960143?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/hJtoVeK1oF0/tapestry-5-laptop-stickers.html" title="Tapestry 5 Laptop Stickers" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-HNa4xLImnOc/Tl6CKS7AVfI/AAAAAAAAALc/1G0B5PyiDNM/s72-c/content___media_external_images_media_193.jpeg" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/08/tapestry-5-laptop-stickers.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4FR34zfip7ImA9WhdXF04.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-4437476787013697421</id><published>2011-08-30T13:58:00.000-07:00</published><updated>2011-08-30T13:58:36.086-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-30T13:58:36.086-07:00</app:edited><title>How They See Us, How We See Them</title><content type="html">&lt;p&gt;Stumbled across this great graphic on &lt;a href="http://www.globalnerdy.com/"&gt;GlobalNerdy Blog&lt;/a&gt;.

&lt;p&gt;
&lt;img src="http://www.globalnerdy.com/wordpress/wp-content/uploads/2011/08/developers-designers-pms-qa.jpg"/&gt;

&lt;p&gt;
Sorry my blog layout gets in the way, try the &lt;a href="http://www.globalnerdy.com/wordpress/wp-content/uploads/2011/08/developers-designers-pms-qa.jpg"&gt;direct link to the image&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-4437476787013697421?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/TRpFD9NlLi8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/4437476787013697421/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=4437476787013697421" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/4437476787013697421?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/4437476787013697421?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/TRpFD9NlLi8/how-they-see-us-how-we-see-them.html" title="How They See Us, How We See Them" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/08/how-they-see-us-how-we-see-them.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEcMR346fip7ImA9WhdXEEs.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-7757207470309155337</id><published>2011-08-22T19:21:00.000-07:00</published><updated>2011-08-22T19:21:26.016-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-22T19:21:26.016-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><title>Clojure: Towards the Essence of Programming</title><content type="html">&lt;p&gt;My talk from the &lt;a href="http://www.whatsnextparis.com/"&gt;What's Next Paris conference&lt;/a&gt;, &lt;em&gt;Clojure: Towards The Essence of Programming&lt;/em&gt; is now online at &lt;a href="http://www.infoq.com/presentations/Clojure-Towards-The-Essence-Of-Programming"&gt;InfoQ&lt;/a&gt;. This is the full talk ... video plus slides. See if you can spot the point where I almost pass out from jet lag!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-7757207470309155337?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/nOEYw_iUoT0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/7757207470309155337/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=7757207470309155337" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/7757207470309155337?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/7757207470309155337?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/nOEYw_iUoT0/clojure-towards-essence-of-programming.html" title="Clojure: Towards the Essence of Programming" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/08/clojure-towards-essence-of-programming.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0ANRHszfSp7ImA9WhdQFU8.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-6885688864277171154</id><published>2011-08-16T12:09:00.000-07:00</published><updated>2011-08-16T12:09:55.585-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-16T12:09:55.585-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><category scheme="http://www.blogger.com/atom/ns#" term="ajax" /><title>Tapestry 5.3 Ajax Exception Reporting</title><content type="html">&lt;p&gt;I just put together this screencast about an exciting improvement to how Tapestry 5.3 presents server-side exceptions to the client.

&lt;embed src="http://blip.tv/play/AYLOhXUA" type="application/x-shockwave-flash" width="1024" height="798" wmode="transparent" allowscriptaccess="always" allowfullscreen="true"&gt; &lt;/embed&gt;

&lt;p&gt;
Tapestry is certainly moving further and further into the rich client space; I think there's some compelling features of Tapestry that make splitting the application across the client web browser and the server quite attractive, including a uniform approach to rendering (both traditional page oriented requests, and Ajax partial page renders).  In any case, the weak link in the chain used to be that with Ajax requests, server-side exceptions sent you scurrying to look at the console, and you would lose (along the way) a lot of the power of the Tapestry exception report page; now you get to have your cake and eat it too.

&lt;p&gt;
The real excitement will be coming in Tapestry 5.4, which will push much deeper into improved JavaScript and Ajax support, including a move to framework agnosticism (as in, switch over to jQuery seamlessly). Part of that support is already in 5.3, with more to come.

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-6885688864277171154?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/KVI0Hkf4IyI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/6885688864277171154/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=6885688864277171154" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/6885688864277171154?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/6885688864277171154?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/KVI0Hkf4IyI/tapestry-53-ajax-exception-reporting.html" title="Tapestry 5.3 Ajax Exception Reporting" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/08/tapestry-53-ajax-exception-reporting.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0cESHg6eyp7ImA9WhdSGE0.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-7197888102539726661</id><published>2011-07-27T16:23:00.000-07:00</published><updated>2011-07-27T16:23:29.613-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-27T16:23:29.613-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="groovy" /><title>GroovyCasts</title><content type="html">&lt;p&gt;If you are a fan of Groovy, or even just curious what the fuss is about, there's a new resource for you: &lt;a href="http://groovycasts.org/"&gt;Groovy Casts&lt;/a&gt;.  Groovy Casts is a blog by my good friend &lt;a href="http://www.curiousattemptbunny.com/"&gt;Merlyn Albery-Speyer&lt;/a&gt; containing short (five minute or less) screen casts about using Groovy, or about interesting and useful Groovy tools and libraries. 

&lt;p&gt;
Some of the screen casts are done by Merlyn, others are simply collected by him from all over the web. Check it out!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-7197888102539726661?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/pvXQIOzugTQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/7197888102539726661/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=7197888102539726661" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/7197888102539726661?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/7197888102539726661?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/pvXQIOzugTQ/groovycasts.html" title="GroovyCasts" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/07/groovycasts.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYBQ3w_fip7ImA9WhZbEUg.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-6833949164661467097</id><published>2011-06-15T09:59:00.000-07:00</published><updated>2011-06-15T09:59:12.246-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-15T09:59:12.246-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="gradle" /><title>Thanks Luke Daley!</title><content type="html">&lt;p&gt;Just wanted to reach out and thank &lt;a href="http://ldaley.com/"&gt;Luke Daley&lt;/a&gt;, who just joined &lt;a href="http://gradleware.com/"&gt;Gradleware&lt;/a&gt;, for giving me a big help with my Gradle build issues. We had a nice chat, did a little screen-sharing, and worked out how to get Tapestry to create the necessary PGP signature files for uploading. I can't imagine any ordinary company offering that level of support to an ordinary user ... or even a user with an expensive support contract.  Kudos!

&lt;p&gt;
But I don't want to scare anyone away from Gradle ... I'm having to pencil pretty far outside the lines for a few specific Apache.org requirements, things that most people will &lt;em&gt;never&lt;/em&gt; have to deal with. Even so, many of the problems I'm hitting will be correctly addressed in the future, possibly in the final 1.0 Gradle release. I'm now using Gradle in &lt;em&gt;all&lt;/em&gt; my projects (for my clients and for my open source projects) without any significant problems ... and I encourage you to do so as well!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-6833949164661467097?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/Z8fa8A_EAWY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/6833949164661467097/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=6833949164661467097" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/6833949164661467097?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/6833949164661467097?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/Z8fa8A_EAWY/thanks-luke-daley.html" title="Thanks Luke Daley!" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/06/thanks-luke-daley.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEIARXs8fyp7ImA9WhZUEUk.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-5236695800193214406</id><published>2011-06-03T17:15:00.000-07:00</published><updated>2011-06-03T17:15:44.577-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-03T17:15:44.577-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><title>What's Next? Paris Clojure Slides</title><content type="html">&lt;p&gt;Here's my slides from the &lt;a href="http://www.whatsnextparis.com/index.html"&gt;What's Next? conference in Paris&lt;/a&gt;. I found a couple of minor errors on review, and I'm expecting to reorganize it a bit before my next talk.

&lt;div style="width:425px" id="__ss_8201831"&gt; &lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/hlship/clojure-essenceprogramming" title="Clojure: Towards The Essence Of Programming (What&amp;#39;s Next? Conference, May 2011)"&gt;Clojure: Towards The Essence Of Programming (What&amp;#39;s Next? Conference, May 2011)&lt;/a&gt;&lt;/strong&gt; &lt;iframe src="http://www.slideshare.net/slideshow/embed_code/8201831" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"&gt;&lt;/iframe&gt; &lt;div style="padding:5px 0 12px"&gt; View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/hlship"&gt;Howard Lewis Ship&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-5236695800193214406?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/goS9a4cB9vU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/5236695800193214406/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=5236695800193214406" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/5236695800193214406?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/5236695800193214406?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/goS9a4cB9vU/whats-next-paris-clojure-slides.html" title="What's Next? Paris Clojure Slides" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/06/whats-next-paris-clojure-slides.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUARHo8fyp7ImA9WhZVE0g.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-2504549892793436962</id><published>2011-05-24T14:09:00.000-07:00</published><updated>2011-05-25T13:10:45.477-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-25T13:10:45.477-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><title>The Tragedy Of Checked Exceptions</title><content type="html">&lt;p&gt;
If you ever get one of those interview questions along the lines of &lt;em&gt;"What DON'T you like about Java?"&lt;/em&gt;, I would hope that &lt;strong&gt;checked exceptions&lt;/strong&gt; are at the top of your list.  I think no other, ahem, &lt;em&gt;feature&lt;/em&gt;, of Java has caused more code bloat, more problems, and less stability than checked exceptions. Java was the &lt;a href="http://en.wikipedia.org/wiki/Checked_exceptions#Checked_exceptions"&gt;first main-stream language to include this concept&lt;/a&gt; (to my knowledge), the only (widely used) programming language that has it, and 
I strongly hope it will be the &lt;em&gt;last&lt;/em&gt; programming language to include it.

&lt;p&gt;
Checked exceptions cause a lot of grief.  They often pollute APIs: look at how the JDBC API plays "cover your ass" by making every single method &lt;code&gt;throws JDBCException&lt;/code&gt; regardless of which methods do any work that could possibly fail. At best, checked exceptions make sense &lt;em&gt;only&lt;/em&gt; when there is a clear and documented way to &lt;em&gt;recover&lt;/em&gt; from the exception (such as waiting and retrying the failed operation).  In many cases, a simple boolean, indicating success or failure, would be much better than an exception, and accomplish the same goals with far less cost.  

&lt;p&gt;
Checked exception's also encourage another terrible pattern: exception swallowing.  &lt;em&gt;"What do I do with this here MyAPIIsBrokenException?  Well Eclipse just inserts code to print out the stack trace, so that's good enough."&lt;/em&gt;  Thus real errors get discarded, and code that should break during testing slips through the cracks, causing nasty runtime failures and ominous messages to the console.

&lt;p&gt;Really, what can you do with an exception?  Either handle it locally and immediately, or wrap it in another exception, usually RuntimeException, and rethrow it ... but that approach is only effective if some higher layer does a good job of reporting the entire stack of exceptions, the way Tapestry does.  More often, the exception just percolates up to a top-level loop and spews out a few hundred lines of glop onto the console or log.

&lt;p&gt;
I think part of the proof that checked exceptions are simply unworkable is the way &lt;code&gt;throws Exception&lt;/code&gt; is creeping into standard APIs, such as the &lt;a href="http://cr.openjdk.java.net/~darcy/ProjectCoin/ProjectCoin-Documentation-v0.9375.html"&gt;ones specified in project Coin&lt;/a&gt; (I'm thinking of Autocloseable).  And what is the semantic value of &lt;code&gt;throws Exception&lt;/code&gt;?  It's useless ... because you are either going to log that exception to the console or wrap it in a new RuntimeException and re-throw it.  So the authors of Autoocloseable have simply shifted work onto your lap (you get to write the code to catch it and rethrow it) when if they simply omitted the throws clause, and documented that "close() methods may throw a runtime exception" you could get the &lt;em&gt;exact&lt;/em&gt; same effect, but write much less code.

&lt;p&gt;I've also seen that checked exceptions have been a factor in the delays for JDK 8 Lambdas, complicating that specification much further than it needed to be, and forcing new and odder syntax into the language to accompany it.

&lt;p&gt;Meanwhile, post-Java JVM languages ... including Groovy, Fantom, and Clojure ... simply ignore checked exceptions; which is easy enough to do as they are almost entirely a fiction of the Java compiler in the first place.   You can write try...catch blocks in these languages, but there's no pressing need to, and application stability ends up being &lt;em&gt;higher&lt;/em&gt; than in traditional Java code.

&lt;p&gt;It is unfortunate that of all the ideas that Gosling, Joy, and folks had at the dawn of the Java language, they deferred ones we've really been missing (such as reified types and lambdas) and included truly experimental features, such as checked exceptions. But that's just hind-sight and second-guessing. The real tragedy is that, unlike (for example) JavaScript, with Java you can't &lt;a href="http://oreilly.com/catalog/9780596517748"&gt;just use the good parts&lt;/a&gt;. Instead, Java snares you with an almost irrational compulsion to preserve the early mistakes in the language, forever.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-2504549892793436962?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/Kxm0towZO90" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/2504549892793436962/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=2504549892793436962" title="22 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/2504549892793436962?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/2504549892793436962?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/Kxm0towZO90/tragedy-of-checked-exceptions.html" title="The Tragedy Of Checked Exceptions" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>22</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/05/tragedy-of-checked-exceptions.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0ANQH09fSp7ImA9WhZVEUo.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-3904031554424495545</id><published>2011-05-23T10:12:00.000-07:00</published><updated>2011-05-23T12:43:11.365-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-23T12:43:11.365-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tapestry" /><title>Learn more about Tapestry power features @ Java Magic</title><content type="html">&lt;p&gt;
New Tapestry user "tawus" has been working his way though the power features of Tapestry and Tapestry IoC on his blog: &lt;a href="http://tawus.wordpress.com/"&gt;Java Magic&lt;/a&gt;.  It's an interesting approach, he's covering one feature of Tapestry on each post, and showing what a (simplified) implementation of that feature would be.  I'd rather he spent a little introduction time on why these features are useful and important, but it's still a very nice effort.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-3904031554424495545?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/E23OrrW6A04" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/3904031554424495545/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=3904031554424495545" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3904031554424495545?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3904031554424495545?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/E23OrrW6A04/learn-more-about-tapestry-power.html" title="Learn more about Tapestry power features @ Java Magic" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>5</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/05/learn-more-about-tapestry-power.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcERH4_eSp7ImA9WhZWGU8.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-3962824557589634625</id><published>2011-05-20T14:46:00.000-07:00</published><updated>2011-05-20T14:46:45.041-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-20T14:46:45.041-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javadoc" /><title>Extending JavaDoc</title><content type="html">&lt;p&gt;
I don't think I've seen a piece of code more poorly designed for extension, and more in need of it, than JavaDoc.  I'm in the process of removing Tapestry's Maven-based component report (as part of an overall move from Maven to Gradle).  My goal is to merge what currently comes from the component report directly into the JavaDocs.

&lt;p&gt;
My first approach was to extend the built-in HtmlDoclet , the one that generates the every-day HTML report.  Good luck with that ... it's like a field guide to anti-patterns for preventing extensibility.  Here's an example:

&lt;pre name="code" class="java"&gt;
public class HtmlDoclet extends AbstractDoclet {
    
    /**
     * The global configuration information for this run.
     */
    public ConfigurationImpl configuration = 
        (ConfigurationImpl) configuration();
    

    /**
     * Create the configuration instance.
     * Override this method to use a different
     * configuration.
     */
    public Configuration configuration() {
        return ConfigurationImpl.getInstance();
    }

    ...
}

public class ConfigurationImpl extends Configuration {

    /**
     * Constructor. Initialises resource for the
     * {@link com.sun.tools.doclets.MessageRetriever}.
     */
    private ConfigurationImpl() {
        standardmessage = new MessageRetriever(this,
            "com.sun.tools.doclets.formats.html.resources.standard");
    }
    
    public static ConfigurationImpl getInstance() {
        return instance;
    }

   ...
}
&lt;/pre&gt;

&lt;p&gt;So, HtmlDoclet commits the cardinal sin of down-casting from the interface to the implementation class, and ConfigurationImpl is effectively final, as its only constructor is private.  But you are encouraged to override the configuration anyway (recommended only if your goal is to throw a ClassCastException).

&lt;p&gt;
JavaDoc is &lt;em&gt;old&lt;/em&gt;.  The HtmlDoclet is just ... tired.  Someone failed to tell these folks about XML and XSL, for example ... or about HTML and CSS, for that matter. JavaDoc is screaming out to be a tool that generates an XML representation of Java source content that can then be transformed into an HTML document tree via XSLT.  I've seen an &lt;a href-"http://code.google.com/p/xml-doclet/"&gt;abandoned project&lt;/a&gt; along those lines.  Perhaps in my spare time ... it would be a fun little side project to create that, and create a really world-class JavaDoc.

&lt;p&gt;
In any case ... I've been forced to impose the use of a &lt;code&gt;@tapestrydoc&lt;/code&gt; tag on component classes that wish to be documented.  Not the end of the world, but not backwards compatible either (though the Tapestry 5.2 Maven component report will continue to work with Tapestry 5.3, so that's not a deal-breaker).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-3962824557589634625?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/Y6B6L0ItwVY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/3962824557589634625/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=3962824557589634625" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3962824557589634625?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/3962824557589634625?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/Y6B6L0ItwVY/extending-javadoc.html" title="Extending JavaDoc" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>5</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/05/extending-javadoc.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0MNR3s5cSp7ImA9WhZREEg.&quot;"><id>tag:blogger.com,1999:blog-4110180.post-5635272566968935372</id><published>2011-04-05T19:04:00.000-07:00</published><updated>2011-04-05T19:04:56.529-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-05T19:04:56.529-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="spock" /><category scheme="http://www.blogger.com/atom/ns#" term="groovy" /><title>An Example Of Why I Like Spock</title><content type="html">&lt;p&gt;&lt;a href="http://code.google.com/p/spock/"&gt;Spock&lt;/a&gt; is really making writing tests fun, instead of a chore.

&lt;pre name="code" class="java"&gt;

    @Unroll("toClass '#javaName' should be #expectedClass")
    def "toClass tests"() {
        expect:

        PlasticInternalUtils.toClass(getClass().classLoader, javaName) == expectedClass

        where:

        javaName | expectedClass
        "java.lang.String" | String.class
        "java.lang.Integer[]" | Integer[].class
        "java.lang.Long[][]" | Long[][].class
        "void" | void.class
        "int" | int.class
        "int[]" | int[].class
        "float[][]" | float[][].class
    }

&lt;/pre&gt;

&lt;p&gt;
This combines lots of things I've seen before in &lt;a href="http://testng.org/doc/index.html"&gt;TestNG&lt;/a&gt;, but nicer; just more readable ... and the @Unroll annotation (which guides Spock on how to report the test execution) is really handy, especially when things go wrong.  It's just a slick overall package.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4110180-5635272566968935372?l=tapestryjava.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/TapestryCentral/~4/gMbBqJVhc1M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://tapestryjava.blogspot.com/feeds/5635272566968935372/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4110180&amp;postID=5635272566968935372" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/5635272566968935372?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4110180/posts/default/5635272566968935372?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/TapestryCentral/~3/gMbBqJVhc1M/example-of-why-i-like-spock.html" title="An Example Of Why I Like Spock" /><author><name>Howard Lewis Ship</name><uri>https://profiles.google.com/117875267367337641160</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-bHfrlWJkyI8/AAAAAAAAAAI/AAAAAAAAAAA/3ItMOpiIgco/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://tapestryjava.blogspot.com/2011/04/example-of-why-i-like-spock.html</feedburner:origLink></entry></feed>

