Monday 21 April 2008

JSTL


Sans JSTL,
scriptlet and custom tag hell,
things much better now.


So what is JSTL? It's a standard custom tag library that can provide extremely useful functionality to your JSP pages. Common programming tasks like looping and flow control, XML processing, and database access can be implemented much more elegantly using these tags. Without them - in many cases anyway - you'd be reduced to using scriptlets, or writing your own custom tags.

There are four categories of custom tag in JSTL. First up are the core tags, these are the everyday tags that you'll use a lot, it's orthodox for these to have a namespace prefix of "c". These are the tags I'll be concentrating on here, as they are the only ones required for the SCWCD exam. There are however three others which are well worth knowing about. The XML tags, which usually have namespace prefix of "x", provide mechanisms for parsing xml. The formating tags, prefix "fmt", provide support for different locales, including tags to parse and format dates and numbers. Lastly the SQL tags, prefix "sql", provide - as the name suggests - database access.

So, enough with the distractions, and on with the core tags. These are the ones we need to know about for the exam, there are 14 in total:

<c:out>, <c:set>, <c:remove>, <c:if>, <c:choose><c:when>, <c:otherwise>, <c:catch>, <c:forEach><c:forTokens>, <c:url>, <c:import>, <c:redirect>, <c:param>

As you can probably guess from the names, these solve fairly fundamental, and common problems, ie looping, flow control, including other pages etc. Each tag can have a whole bunch of attributes, and can sometimes contain stuff in the tag body. So where to start?

Firstly there are some commonly used attributes. "var" is probably the most commonly used, and is used to provide the name of an attribute you want to store the result of whatever it us you've just evaluated. For example:

<c:catch var="cheeseError" >
<c:set var="favouriteCheese" value="gorgonzola" />
<c:if var="isFavouriteStilton" test="${favouriteCheese == 'stilton'}" />
<c:import var="allTheCheeses" url="http://en.wikipedia.org/wiki/List_of_British_cheeses" />
</c:catch>

Where you see "var" you also often see "scope". This is used, as you might guess to specify the scope of the attribute, any one of the usual four values are allowed, so you could have scope="page", scope="request", scope="session", scope="context". The two iteration tags, <c:forEach> <forTokens>, and also <c:catch> have a "var" attribute, but no "scope" attribute.

"test" appears in the flow control tags <c:if> and <c:when>

"value" appears in any of the tags that involve setting a value of some kind. <c:set>, <c:param> and <c:url>. If this attribute is omitted, the value can be placed in the body of the tag.

So what do they all do?
This should be mostly obvious from their names, but here's a more detailed run down.

<c:out>
This very useful tag can be used to output a value, and has some distinct advantages over embedding EL or an expression I here you ask? Well, expressions and EL don't have default values, and expressions and EL don't automagically escape any nefarious xml tagging for you!

<c:out> has three attributes, "value" which is mandatory, and default, and escapeXml which are optional. You can use "default" to set a default value that gets output when value resolves to null. "escapeXml" can have a value of either true or false. If it's set to true <, > &, ', and " get changed to matching entity references. If escapeXml is omitted, you get esapeXml="true" implicitly.

<c:set>
<c:remove>
Guess what, you can use these for setting and removing attributes. <c:set> has the "value", "var", and "scope" like you might expect. But it can also work on bean properties, in which case you can use "target" and "property" to name the bean and it's property you want to set.

<c:remove> does the what you'd expect, although doesn't have any bean manipulation powers. Has a mandatory "var" attribute, and an optional "scope" attribute.

<c:if>
If an expression - declared using the "test" attribute - resolves to true, process the body of the tag. If it resolves to false, skip the tag body! All very logical and self explanatory. But wait, there's more! You can store the boolean result of the test expression in a variable for use later. The <c:if> doesn't even need to have a body. Use the usual "var" and "scope tags to do this.

<c:choose>
<c:when>
<c:otherwise>
You can consider these a bit like a Java switch statement, or an if - else if - else chain.
<c:choose> has no attributes, and is use to mark the begining and end of the conditional operation. It must have at least one <c:when> and at most one &lt:c:otherwise> tags.
<c:when> has just one attribute "test", which as one may guess determines whether the body of the when is executed. You can have multiple <c:when>s. You can only have one <c:otherwise> which has no attributes, and acts like as a default, should all the corresponding <c:when>s fail. Have an example:

<c:choose>
<c:when test="${someValue == someOtherValue}">
Some JSP that might or might not get processed.
</c:when>
<c:when test="${someValue == aDifferentValue}">
Some other JSP that might or might not get processed.
</c:when>
<c:otherwise>
None of the c:whens fired, now you have a default value!
</c:otherwise>
</c:choose>

<c:forEach>
<c:forTokens>
These as you may guess are used for iteration, and are very similar.
Both have "items", "begin", "end", "step", "var", and "varStatus".

"begin" specifies index to begin with, "end" specifies the index to end on, and "step" is step you want to take on each iteration. None of these are mandatory.

"var" is the name of an attribute to hold the object retrieved at the current step of the iteration, and "varStatus" is the name of an attribute to hold a javax.servlet.jsp.jstl.core.LoopTagStatus (this provides access to information about the current iteration you are processing). Neither of these are mandatory either.

So what can you iterate over? Well pretty much anything, and even nothing. You specify the object you want to iterate over using the "items" attribute, this could be a java Array, a Collection, an Enumeration, a String. It can also be left out, in which case the tag iterates over nothing, but assuming you've supplied a "begin", "end", and/or "step" attributes it can be used to repeat an action a specific number of times.

You can even iterate over String. If you're using <c:forEach>, the string is assumed to be a comma separated list of substrings. If you want to use a different delimiter you can use <c:forTokens> which can only operate on String objects, and has an extra attribute - "delim"- which can be used to, surprise, surprise, specify a particular delimiter.

<c:url>
This is used to generate a url, and either output it, or store it in a variable. It has the usual "var" and "scope" attributes to store the url if you want. It can take a "context" attribute which you can use to specify a local url to a foreign context, and finally a "value" attribute, which contains the url. This tag has one useful property in that it can automatically add the "jsessionid=" parameter if url rewriting is being used for session logging.

<c:import>
This tag can be used to import the contents of another page into the current page. It's a but like <jsp:include> except (as usual) has several advantages.

Advantage one: it doesn't have to be a locally accessible resource, it could be from any where on the intertubes. Advantage two: you can store the contents in an attribute for processing later!

This tag has the usual attributes you'd expect. "url" is the only mandatory one, and I shouldn't need to tell you what you put in it! There are the usual "var" and "scope" attributes. It also has "context" which works like the "context" attribute in the <c:url> tag. "charEncoding", is used to specify the character encoding of the import source, and finally "varReader" which you can use to specify the name of a reader object that you can use to process the resource. Access to this reader is scoped to only be available in the body of the tag.

<c:redirect>
Does a simple redirect. Only has "url" and "context" attributes, which do what you'd expect.

<c:param>
Finally <c:param> this tag can be placed in the bodies of <c:url>, <c:import>, and <c:redirect> to specify extra request parameters. It has a mandatory "name" attribute, and an optional "value" attribtue, both do what you'd expect.


That's it for the JSTL core tags. Hope it was useful. As usual there's plenty more info on Sun's site: http://java.sun.com/products/jsp/jstl/1.1/docs/tlddocs/index.html