No, I'm not talking about those things full of water running down the mountainside, I'm talking about how you assemble a full HTML (or whatever) page from a webapp. Being a Trac user, I've done a bit with Genshi, and then with Grails used SiteMesh, both of which are stream-based engines. I'd always though the paradigm was a bit odd, and kind of inside-out, but today I realized they're exactly the right way to do things.
The basic idea is that your app generates the page body, as a full HTML document, and then it gets passed down the stream and manipulated. Maybe it gets some LINK and SCRIPT tags added to the HEAD, or a footer to the end of the BODY. Contrast this with the "content variable" assembly process, where your app generates a bodyContent variable which is handed to a layout to render into a full page.
What invariable ends up happening with the latter approach is a whole pile of variable along with bodyContent (headContent, pageTitle, footerContent, aboveFooterContent, belowFooterContent, etc.). A huge mess, especially if you're using Fusebox or something like it, where you typically put your HTML in a dsp_ file and include it into a content variable, because you end up with a massive number of one-line includes, most of which are all bound to rendering a single page (i.e. aren't reused).
Contrast that with SiteMesh or Genshi, where you can package all those separate pieces up in a single packet, and let them stream out of your app to the layout engine that takes care of everything. It might be combining your content with another app (think a portal), might be wraping it with header/nav/footer, might be restructuring it for an iPhone vs. a PC.
This doesn't come without a downside, of course. For example your navigation isn't controlled by your application anymore, so if you're addicted to XFAs (as I am), you're kind of stuck. For websites it's not as big a deal because you don't want your URL-space changing, but for applications it's a bit more troublesome. This is straightforward to deal with in the opposite direction, however. There are also some performance implications, though they're pretty trivial.
I'm trying to figure out a good way to integrate SiteMesh with CFML. CF is just a huge burden to try and drag into the JEE way of doing things, but Railo should be far simpler. I really don't want to use JSP for the layout engine, and while I like using Velocity, keeping to a single templating language is certainly beneficial. Whether I'll actually have time to implement something remains to be seen, but it sure would be nice.
I don't like the whole content variable thing either. Its one of those things I'd really like to "fix" in Fusebox. To date (not giving it a whole bunch of mind time share) I came up with only one idea that mildly better (in theory not practice). Registering a Page.cfc in the controller with fusebox. Then Layout.setBody() etc. It would clean up the API some and be an attempt to keep variables from floating. I guess it would also keep layout local to the app.
Why do you think CF is a burden to drag into JEE way of doing things? Aside from its size (behemoth) how does it differ from Railo?
Adam,
Layout.setBody() doesn't really change anything. You still end up with all the separate fields that have to be assembled down the road somewhere. And worse, you have to go declare the methods on the CFC before you can save something there. With a stream layout engine, you just have HTML documents all the way through.
CF in the JEE space is mostly complicated by it's adherence to the Servlet spec that JRun implements. So if you integrate on a newer container (we use Tomcat 6), you're in a bit of a bind, because the container does things according to the spec it's implemented against (which is backwards compatible with JRun's), but CF assumes things will always be done the JRun way. One example is flushing around includes. It used to be implicit, but now it's explicit. But CF ignores the explicitness because it assumes JRun's Servlet spec, regardless of whether it's on JRun or not. I haven't spent much time integrating Railo, but to this point, I've not seen any of this type of issues.
And CF's like 400MB. ;). Not that the ~20MB of Railo is tiny, but a trivial Spring/Hibernate app is going to be at least 10MB or so, so 20MB isn't really that bad.
So lets look at the stream for a second. How does a stream work? You build up an HTML page then at the end it is transformed into something else. That's the long and the short of it right? So really instead of putting things into content variables we are putting things into HTML tags. We still end at the same place: content we want styled placed into predefined specified areas. Really the stream is more limited if we follow using the stream and nothing else (like layouts), we are required to follow a top down procedural approach to assembly. For me I want something that is as liberating as possible. I want a way I can just throw data at the view and the view is smart enough to do something with it. Now I could certainly do with with a UI Stream but I am not liberated completely b/c I am still forced to put this together in a top down approach. I suppose I don;t have to but lets be honest if all I am going to do is reagrance all my specially IDed nodes in the HTML I might as well use content variables.
Now lets look at the layout concept some. Don't think about it in a setBody sense that's too restrictive. I look at it feature by feature of a page. So something like setNavigationDetail(Struct navTree) or setUserDeatils(User session.user). Then my layout can decide what it wants to do with that data. The beauty of this, to me, is the liberation this gives your application. Hell I could even do something like PageLayout.setBodyRender(ReaportLayout). Report Layout could be used in all my apps. Now my layout has 100% control over how my data is represented. I now have the ability to do something like: if(mode == "AJAX") Layout new AjaxResponse() else GeneralLayout(). In this instance my application can switch between a slick AJAX UI or page driven design with much less effort. Or possibly more powerful: if(browser.isMobile()) Layout new MobileLayout(browser.mobileVersion) else Layout new GeneralLayout(). I'm not saying you can not do with with UI streams but I do think this Layout approach would provide a cleaner more well defined API for someone to follow as well as allow them to produce the data in the best OO designed way without consideration to UI ordering.
Adam,
It's not about the fact that you're packaging your content into HTML instead of a structure, is that you're packaging your content into it's final form. And then something totally separate may or may not decide that the final form your app selected is really the final form or not.
Using an in-app layout engine is not mutually exclusive with a layout stream. At some point your app will end up emitting an HTML document. How it happens is one question, what happens to it before the browser sees it is another. Both phases ultimately control the client experience, but they really deal with different tasks.
A better way to think of it might be XSL post-processing to your pages. So your app builds an XML document (likely XHTML), and then you have cross-app filters that run XSL on it (maybe even client-side!) to manipulate what the app generates.