by Bear Bibeault
More and more as of late in the JavaRanch JSP Forum, I've been encouraging people to embrace writing script-less JSP pages; something much easier to do under JSP 2.0 than ever before.
Many have come to a similar conclusion as myself, that this is A Good Thing™; others are, as yet, unconvinced. And still others would like to believe, but get frustrated when they find themselves stumped by something they can't figure out how to do using the JSTL and the EL.
This article is aimed primarily at this latter audience. I'm not going to say I've given up on trying to convince the former segment, but I don't think that anything I could write in a journal-sized entry would do much in the way of convincing.
Most of the frustrations I field seem to stem from some "wrong thinking" about JSP 2.0, the JSTL and the EL. This "wrong thinking" can be summarized by:
"Given the data structures available to the page, I can't get the JSTL and EL to do such-and-such."
The "correct thinking" should be:
"How should the servlet controller1 set things up so that I can easily do what I want on the page?"
This is a different way of thinking about things than back in the Ice Ages when we put Java in the JSPs willy-nilly. Back then, even when we used servlet controllers, they were usually relegated to database access and not too much attention was paid to the formats of the data that got sent to the page. After all, we could use the full power of Java to access the data in any way that we wanted.
Then came the JSTL and, in particular, the EL, and things have changed. Not radically mind you, but enough to make us look at the controller/view interface in a slightly different manner; what I call a "30-degree shift" in thinking.
The EL is deft at handling certain types of data, but not others. It's great with Java Beans and collections, but not so good with plain-old unpatterned classes.
It behooves us, therefore, to put the "smart thinking" in the servlet controller where we have maxium flexibility, and to make sure that we pattern the data that we are going to send to the page in such a way that the page can be as simple as possible.
Let's look at one concrete example. In a JSP Forum topic post, one member complained that he wanted to list out all the application-level scoped variables and their values, and claimed that the JSTL was useless because it could not handle this task easily. Wrong thinking.
Wrong, because he was thinking of how to make the page conform to the data, rather than the other way around.
The problem stems from the non-EL-friendly pattern exposed by the ServletContext (and repeated by the other context scopes) for obtaining this data.
In Java code, showing these values might look something like:
Enumeration e = servletContext.getAttributeNames(); while (e.hasMoreElements()) { String name = (String)e.nextElement(); Object value = servletContext.getAttribute( name ); System.out.println( name + "=" + value ); }
In a scriptless JSP this is a problem because even though we can obtain the names of the scoped variables as an EL-friendly enumeration (using ${pageContext.servletContext.attributeNames}), you must make a call to a method that takes the name as a parameter in order to obtain the value. And that is something that the EL cannot do.2
So, if it is a requirement that the page show this information, "correct thinking" is to make it the controller's responsibility to set up the data in an EL-friendly manner.
The set of name/value pairs of the scoped variable to be displayed is a natural fit to a Map, so in the controller I would collect and expose the data as such:
Map attributeMap = new HashMap(); Enumeration e = servletContext.getAttributeNames(); while (e.hasMoreElements()) { String name = (String)e.nextElement(); attributeMap.put( name, servletContext.getAttribute( name ) ); } request.setAttribute( "applicationVariableMap", attributeMap );
And in the JSP page, rendering this data is incredibly simple, easily readable, and easily maintained as:
<c:forEach items="${applicationVariableMap}" var="entry"> ${entry.key} = ${entry.value}<br/> </c:forEach>
With about half a dozen lines of code in the controller, we have made this seeming on-page headache just go away.
To summarize: "Ask not what your page can do for you, but what your controller can do for your page."
1 If you are not using a Model 2 pattern with servlet controllers, you are (in my opinion) doing it wrong.
2 Someone is sure to point out that with JSTL 1.1 it is possible to define functions that can be accessed from the page. To me, that is an action of last resort, and would involve more, and certainly less straight-forward, code than what I am proposing here.