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!

Wednesday, February 14, 2007

Direct Component Rendering in T5

One question that pops up in the T4 mailing lists quite often is "How do I render a PDF?" (or a chart, or an Excel spreadsheet, etc.).

In T4, this takes a bit of care and planning: a new engine service to represent the resource to be generated, perhaps a new Asset implementation that references the service. The necessary HiveMind configuration to add the engine service to the mix, and perhaps give it a friendly URL mapping. Perhaps a touch of web.xml for that last bit as well.

In Tapestry 5, there's an interface, StreamResponse, that may be used as the return value from an component event handler method. StreamResponse encapsulates a MIME content type, and an InputStream that will be read and pumped down to the client.

And that's really all it takes. Here's an example from the test suite:

public class Start
    Object onActionFromTextStreamResponse()
        String text = "<html><body>Success!</body></html>";

        return new TextStreamResponse("text/html", text);

This method is invoked when the corresponding component's link is clicked; from the template:

<a t:type="ActionLink" t:id="textStreamResponse">Text Stream Response</a>

This is just an event handler method; normally these return a page name or a page instance, and a client redirect is sent to get that page to render. By returning a StreamResponse, it is the stream response that is returned to the client (immediately, in the same request; there's no redirect).

That's the point of T5: Simple things should be easy.


Massimo said...

Cool, now i can discard my 10 lines of code which has done the same thing till todays commit ;)

Howard, thanks!

Anonymous said...

Haven't looked at the T5 code, but this TextStreamResponse class should allow specifying desired HTML headers, right?
Anyway, making custom services has always been one of those features that could scare newbie Tapestry developer, so I'm quite glad that these "hard" things became simple now.
One other "hard" thing that T5 would be great to have prime support for is "persistence contexts" for domain objects, and easy way to integrate with state-persistence libraries (Hib, JPA..). It is extremely hard to design these things right even for good developers. I think it would be wise to glance at Seam before doing anything in T5, because it is probably the strongest framework in that area.

Hugo Palma said...

This is great stuff.
Is T5 going to provide more implementations of StreamResponse besides the text one ? I think at least an UrlStreamResponse should be provided by the framework, i'm sure a lot of people will use that. At least i will :o)

Drew said...

This could be the thing that pushes me over the edge to T5. I've been reluctant for a number of reasons (which I've expressed on TSS) but my major Tapestry client site delivers a lot of PDFs and other stuff for download. This will make things a lot easier, especially when explaning to other developers.

Nice work.

Daniel said...

I agree with the comment about Seam. Tapestry is an excellent framework, but managing server-state and persistent contexts is the one thing that Seam does extremely well. It would be really nice to have state management with other scopes besides session and the necessary framework for specifying the scope boundaries, etc.. The pieces are there in tapestry with it's ASO concept, but it doesn't do it out-of-the box, and requires mucking around with custom hivemind stuff.

Howard said...

Mkaing properties or ASOs store their state in a manner similar to Seam's conversational state is not a huge effort; it's something that will "slip" into place naturally using Tapestry IOC configuration, just as "session" and "flash" persistence slip into place today.