Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Sunday, May 31, 2009

Back from Training and Portland Code Camp

It's been a rough week ... I still had my sore throat (noticeable during the webinar) when I arrived to do four days of accelerated Tapestry training in Michigan. Returning after midnight on Friday, I had morning and afternoon slots at Portland Code Camp on Saturday to talk about Clojure and Tapestry. I think it was a good little conference, and 75 minute time slots are just barely enough time to say something meaningful.

I attended a nice introduction to jQuery (once again confirming that I backed the wrong horse when selecting Prototype over jQuery for Tapestry), and another session on coding for iPhone.

The only other session I attended was iPhone Development from an ex Softie by Rory Blyth. It was entertaining in an unusual way, since Rory is very glib in a stream of consciousness kind of way, but he spent all but five minutes of his time ranting against Objective-C and iPhone toolkits. Literally he had five minutes for the core of his talk!

I was one of a few people in the audience who knew Objective-C (though it's more than ten years since I coded in it) and found many of his objections quite unreasonable. Basically, he wants Objective-C to look like every other language derived from C, which is missing the point of what Objective-C actually is: a melding of concepts from C and Smalltalk designed to operate on the very constrained hardware available, even for desktops, in the late 80's. It obligates developers to do something unreasonable by today's standards (a cumbersome retain count mechanism, rather than garbage collection), and the (optional) type syntax (such as (NSString *)) reveals its C heritage (as Smalltalk doesn't deal with declared types).

I even made this point to him; that Objective-C may be a natural fit for the constrained devices such as mobile platforms. His response to any challenge from any audience member was that we were afflicted with "Stockholm syndrome".

Strangely, a few minutes after I pointed out the "constrained device" theory (which he dismissed, disjointedly citing Windows smart phones as a "success") he then talked about ... the constraints of the iPhone in terms of memory, battery and CPU utilization.

Basically, Rory is unable to wrap his head around anything unfamiliar or to understand how a difference in philosophy can inform how a language syntax evolves, as well as the terminology (i.e., Objective-C's "receivers", "messages" and "selectors") used to describe that language.

There's a quote from the book Freakonomics, roughly (from memory):

Morality is how we think we should live our lives. Economics reveals how we actually do.

Rory has a kind of "language morality" that states the objects should be listed first, with periods separating member access, such as method invocation, and that languages that deviate from this are failed and broken. Unfortunately for that argument, the explosive success of the iPhone and the iPhone app market indicates that Objective-C is a tremendous development platform for the kind of intuitive, focused, responsive applications that dominate the market.

It was a shame, because his style was entertaining, if very "slacker" styled, and if he organized his thoughts a bit and kept track of the clock, his valid criticisms of the iPhone development environment would hold a bit more weight and reach a wider, more receptive audience.

Tuesday, May 26, 2009

Tapestry 5 Support in IDEA Maia

The next generation of IDEA, Maia will include Tapestry 5 support; this is according to Hugo Palma, who wrote the initial version of the IDEA Tapestry plugin that has been expanded for inclusion in Maia. Cool!

Monday, May 25, 2009

Tapestry & Clojure @ Portland Code Camp

Portland Code Camp is a local (in Portland) free conference on development tools and languages ... and it's coming up fast (this Saturday). I'll most likely be talking about Tapestry and Clojure ... I actually won't know until they make their last minute session selection tomorrow (it's based on how many attendees express interest). If you're in the Portland area and want to learn a bit more about Tapestry or Clojure, sign up and vote!

Sunday, May 24, 2009

Tapestry Road Show: Ann Arbor, Michigan

This week, I'll be in Ann Arbor, MI to do a bit (aka, four grueling days) of on-site Tapestry training. If you're in Ann Arbor and would like to get together for a drink and a chat about Tapestry, Open Source, or anything along those lines, please drop a line!

Wednesday, May 20, 2009

Last call: Tapestry Webinar (Thu May 21)

Formos is sponsoring a live Tapestry Webinar on May 21st. I'll be presenting a 35 - 40 minute introduction to Tapestry 5, followed by live Q&A. I'll be focusing on the big-picture issues about what makes Tapestry useful, unique and fun. If you've been curious about what Tapestry is, or want a chance to ask a question about Tapestry directly, this is a great chance to do it!

Click a link below to register for the Webinar:

Thursday, May 14, 2009

Tapestry 5 Performance Study

Ben Gidley has done a detailed analysis of Tapestry 5 performance He used some quite reasonable assumptions, including running tests for over an hour to gauge memory use over time. His conclusion is that Tapestry performance is excellent, which is a relief if not a surprise.

What I'm really pleased with is that he's demonstrated that Tapestry will perform well under load; more importantly, if a Tapestry application gets "Slashdotted", it may slow down, but it recovers once the wave of requests dies down.

Ben compared a Tapestry application to a Struts application with similar behavior and found that T5 out-performed Struts pretty handily, despite the fact that T5 doubles the number of requests (due to its automatic redirect-after-post behavior).

Thanks for the great work Ben!

Tuesday, May 12, 2009

Clojure @ JavaWorld

Another nice introduction to Clojure is now online: Clojure: Challenge your Java Assumptions at JavaWorld. An introductory Tapestry article is due at JavaWorld shortly as well.

Saturday, May 09, 2009

Apache Tapestry Community Map

Inspired by a map for Clojure, I created a map for Tapestry users:

View Apache Tapestry Users Worldwide in a larger map

Want to add yourself? The map is wide open, something I'll probably regret later.

Update: People have been adding themselves over the weekend, and trends are starting to appear. It seems like adoption is heavier in Europe than in the US (at least, among the narrow set of people who use Tapestry, know about the map, and have actually added themselves). Still, if that sample is true, it begs the question: why? Is it more to do with the European developers, or due to features of Tapestry (such as localization support).

I'm tending towards the former: talking with many Europeans shows that managers have a more "hands off" attitude towards project leaders, who are given more free reign to use tools of choice as long as they deliver. But that's also a horrible generalization from limited data. In fact, a contrary opinion was offered by some developers in Oslo last september ... in Norway, a small number of developers make strong vocal choices (lately, Spring and SpringMVC) and the whole community follows along. But even Europe is a big place which room for many different developer cultures.

Friday, May 08, 2009

Tapestry 5 Webinar: May 21st

Formos is sponsoring a live Tapestry Webinar on May 21st. I'll be presenting a 35 - 40 minute introduction to Tapestry 5, followed by live Q&A. I'll be focusing on the big-picture issues about what makes Tapestry useful, unique and fun. If you've been curious about what Tapestry is, or want a chance to ask a question about Tapestry directly, this is a great chance to do it!

Click a link below to register for the Webinar:

Wednesday, May 06, 2009

Apache Tapestry 5.1 Final Release -

The stable release for Tapestry 5.1, release, is now available for download.

Tapestry 5.1 includes a large number of improvements to the speed and scalability of Tapestry without sacrificing backwards compatibility. However, you should still consult the upgrade notes that identify a couple of minor "gotchas".

Major improvements from Tapestry 5.0 to Tapestry 5.1:

  • Performance optimizations that speed rendering of very complex pages, and reduce memory usage
  • Static JavaScript libraries will now be dynamically combined into a single request
  • Automatic GZIP compression of static and dynamic content, for clients that support it
  • Significant improvements to Tapestry's property expression language
  • Partial page updates (via Ajax) may now update multiple client-side Zones
  • Many new features and improvements to Tapestry template files
  • Improved client- and server-side validation of numeric input values
  • User's preferred locale is now encoded into the URL, rather than stored in a cookie
  • A new ProgressiveDisplay component for incremental page loading
  • Vastly improved Quickstart Archetype
  • New Tapestry IoC features to make it easier to override services and contributions to service configurations
  • Many new localizations of validation messages
  • Component reference documentation now identifies events triggered by components
  • Really slick new client-side logging facility (based on Blackbird)
  • New API for URL rewriting (to provide improved control over Tapestry URLs)

... and many, many more bug fixes and improvements; full details are available in the release notes.

Download Tapestry

Tuesday, May 05, 2009

Clojure: Turns out, parsing is somewhat iterative, not functional

... at least for me. I'm working on some more real-world code in Clojure, and I'm struggling with turning a stream of tokens (representing XML content parsed from a stream) into a DOM-like hierarchy. The tokens represent open tags, attributes, close tags, and text content.

I've written dozens of recursive decent compilers in the past, in Pascal, C, PL/1 and Java. The problem is, that style of compiler works by consuming a stream (of characters or tokens) ... a destructive operation.

Logically, I want code such as:

List parseTemplate(tokens)
  result = []

  while (tokens not empty)
    token = pop(tokens)
    if (token.type == text)
      add text token to result;

    if (token.type == element)
      e = parse_element (token, tokens)
      add e to result;

Now, in Clojure I can write a (parse-element) function that takes a bunch of tokens, and it can work its way through the tokens using Clojure's (loop) and (recur) forms ... but to the calling function, tokens will not have changed.

One approach might be to pass along some pretty complex state; literally a state machine (inside a map) so that each function can keep push and popping values into and out of the state before invoking each. This would involve recursing on each token. Anyway, I have a rough idea of what that would look like and it seems complicated.

Instead, I've been pounding my head, looking at Clojure's support for monads. Monad are a way to combine certain types of functions together in a way that appears as if there were mutable state. You provide some base types of monadic functions and, within the context of a monad definition, it streamlines the way those basic monadic functions can be strung together to form more complex monadic functions.

Still trying to wrap my head around the concept however. It gets really confusing because the real power of monads appears when the monadic values, the things returned from a monadic function, are themselves functions. Yep, that's where I get lost too.

Update: passed the initial Monad hurdle; I'm finally beginning to understand them, perhaps even enough to explain them to other people.

Sunday, May 03, 2009

Excellent Clojure Introduction

R. Mark Volkmann has put together a terrific Clojure Introduction. It starts with basic syntax and quickly works through all the core functions, broken out by categories such as "Collections", "Iteration" or "Concurrency".

Very brisk, but full of functions (from the core) that I didn't know of.

For example, here's a function I never noticed before, (get-in), as defined in the Clojure API

(get-in m ks)
returns the value in a nested associative structure, where ks is a sequence of keys

That's accurate, but really encourages you to read the source or experiment in the REPL to see exactly how it works. Worse, its mixed in with hundreds of other functions and really easy to miss (I never noticed it before!).

Here's what the Clojure Introduction says:

To demonstrate this we'll create a map that describes a person. It has a key whose value describes their address using a map. It also has a key whose value describes their employer which has its own address map.

(def person {
  :name "Mark Volkmann"
  :address {
    :street "644 Glen Summit"
    :city "St. Charles"
    :state "Missouri"
    :zip 63304}
  :employer {
    :name "Object Computing, Inc."
    :address {
      :street "12140 Woodcrest Executive Drive, Suite 250"
      :city "Creve Coeur"
      :state "Missouri"
      :zip 63141}}})

The get-in function takes a map and a key sequence. It returns the value of the nested map key at the end of the sequence. The -> macro and the reduce function can also be used for this purpose. All of these are demonstrated below to retrieve the employer city which is "Creve Coeur".

(get-in person [:employer :address :city])
(-> person :employer :address :city) ; explained below
(reduce get person [:employer :address :city]) ; explained below

This is a document to keep linked and to print out and study. Thanks Mr. Volkmann!