XSL / CSS Processing Instructions using ROME

Have you seen the way that the smart guys at FeedBurner display RSS feeds in a browser (here’s a sample if you haven’t)? If you’re like me, the first time you see a feed they manage, you’ll probably think that you’re viewing a page that contains a link to an RSS or ATOM feed, not the actual feed. In fact what you’re seeing is the feed transformed by your browser using XSL and CSS. Take a peek at the source and you’ll see that the XSL and CSS transformations are produced by what are technically called processing instructions. I won’t go into the work that they’ve done to create that look (but if you poke around the source it’s not trivial), but the inclusion of the processing instruction… now that’s something I can help you out with. I’ve used ROME on a couple different projects now because it is trivial to create RSS, RSS2 or ATOM feeds with only a couple lines of code. There are a number of tutorials up on the ROME site that show you how to create a feed and then write to a String, a File or a Writer which you should check out if you don’t have any experience with ROME. Assuming that you do however and given that you have a feed (technically a SyndFeed in ROME), you would write it out in a servlet like this:

SyndFeedOutput output = new SyndFeedOutput();
output.output(feed, response.getWriter());

ROME abstracts you from having to work directly with an XML document, which is handy most of the time. But if you want to add a processing instruction to style your feed, it takes a little fiddling with the source. If you peek at the source of SyndFeedOutput, you’ll see that it wraps WireFeedOutput. WireFeedOutput uses a JDOM Document and the XMLOutputter class to create write the feed. Conveniently, the Document class has methods for adding processing instructions, in fact it has a ProcessingInstruction class. Putting all these things together, you’d get this if you were creating the RSS feeds for FeedBurner using ROME:

WireFeedOutput feedOutput = new WireFeedOutput();
Document doc = feedOutput.outputJDom(feed.createWireFeed());
// create the XSL processing instruction
Map xsl = new HashMap();
xsl.put("href", "http://feeds.feedburner.com/~d/styles/rss2full.xsl");
xsl.put("type", "text/xsl");
xsl.put("media", "screen");
ProcessingInstruction pXsl = new ProcessingInstruction("xml-stylesheet", xsl);
doc.addContent(0, pXsl);
// create the CSS processing instruction
Map css = new HashMap();
css.put("href", "http://feeds.feedburner.com/~d/styles/itemcontent.css");
css.put("type", "text/css");
css.put("media", "screen");
ProcessingInstruction pCss = new ProcessingInstruction("xml-stylesheet", css);
doc.addContent(1, pCss);
// write the document to the servlet response
XMLOutputter outputter = new XMLOutputter(format);
outputter.output(doc,response.getWriter());

So now that I’ve made that easy for you, who’s going to show me the website that has a bunch of easy to use CSS and XSL templates for transforming RSS and ATOM feeds? Better yet, when are browsers going to have this kind of logic baked in so that my mom doesn’t have to look at an RSS feed that looks like a bunch of gobbly gook XML?

Hacking WebWork Result Types: Freemarker to HTML to JavaScript

I threw all of my past experience with Struts out the window when I started my new job because we use WebWork. WebWork is approximately one thousand times better though so I’m not complaining. One of the unique to WebWork features (as compared to Struts) is the notion of a result type which is the process responsible for combining the model with a some kind of template (usually JSP or FreeMarker) to create a view.

Anyway, for various reasons we’ve found it useful at work to output some pieces of content usually rendered as HTML instead as JavaScript via document.write(). Because it would be a nightmare to maintain two FreeMarker templates, one for HTML and one for JavaScript output, I figured out a way to transform a FreeMarker template that normally renders HTML to render JavaScript using a custom WebWork result type. Simply extend the FreemarkerResultdoExecute() method with code that will look extremely similar to the existing doExecute() method. I’ll highlight the differences by showing you two lines from the existing doExecute() method:

// Process the template
template.process(model, getWriter());

and then the updated version that transforms the HTML into JavaScript using the StringEscapeUtils class from the commons lang library:

// Process the template
StringWriter writer = new StringWriter();
template.process(model, writer);
getWriter().write("document.write('" + StringEscapeUtils.escapeJavaScript(writer.toString()) + "')");

The xwork config file changes a bit as well. Here’s both the HTML version and the JavaScript version:

<result name="html" type="freemarker">foo.ftl</result>
<result name="javascript" type="freemarkerhtml2javascript">
  <param name="location">foo.ftl</param>
  <param name="contentType">text/javascript</param>
</result>

The end result is that with a little logic added to your WebWork action, you can include the result of that action in your own site:

<ww:action name="viewSnippet" executeResult="true" />

and enable other people to use the same result via embedded JavaScript:

<script src="http://yoursite.com/yourSnippet.jspa?format=javascript"></script>

JSON: Making Content Syndication easier

At work we’ve been having some discussions about sharing content between two websites: the natural first option was an XML solution, in this case RSS. Site A would subscribe to the RSS feeds of the site B, periodically retrieving the updated feeds, caching the contents of each feed for a specified period of time all the while displaying the resulting content on various parts of site A.

A couple months ago (December 2005 to be exact), Yahoo started supporting JSON (a lightweight data interchange format which stands for JavaScript Object Notation), as optional result format for some of it’s web services. The most common thing said about JSON is that it’s better than XML, usually meaning that it’s easier to parse and not as verbose, here’s a well written comparison of XML and JSON if you don’t believe me. While the comparisons of simplicity, openness and interoperability are useful, I think JSON really stands out when you’re working in a browser. Going back to the example I used above where site A needs to display content from site B, as I see it, this a sample runtime / flow that bits travel through in order to make the syndication work:
every_n_seconds() --> retrieve_feed() --> store_feed_entries() and then per request to site A:
make_page() --> get_feed_entries() --> parse_entries() --> display_entries(). There are a number of libraries built in Java for creating and parsing RSS, some for fetching RSS and you there’s even a JSP taglib for displaying RSS. But even with all the libraries, there’s still a good amount of code to write and a number of moving parts you’ll need to maintain. If you do the syndication on the client side using JSON, there are no moving parts. To display just the title of each one of my del.icio.us posts as an example, you would end up with something like this:

<script type="text/javascript" src="http://del.icio.us/feeds/json/ajohnson1200"></script>
<script type="text/javascript">
for (var i=0, post; post = Delicious.posts[i]; i++) {
  document.write(post.d + '<br />');
}
</script>

I’m comparing apples to oranges (server side RSS retrieval, storage, parse and display against client side JSON include) but there are a couple of non obvious advantages and disadvantages:

  1. Caching: If used on a number of pages, syndicated JSON content can reduce the number of bits a browser has to download to fully render a page. For example, let’s say (for arguments sake) that we have an RSS feed that is 17k in size and a corresponding JSON feed of the same size (even though RSS would inevitably be bigger). Using the server side RSS syndication, the browser will have to download the rendered syndicated content (again let’s say it’s 17k). Using the JSON syndicated feed across a number of page views, the browser would download the 17k JSON feed once and then not again (assuming the server has been configured to send a 304) until the feed has a new item. Winner: JSON / client
  2. Rendering: Of course, having the browser parse and render a 17K JSON feed wouldn’t be trivial. From a pure speed standpoint, the server could do the parse / generate once and then used an HTML rendering of the feed from cache from then on. Winner: RSS / server
  3. Searching: Using JSON on the client, site A (which is syndicating content from site B), wouldn’t have any way of searching the content, outside of retrieving / parsing/ storing on the server. Also, spiders wouldn’t see the syndicated content from site B on site A unlike the server side RSS syndication where the syndicated content would look no different to a spider than the other content on site A. Winner: RSS / server
  4. Ubiquity: JSON ‘only’ works if the browser has JavaScript enabled, which I’m guessing the large majority of users do have JavaScript enabled. But certain environments won’t and phones, set top boxes and anything else that runs in a browser but not on a PC may not have JavaScript, which means they won’t see the syndicated content. Server side generated content will be available across any platform that understands HTML. Winner: RSS / server

So wrapping up, when should you use JSON on the client and when should you use RSS on the server? If you need to syndicate a small amount of content to non programmers who can cut and paste (or programmers who are adept at JavaScript), JSON seems like the way to go. It’s trivial to get something up and running, the browser will cache the feed you create and your users will see the new content as soon as it becomes available in your JSON feed.

If you’ve read this far, you should go on and check out the examples on developer.yahoo.com and on del.icio.us. Also, if you’re a Java developer, you should head on over to sourceforge.net to take a look at the JSON-lib, which makes it wicked easy to create JSON from lists, arrays and beans.

Links: 8-12-2006