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!

Monday, November 24, 2008

Goodbye Vienna, Hello NetNewsWire

Seems like the Mac has a huge number of RSS readers. For a while I was using Vienna, but it stopped working after a recent update (no blogs ever updated!). So I just switched to NetNewsWire. So far, I like it quite a bit!

I was even able to keep my old subscriptions by dragging them (alas, one at a time) from the Vienna window the the NNW window.

I'm sure everyone has their favorite, and its not like I did a systematic survey; just a quick couple of Google searches and decided I liked it.

Friday, November 21, 2008

Maven: Displaying the version number in the generated site

It took a lot of ferreting out, but I eventually learned that you can put a <version/> tag in your site.xml, and Maven will put the project version number into the generated site. By default, it appears at the top, next to the (default position of) the publish date. <version/> can have a position attribute, as with <publishDate/>.

Begin rant.

And by ferreting, I mean about an hour of following broken links and scanning undocumented code, jumping to random places in the Maven SVN, trying experiments, and other forms of unnecessary guesswork. As usual, it would have been nice if it was documented; likewise it would be nice if the Maven brats ate their own fucking dog food when it comes to site generation.

As is, if you want to figure this stuff out, your have to guess and hunt around to figure out which of the bajillion plugins is responsible, then you have to guess where the site for that plugin is generated (or try a Google search and hunt around), and then guess where the source code is in the repository (since the documentation is usually fucking wrong and entirely missing). Fucking project comprehension my ass.

The Maven crew has always been about slopping out undocumented, broken shit, rather than actually producing useful tools. I can't wait to get off of it forever.

End rant. I still like the concept of the repository and the transitive dependencies; that aspect of Maven is worthwhile, but as a build tool, it sucks up far more time and energy than it saves. Possibly an order of magnitude more.

Wednesday, November 19, 2008

Ready for 5.0.17?

Looks like 5.0.16 will not be the final release, there will be a very modest 5.0.l7 that addresses a couple of annoyances that didn't have good work-arounds.

Sunday, November 16, 2008

Tapestry 5.0.16 (Release Candidate) it OUT!

The latest release of Tapestry, Tapestry 5.0.16 (Release Candidate), is now available.

Tapestry 5.0.16 is the release candidate; we encourage users to download this version. In about a month, the Tapestry PMC will run a vote to grant it release status, barring any blocker bugs (critical bugs with no workaround).

In the two months since the previous release, we've addressed over 80 issues, including many bugs and a few last minute improvements. New features include a LinkSubmit component (dearly missed from Tapestry 4), new support for reporting Ajax errors on the client side, smarter client-side validation, support for several new locales, and much new documentation.

Tapestry 5.0.16 is available for download, or via the central Maven repository.

Thursday, November 13, 2008

Clojure Baby Steps

Getting some baby steps going with Clojure. What threw me for a while is how difficult it was to get to do some basic output.

I wanted to see the JVM system properties. It's easy to get them, Java inter-operation is strong in Clojure, but in a readable format?

user=> (System/getProperties)
#=(java.util.Properties. {"java.runtime.name" "Java(TM) 2 Runtime Environment, Standard Edition", "sun.boot.library.path" "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Libraries", "java.vm.version" "1.5.0_16-133", "awt.nativeDoubleBuffering" "true", "gopherProxySet" "false", "java.vm.vendor" "Apple Inc.", "java.vendor.url" "http://www.apple.com/", "path.separator" ":", "java.vm.name" "Java HotSpot(TM) Client VM", "file.encoding.pkg" "sun.io", "sun.java.launcher" "SUN_STANDARD", "user.country" "US", "sun.os.patch.level" "unknown", "java.vm.specification.name" "Java Virtual Machine Specification", "user.dir" "/Users/Howard", "java.runtime.version" "1.5.0_16-b06-284", "java.awt.graphicsenv" "apple.awt.CGraphicsEnvironment", "java.endorsed.dirs" "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/endorsed", "os.arch" "i386", "java.io.tmpdir" "/tmp", "line.separator" "\n", "java.vm.specification.vendor" "Sun Microsystems Inc.", "os.name" "Mac OS X", "sun.jnu.encoding" "MacRoman", "java.library.path" ".:/Users/Howard/Library/Java/Extensions:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java", "java.specification.name" "Java Platform API Specification", "java.class.version" "49.0", "sun.management.compiler" "HotSpot Client Compiler", "os.version" "10.5.5", "user.home" "/Users/Howard", "user.timezone" "", "java.awt.printerjob" "apple.awt.CPrinterJob", "file.encoding" "MacRoman", "java.specification.version" "1.5", "java.class.path" "/usr/local/clojure/clojure-contrib.jar:/usr/local/clojure/clojure-lang-1.0-SNAPSHOT.jar", "user.name" "Howard", "java.vm.specification.version" "1.0", "java.home" "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home", "sun.arch.data.model" "32", "user.language" "en", "java.specification.vendor" "Sun Microsystems Inc.", "awt.toolkit" "apple.awt.CToolkit", "java.vm.info" "mixed mode", "java.version" "1.5.0_16", "java.ext.dirs" "/Users/Howard/Library/Java/Extensions:/Library/Java/Extensions:/System/Library/Java/Extensions:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext", "sun.boot.class.path" "/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/classes.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/ui.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/laf.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/sunrsasign.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/jsse.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/jce.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/charsets.jar", "java.vendor" "Apple Inc.", "file.separator" "/", "java.vendor.url.bug" "http://bugreport.apple.com/", "sun.io.unicode.encoding" "UnicodeLittle", "sun.cpu.endian" "little", "mrj.version" "1050.1.5.0_16-284", "sun.cpu.isalist" ""})
user=> 

So I thought I'd print out the results, one item per line.

user=> (range 5)
(0 1 2 3 4)
user=> (for [x (range 5)] (println x))
(0
nil 1
nil 2
nil 3
nil 4
nil)
user=> 

Hm. This took a while to figure out. The trick is that for is a lazy operator, used for list comprehensions. That is, the sequence produced by for is lazy, you have to start navigating the sequence for it to fully execute. Thus, what we see above is two different streams of output, mixed together: A list: (nil nil nil nil nil) interspersed with the println calls. Here's the order of operations:

  • The Repl starts to print the sequence, starting with a "(" before the first atom
  • The first atom is evaluated, printing "0" and returning nil
  • The Repl prints the "nil"
  • The second atom is evaluated (lazily), printing "1" and returning nil
  • The Repl prints the second "nil"
  • And so on ...

That's a lesson ... in a lazily-evaluated world, even the most basics ideas have to be thrown out the window. You would never see this in Haskell, because it creates impenetrable barriers between functional, side-effect free code and any code that communicates with the outside world, even something as simple as println.

The solution? Collapse the list to a string and print that at the end:

user=> (use 'clojure.contrib.str-utils)
nil
user=> (println (str-join "\n" (range 5)))
0
1
2
3
4
nil
user=>

Baby steps. Sometimes the simplest things are tricky, but I suspect the more advanced things are easier.

Tapestry 5 Ajax Screencast

This is a follow on to my previous JSF comparison; Jim Discoll produced a Simple Ajax JSF example, and this screencast is the Tapestry 5 equivalent. I promise I'll stop now!

Wednesday, November 12, 2008

Getting started with Clojure

Clojure Logo Whenever I finish a major piece of work (Tapestry 5.0.16, in this case), I like to try learning some new things before I start the next major phase. I've got a pent-up stack of things to try and learn, but foremost of these is Clojure.

I've always been fascinated with Lisp, though I last used it in college, nearly 20 years ago. Still, even then, I considered it a kind of litmus test ... if you didn't "get" Lisp (and, especially, recursion) you really were limited in how far you could go in this industry.

In any case, Clojure is something new; it's a Lisp, meaning it is similar to Common Lisp ... but it is not a common Lisp, it's a new functional language designed for high concurrency but also full interoperation with the JVM. In other words, it's like Scala, a new language explicitly tied to the JVM, but it goes the opposite direction in terms of the type system.

What's attracting me to it, rather than Scala (or Haskell for that matter) is the syntax. Scala's type system has been pretty daunting for me, and its syntax does reflect that. Lisp has the simplest syntax you can imagine.

The fact that Clojure supports Erlang-ish agents as well as Software Transactional Memory means that it is built for very high concurrency applications ... but you can always escape down to the nitty gritty Java level.

In other words, I've been buying into the "less-is-more" philosophy of Clojure vs. the "more-is-more" philosophy of Scala. Further, it's combining nearly 50 years of Lisp concepts with the ubiquitous and fast JVM platform, and embracing relatively new ideas to support concurrency, with a target on the dozens or hundreds of cores typical machines are expected to have in just a few years.

I've started reading Stuart Halloway's book, Programming Clojure. Fun read so far, Stuart's style is very approachable.

However, Clojure is right on the bleeding edge, and getting an environment set up is still a bit of an exercise left up to the reader.

To keep things organized, I created a /usr/local/clojure directory (I'm on a Mac) to keep the various Clojure resources stable and centrally located.

First is Clojure itself. There are downloads available, but Stu's book is already ahead of the latest download, so you'll want to build it yourself.

svn co https://clojure.svn.sourceforge.net/svnroot/clojure/trunk clojure
It builds with Maven in a few seconds. It's relatively small too .. 461K, and that includes a private copy of ASM. Just build with mvn clean install and copy the JAR file to /usr/local/clojure/clojure.jar.

Next is the standard contrib library for Clojure; this is needed for many of the examples in Stu's book. I haven't found an easy download for this yet, so I downloaded the source and built it myself:

svn co https://clojure-contrib.svn.sourceforge.net/svnroot/clojure-contrib/trunk clojure-contrib 

This one has an Ant build. It's really just a JAR file of Clojure script files. I recommend building it yourself, but if you are in a rush, I've placed a copy of clojure-contrib.jar on my website for download. You can imagine that it will be quickly out of date.

Next up is Emacs. Clojure syntax is very close to Lisp syntax, and nothing edits Lisp syntax better than Emacs, which is itself written in its own Lisp dialect.

I haven't used Emacs in anger in years, and then it was a bit of a monster (a version written in PL/1!). In fact, its probably been long enough that I won't remember the wrong, broken key bindings of the PL/1 version. In any case, I'm on a Mac, so Emacs is built in ... but there's a better alternative, Aquamacs Emacs which is striving for a best-of-both worlds approach. If you are on windows, Cygwin (as usual) is the way to go, and there's a lot of discussion on the Clojure Programming Wiki Page as well.

Next up we need to set up a Clojure mode for Eclipse. This is available at http://clojure.codestuffs.com/. Download, unpack, and copy the files to /usr/local/clojure as well.

The last step is to modify Emacs to load the Clojure mode. The following can go into ~/.emacs for a traditional Emacs, or ~/Library/Preferences/Aquamacs Emacs/Preferences.el for AquaEamcs:


(setq inferior-lisp-program
                                        ; Path to java implementation
      (let* ((java-path "java")
                                        ; Extra command-line options
                                        ; to java.
             (java-options "-Xms100M -Xmx600M")
                                        ; Base directory to Clojure.
                                        ; Change this accordingly.
             (clojure-path "/usr/local/clojure/")
                                        ; The character between
                                        ; elements of your classpath.
             (class-path-delimiter ":")
             (class-path (mapconcat (lambda (s) s)
            ; Include all *.jar files in the clojure-path directory
        (file-expand-wildcards (concat clojure-path "*.jar"))
                                    class-path-delimiter)))
        (concat java-path
                " " java-options
                " -cp " class-path
                " clojure.lang.Repl")))

;; Require clojure-mode to load and associate it to all .clj files.

(add-to-list 'load-path "/usr/local/clojure")

(require 'clojure-mode)

(setq auto-mode-alist
      (cons '("\\.clj$" . clojure-mode)
            auto-mode-alist))

Of course, you'll need to modify this to reflect the proper path you've been placing all these files.

Note: Stu's book mentions adding the sources from the book to the classpath as well; you can see in the above code where a list of classpath entries is being assembled, and you could add "/path/to/book/source" into the list.

When you next restart Emacs, you'll want to find a Clojure file to activate Clojure Mode ... any file with a .clj extension will activate Clojure Mode. Then C-c C-z will launch Clojure.

Lots to learn, lots to learn.

Monday, November 10, 2008

Tapestry 5.0.16 (Release Candidate) is under vote

Tapestry 5.0.16 is being voted upon for release as the RC (Release Candidate). The vote will run for a few days, then we'll make it publically available from Apache (and Maven). A couple of weeks from now, we can vote it as the GA release (assuming nothing horrible announces itself in the meantime).

Sunday, November 02, 2008

"Simple" JSF 2.0 Component vs. Tapestry

I just saw an amusing example of JSF 2.0 on Jim Driscoll's blog. He's creating a simple output-only component to display some text in yellow, using an inline CSS style. To make things 'simpler' (my quotes) he's using JSF.

Here's his JSF page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ez="http://java.sun.com/jsf/composite/simpleout">
<h:head>
    <title>Yellow Text Example</title>
</h:head>
<h:body>
        <h1>Editable Text Example</h1>
        <h:form id="form1">
                <ez:out value="Test Value"/>
            <p><h:commandButton value="reload"/></p>
            <h:messages/>
        </h:form>
</h:body>
</html>

Not too bad ... the relevant part is <ez:out value="Test Value">, that's the reference to his Out component inside his simpleout library. JSF puts a lot of interpretation into the namespace URLs.

On the other hand, the implementation of this simple component seems a bit out of hand:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:composite="http://java.sun.com/jsf/composite">
<head>
<title>This will not be present in rendered output</title>
</head>
<body>

<composite:interface name="yellowOut"
                     displayName="Very Basic Output Component"
                     preferred="true"
                     expert="false"
                     shortDescription="A basic example">
    <composite:attribute name="value" required="false"/>
</composite:interface>

<composite:implementation>
    <h:outputText value="#{compositeComponent.attrs.value}" style="background-color: yellow"/>
</composite:implementation>
</body>
</html>

You can kind of vaguely see, inside all that clutter, what the component is doing. I'm personally troubled by name="yellowOut" ... is the component's name "out" or "yellowOut"? Also, compared to the Tapestry version coming up, you can see that a few nods to visual builder tools are included.

So, what does the Tapestry equivalent look like?

Well, our page template would look something like*:

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<head>
  <title>Yellow Text Example (Tapestry)</title>
</head>
<body>
  <h1>Yellow Text Example</h1>
  <t:form t:id="form1">
    <t:out value="Test Value"/>
   <p><input type="submit" value="reload"/></p>
  </t:form>
</body>
</html>

The Tapestry namespace identifies elements and attributes that are not truly HTML, but controlled by Tapestry. This includes built-in Tapestry components such as Form as well as user-defined components such as Out.

The Out component itself is in two parts: a Java class to define the parameters (and, in a real world example, provide other logic and properties) and a matching template to perform output**. First the class:

package org.example.myapp.components;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.annotations.*;

public class Out
{
  @Property
  @Parameter(defaultPrefix = BindingConstants.LITERAL)
  private String value;
}

The @Parameter annotation on the value field marks the field as a parameter. The defaultPrefix attribute is configured to indicate that, by default, the binding expression (the value attribute inside the page template) should be interpreted as a literal string (as opposed to the normal default, a property expression).

The @Property annotation directs Tapestry to provide a getter and setter method for the field, so that it can be accessed from the template.

<span style="background-color: yellow">${value}</span>

Again, this is a trivial example. We didn't even have to define the Tapestry namespace for this template. The ${value} is an expansion which outputs the value property of the Out component instance, which itself is the literal string ("Test Value") bound to the Out component's value parameter.

Let's add a little improvement: If the value is null or empty, don't even render the <span> tag. This is accomplished in one of two ways.

First, we can change the template slightly, to turn the <span> tag into an If component that renders a span tag:

<span t:type="if" t:test="value" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" style="background-color: yellow;">${value}</span>

The If component evaluates its test parameter and if true, renders its tag (the <span> in this case), its non-Tapestry attributes (style) and its body (${value}).

Alternately, we can leave the template alone and abort the component's render in the Java class:

  boolean beforeRenderTemplate()
  {
    return ! (value == null || value.equals(""));  
  }

This is a Tapestry render phase method, a kind of optional callback method that can control how the component is rendered. We return true to render normally, and false to skip the template rendering. Tapestry invokes this method at the right time based on a naming convention, though an annotation can be used if that is more to your taste.

This is a good example of how Tapestry really cuts to the chase relative to other frameworks. Both the usage of the component and its definition are simple, concise and readable. Further, Tapestry components can scale up nicely, adding sub-components in the template, and more properties and logic in the Java class. Finally, we can also see a hint of how Tapestry relieves you of a lot of low-level drudgery, such as using the @Property annotation rather than writing those methods by hand (Tapestry has a wide ranging set of such meta-programming tools built in).

* I'm not sure why the original version used a form, but I've dutifully recreated that here.

** For a component this simple I'd normally not bother with a template and do it all in code.