Skip to main content

Hudson 1.40 and why I eventually said bye bye to JSP in favor of Jelly

Posted by kohsuke on August 8, 2006 at 6:05 PM PDT

I just posted Hudson 1.40. This includes one of the biggest changes I made in Hudson, namely to ditch JSP/JSTL in favor of Jelly.

I've never been truly happy with JSP (and consequently any technologies built on top of it, including JSF and things like that.) It felt very anti object-oriented, because in JSP and their siblings, pages are always the king and the data is the servant. You write, say, /showProduct.jsp, then give the data as the parameter, maybe like /showProduct.jsp?id=513. This always remind me of writing C code, where you'd say printProduct(513) where the parameter is int used as the product ID. I thought we are all supposed to program in the Java way, that is product.print() where 'product' is the object that perhaps gives you product.getId()==513.

You might think it's just a matter of ordering between the method/JSP name and data, but it's far more significant. The first reason why it matters is because the Java way allows you to overload method/JSPs. Even in the web app it's pretty convenient to overload views --- you'd want to show a different page for displaying book details and CD details. So depending on the kind of product 513, you'd want to use different JSPs. The 2nd reason why this matters is because it hides the identity of the object. In the Java way, I don't need to know that the product is actually identified by a single integer, while C way forces me to expose it. In reality, you do it a little better in C by using a structure and a pointer, but in JSP you'd have to pass in all the "primary keys" explicitly.

It gets even worse in JSP. Once you get a "primary key", you still have to get to the corresponding data from JSP, and when you have multiple JSPs that work on the same data (perhaps you have createProduct.jsp and checkProductInventory.jsp or something), then the said logic needs to be on all such JSPs. In Java, coupling between methods and instance fields make it very easy for methods to access data.

So when I wrote Hudson, I wrote a little library called stapler so that I can use JSPs a lot more like methods. That has allowed me to use intuitive URLs like http://myhost/job/jaxb-ri/513 (which means the JAXB RI project build #513), and minimize the amount of boiler-plate code (I believe this kind of stuff has been done by web frameworks in other languages, like Zope/Python.) JSP is designed well enough to be amenable for this kind of use, and kudos to the designers of JSP for that.

The life has been good for about a year, but lately I started to get annoyed by problems. The first one is that containers are often very slow to reload JSP pages. It seems to be particularly bad if you have a lot of tag files, and I guess stapler's creative/abusive use of JSPs wasn't helping the reloading performance either. It was apparently because every JSP page and every tag file is compiled into its own class, by using javac. Even when javac is run within the container process (and some don't even do that), it's still painful 3 to 5 seconds. When you are just making color changes or something like that, such a delay just turns you off.

Then there's another problem of pluggability. One of the thing with a continuous integration system like Hudson is that you need to be able to support plugins. Because people use all sorts of different build tools, supporting everything in one monolithic application is simply not practical. A better approach is to let people write plugins, like perhaps one plugin to show checkstyle reports, another plugin to show Japex trend reports , or perhaps plugins to talk to some in-house systems (for me, that would be Sun's bugtracking system.)

When you think about what it takes to support plugins in web applications, today's web tier technology is quite immature. For example, how do you let plugins to render HTML pages? How do you pull in JSP pages from plugin jar files? taglibs? How do you isolate plugins so that their dependency libraries, taglibs, and view pages don't collide?

So I've been thinking about throwing away JSP for a while, and last Friday I must have written one too many JSP pages. I felt enough was enough, and now I switched to Jelly. It took me a full day to convert existing JSP pages to Jelly scripts. Fortunately, with mechanical search&replace, it wasn't too hard nor error prone. Some 100 JSP pages and 30 or so tag files were all converted to Jelly scripts, and that's what's in Hudson 1.40.

The page reloading got a lot faster, although probably rendering speed is slower. I don't think my users would mind that at all, because it's not like we are talking about 10 hits per second, and most latency is network origin anyway. A nice side-effect was that JEXL that replaced EL (Jelly uses JEXL) is more powerful, and I no longer have to define a stupid one-line JSP function and write all taglib.tld just so that I can invoke Set.contains.

Supporting pulgins is still going to take some work, but so far I'm happier. About the only thing I miss is the good tool support I used to have with JSP. You know, things like auto-completion of EL expressions, tag attributes, jumping to the tag definition, etc. Since I'm an XML guy at my day job, I think maybe I should write a RELAX NG schema generator for Jelly tags and validate Jelly scripts as a part of the build process.

If you have similar problems like I do, you might be interested in using stapler!

Related Topics >>