The Source for Java Technology Collaboration
User: Password:



Michael Nascimento Santos

Michael Nascimento Santos's Blog

Crazy JSTL: when an empty Collection is not empty

Posted by mister__m on November 27, 2003 at 08:32 PM | Comments (2)

Previously, I have promoted JSTL as an easier way to code the web tier. While I haven't changed my mind about it, I have just come accross one of its pitfalls yesterday, a few minutes after writing a blog entry about grid computing. Have you ever been in a situation code that looked impossible to be incorrect actually was incorrect? Well, it happens in many occasions, generally in the most unwanted ones - as it did yesterday, when it was around 3 am in Brazil.

Let's go straight to the point: for those of you who are familiar to JSTL, consider the following code:

<%-- Block 1 --%>
<%@ page import="java.util.Collection" %>
<%
    Object o = request.getAttribute("collection");
%>
<%= (o instanceof Collection) && ((Collection)o).isEmpty() %>

<%-- Block 2 --%>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

Assuming the first block prints true and that the Collection implementation used is flawless, what does the second block prints? true, right? Wrong! The right answer is: it depends.

Wait a minute: it depends? But how? Verbatim from the JSTL spec 1.0, section 3.8:

To evaluate empty A:
    If A is null, return true,
    Otherwise, if A is the empty string, then return true.
    Otherwise, if A is an empty array, then return true.
    Otherwise, if A is an empty Map, return true
    Otherwise, if A is an empty List, return true,
    Otherwise return false.

So, the JSTL 1.0 spec says nothing specifically about using the empty operator to evaluate Collections; it just supports the intended behaviour - the one we would expect - for Maps and Lists, and not all Collections. If you happen to have a Set implementation as your Collection implementation, for example, it won't work. The empty operator support in JSTL 1.0 is just for null, Strings, arrays, Lists and Maps, not for Sets. It's simply bad, too bad it works this way.

A good reason for people assuming it works like this is that most articles and presentations about JSTL say empty works for Collections. Just to mention a few examples, articles published at OnJava, IBM DeveloperWorks and even java.sun.com wrongly state it. But why can it simply return true although the spec doesn't mention it? Well, because it cannot be considered an omission. The spec says that otherwise, it must return false. Otherwise sadly covers Sets and any other Collections implementation that does not descend from a List. Sad, very sad indeed.

Are we doomed to look for alternatives approaches? One of them that works is using ${collection['empty']} when we are sure we are dealing with a Collection. Using a straight property access construction won't work as the property has the same name as the reserved operator. Fortunately, the Expression Language definition specified in JSP 2.0 has fixed the issue by replacing the line that mentioned List with:

   Otherwise, if A is an empty Collection, return true,

So, if you are using JSP 2.0, it's not a problem for you. However, be careful about JSTL 1.0 and this bizarre pitfall. I tried to warn you :-D


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • java.lang.Object and org.omg.CORBA.Object
    I can recall 2 situations when I had to tear my hair out, over what I took for granted and what turned out to be a nasty surprise later!

    1. I had just started programming using JDK 1.2.2. I had implemented my own data structure as a class and overrode the equals() method. I was happily adding the instances of this class to a Collection. The code that adds my object to the Collection, would first check if the Collection already contained that object using the Collection's contains(Object) method. I was assuming that my Object's equals() method would get called and everything would be fine.

    What actually happened (as you might have guessed by now!) was that the Collection.contains(Object) method always returned false, though by class's equals() method was getting called! After some poking around and looking at the source code of the Collection implementation (thanks to Sun for bundling the sources with the JDK!) I learnt that the hashCode() method needs to be overridden as well ... whew!

    I learnt the hard way that whenever equals() method is overridden, the hashCode() method must be overridden as well!

    For seasoned Java programmers, this maybe a very obvious rule, but as a novice Java programmer with a C/C++ background, I would've appreciated better documentation for the equals() method. And yes, the current (J2SDK 1.4.1) Javadocs for equals() method in java.lang.Object explicitly states this rule. Perhaps, a compiler-level "warning" should be generated if a "equals()" method is overridden without overriding "hashCode()" method.

    2. Later I had to write some CORBA code. Same kind of problem! java.lang.Object.equals() did not behave as expected. Some dead-ends and head scratching later ... a seasoned CORBA specialist pointed out, org.omg.CORBA.Object._is_equivalent(Object) is the method that is to be used to check CORBA objects!

    I am definitely not a CORBA expert, but I would expect the java.lang.Object.equals() method contract to be honored whether it is a pure Java object, or a Java wrapper for a CORBA ORB ...

    Posted by: paulponraj on December 01, 2003 at 04:18 AM

  • Thanks, for this verbose explanation of the problem.
    It helped us to find a workaround.

    ALex.

    Posted by: alexgreif on February 10, 2005 at 02:50 AM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds