Okay, I know I haven’t posted anything on my blog in over a year, but this falls under the OMG-Why-Couldn’t-I-find-a-straightforward-answer category. Whomever suffers the same headache I suffered will hopefully get led here by Google…
The Problem: Running the most basic JSP Example
Up until now, the few times I’ve needed to cobble together a JSP file for some sort of front-end functionality, I’ve used raw, low-level JSP Scriptlets. Which means typing things like
<% if (something) { %> <some-html> <% } else { %> <some-html> <% } %>
all over the place. And if I wanted to do any looping, well forget about it! It’s a nightmare. So I’ve got these few books and articles that talk about the better ways to solve these problems using cleaner xml-y solutions, and most all of them dive into using JSTL (JSP Standard Tag Library) which is a damned standard and yet isn’t included with Tomcat. It’s one of those things that each vendor is supposed to implement independently, and yet the only implementation out there appears to be Oracle’s Glassfish implementation! (There’s an Apache JSTL project, and they say on their web page that a version 1.2 implementation (which is the stated version for the Java 6 EE standard collection, alongside Servlet 3.0 and JSP 2.2) but that webpage hasn’t been updated since October 2009!!
So apparently JSTL is so basic and simple that it’s included in the elementary pages of any JSP books, but like some bastard stepchild that nobody wants, it’s support is freakishly missing. Okay, enough bellyaching about how FUBAR that is… what about just getting the thing to run?
Here are some watchas that I ran into…
- It doesn’t work… until it does. Until I had the right library dependencies working and the right namespace down, I didn’t get any error messages or diagnostics. I just found that my sample <c:forEach> tags would get merely copied into the resulting HTML.
- The JSP namespace changed. Some early books showed examples where the JSP namespace was written out as
xmlns:c="http://java.sun.com/jstl/core"when in fact it later got renamed toxmlns:c="http://java.sun.com/jsp/jstl/core"! Again, no error messages to help me out here. It just didn’t work until it did. - Dependency hell, JSTL 2.1 can include the wrong JSP API versions. More on this in the next section.
The good news is you should just be able to grab the Oracle Glassfish implementation JAR, toss that into Tomcat’s lib directory, and stop worrying. But if you use Maven for your dependency management, then life gets crazy again.
JSTL and Maven Dependencies
For the longest time I avoided learning Maven because it just seemed like “just one more thing” and besides, Eclipse seemed to handle all the packaging, etc. just fine. But once I started playing around with XML and SOAP and Web Services and Dependency Injection and Spring and all that really cool stuff, I knew I had to bite the bullet. And I’m glad I did.
Normally if I want to start using some feature (like JSTL) I’ll just do a search in my Eclipse pom dependencies pane, add the dependency and start using the thing. (It’s fantastic!) But unfortunately, Maven dependencies seem to have gotten tripped up when it comes to JSTL 1.2, the bastard stepchild of a dependency. And especially for Tomcat (6 or 7 apparently) which is the “We’re the de-facto standard Servlet 3.0/JSP 2.2 container… except for the JSTL Bastard Stepchild!” server.
When I tried to do a simple dependency inclusion…
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
I got the API, but no runtime libraries. (As I said in the earlier section, this doesn’t result in a compile or runtime error. The JSTL tags simply don’t run and get passed through the outgoing HTML!) So then, after looking for an Apache implementation (I wanted to keep my implementation flavors consistent, and Apache wrote the Servlet 3.0/JSP 2.2 for Tomcat) I decided to include the Glassfish implementation…
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jstl-impl</artifactId>
<version>1.2</version>
</dependency>
and THEN I finally got a runtime exception. But this
javax.servlet.ServletException: java.lang.LinkageError: loader constraint violation: when resolving interface method "javax.servlet.jsp.JspApplicationContext.getExpressionFactory()Ljavax/el/ExpressionFactory;" the class loader (instance of org/apache/jasper/servlet/JasperLoader) of the current class, org/apache/jsp/index_jsp, and the class loader (instance of org/apache/catalina/loader/StandardClassLoader) for resolved class, javax/servlet/jsp/JspApplicationContext, have different Class objects for the type javax/el/ExpressionFactory used in the signature
At least this time I got an error message, strange as it was! (And all throughout this experience I didn’t find much of use on the Internet, which is why I’m writing this blog entry.) So at least I got the general idea that there was a revision/dependency problem between the classes. This is why I really hoped to find a JSTL library by Apache—because if you stick with one “flavor” you’re less likely to run into this.
I went into the Dependency Hierarchy tab of my Eclipse POM editor, and I could see that the Glassfish Maven module I’d included also decided to include the Servlet 2.5 and JSP 2.1 libraries. So I was causing a Servlet 3.0/2.5 and JSP 2.1/2.2 clash in my class loader. I clicked on those individual components, right-clicked, asked to remove the dependency, and ended up with this:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jstl-impl</artifactId>
<version>1.2</version>
<exclusions>
<exclusion>
<artifactId>servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
<exclusion>
<artifactId>jsp-api</artifactId>
<groupId>javax.servlet.jsp</groupId>
</exclusion>
<exclusion>
<artifactId>jstl-api</artifactId>
<groupId>javax.servlet.jsp.jstl</groupId>
</exclusion>
</exclusions>
</dependency>
Now to reclaim the hours it took me to get this far! I almost think I should have stuck with the old JSP-style scriptlets all over the place!