Why JSTL Continues to Rock

So I just spent the last 30 minutes putting an RSS feed on the MobKnowledge site. It took me a bit longer than usual because I decided to grab the latest version of the JSTL, upgrade my Java version on the server, and then screwed around for a while trying to get the date on the feed, but didn't succeed, so I said screw it and posted what I had. You can find the new feed here: MobKnowledge RSS.

JSTL is still so great for this sort of quick and dirty hacks on the server. If you haven't familiarized yourself with it, you should because it's so damn useful. Here's the steps I took to get the RSS feed up on OrionServer.

First I upgraded Java to a JDK 1.4.2+ version. The reason for this is that the JSTL only needs two jars (jstl.jar and standard.jar) if you've got the latest version of the SDK, otherwise you need a bunch of XML stuff and the extended JDBC jars, and these can sometimes conflict with the container or in my case, with the Jive Knowledge Base web app which may be using other libraries. It's easier just to get the newest JDK.

Once that was done, I copied the two JSTL jar files into the WEB-INF/lib directory. Normally you'd need to add all the .tld's and modify your web.xml file, but there's some magic going on inside the standard.jar file that makes it work without that effort. The .tld descriptors are inside the META-INF, and somehow the web app knows this. I'm going to have to learn how that's done at some point.

Okay, so that's the server setup. To get to the data, I edited the data-sources.xml file to add another Pooled data source to give me access to MySQL. I could've just hammered the DB from the rss.jsp page but I'm relying on Orion to do some of the hard pooling stuff for me. The datasource snippet looks like this:

<data-source

        name="JiveKbDb"
        location="jdbc/JiveKbDb"
        class="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"
        max-connections="50"
        min-connections="2"
        inactivity-timeout="30"
        wait-timeout="30"
        username="username"
        password="password"
        url="jdbc:mysql://localhost:3306/mobknowledge?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8"       
>
      <property name="autoReconnect" value="true"/>    

</data-source>

Then I copied another version of the rss.jsp file that I already had around so that I had the base of RSS without having to go through the work of looking up the spec again. I explored the Jive KB SQL for a bit and figured out the best query, then popped it in the top, changed some of the references below and posted it. And of course it didn't work the first time. So I went back and checked my libraries and messed with things a bit to get it to work (adding in the .tld's though I shouldn't have had to), screwing up the server in the process causing me to have to reboot like 10 times. But in the end it just worked. I know that sounds nuts, but the idea is that once the libraries are installed correctly, the page is dead-simple to put together. Here's what it looks like:

<% out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); %>
<%@ page contentType="text/xml;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib uri="http://jakarta.apache.org/taglibs/datetime-1.0" prefix="dt" %>

<fmt:setTimeZone value="GMT-9" var="tz"/>

<sql:setDataSource dataSource="jdbc/BlogDb" />

<sql:query var="entries" maxRows="30">
        select jiveKbEntry.*, jiveKbPart.body, jiveUser.name as author
        from jiveKbEntry, jiveKbEntryPart, jiveKbPart, jiveUser
        where jiveKbEntry.entryId = jiveKbEntryPart.entryId
        and jiveKbEntryPart.partId = jiveKbPart.partId
        and jiveKbEntry.userID = jiveUser.userID
        order by creationDate desc
</sql:query>

<rss version="2.0">

<channel>
        <title>MobKnowledge</title>
        <link>http://www.mobknowledge.com</link>
        <description>Open Mobile Knowledge Base</description>
        <language>en</language>
        <managingEditor>russ@russellbeattie.com</managingEditor>
        <webMaster>russ@russellbeattie.com</webMaster>
<fmt:parseDate pattern="yyyy-MM-dd HH:mm:ss" var="lastPublished"><dt:format pattern="yyyy-MM-dd HH:mm:ss"><c:out value="${entries.rows[0].creationDate}"/></dt:format></fmt:parseDate>
        <lastBuildDate><c:catch><fmt:formatDate value="${lastPublished}" pattern="EE, dd MMM yyyy HH:mm:ss Z"/></c:catch></lastBuildDate>

        <image>
                <url>http://www.mobknowledge.com/images/mobknowledge.gif</url>
                <title>MobKnowledge</title>
                <link>http://www.mobknowledge.com</link>
                <width>300</width>
                <height>60</height>
        </image>

<c:forEach var="row" items="${entries.rows}">
<fmt:parseDate pattern="yyyy-MM-dd HH:mm:ss" var="created"><dt:format pattern="yyyy-MM-dd HH:mm:ss"><c:out value="${row.creationDate}"/></dt:format></fmt:parseDate>
        <item>
                <title><c:out value="${row.title}" escapeXml="true"/></title>
                <link>http://www.mobknowledge.com/entry.jspa?entryID=<c:out value="${row.externalID}"/></link>
                <description><c:out value="${row.body}" escapeXml="true"/></description>
                <author><c:out value="${row.author}"/></author>
                <guid isPermalink="false"><c:out value="${row.externalID}"/></guid>
                <pubDate><c:catch><fmt:formatDate value="${created}" pattern="EE, dd MMM yyyy HH:mm:ss Z"/></c:catch></pubDate>
        </item>
</c:forEach>

</channel>
</rss>

Now, all isn't perfect in JSTL world. Jive stores its dates in milliseconds like the Date.getTime() method, and there was no built in functionality to parse that into a date for me. The options are to break out into Java code and parse the result, write a custom Tag (which I could do instantly if I was using JSP 2.0), or just ignore it and write out the RSS without the date. I chose the last option because I'm being lazy. Any thoughts on how to parse that date would be good for next time.

The "right" way, by the way, to do this would've been to integrate with WebWork another script and all that. But JSTL is just such a low-hanging fruit that I went with it. There are sooo many times when having an action in front of your query is just overkill.

Hmmm. So if you add this write up to the dev time, I just spent an hour messing with that site. Wasn't the idea that I wasn't going to do that? ;-)

-Russ

Update: I had a brain-tumor or something. I *completely* forgot about Jakarta's tag libraries the other night. I have no idea why I wasn't looking outside the JSTL standard libraries. I *knew* I could do it, but was like "that's strange... I don't see it here." What's happening to my brain?!?! Anyways, the DateTime Taglib has exactly what I needed and I just implemented it in 30 seconds. Duh. Lots of other great tags in that collection as well for String manipulation, etc. All the minor stuff you need to do while creating the presentation layer for your app.

-R

< Previous         Next >