• Post Reply Bookmark Topic Watch Topic
  • New Topic

Issues with Character comparison in JSTL 1.1  RSS feed

 
Kerry Baer
Ranch Hand
Posts: 39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am trying to upgrade from Tomcat 6.0.20 x86 to Tomcat 7.0.26 x64.

I have an application that we have been running on the old Tomcat. Inside the application I have a PROGRAM object that contains the following property:


Inside one of the JSP pages I am trying to determine whether or not to display content based on the value of this character. Here is the JSTL in my JSP:


I recieved the following error message when attempting to load the page:
  • javax.el.ELException: Cannot convert N of type class java.lang.String to class java.lang.Long


  • So I modified the IF statement in attempt to resolve the error by changing == to eq:


    Same error. So I started looking around in the JSTL functions and found fn:contains. Since this is a character object, I figured it would be sufficient if I could get it to work. So I changed my JSTL code to the following:


    Voila! It works. However, I am not concerned as to why the change in Tomcat/Java JRE has caused JSTL to get confused with the comparison. When I print out the object I get the character value as expected.


    It's during the comparison that it fails. Does anyone know why my original code doesn't work in the on the new application server?
     
    Bear Bibeault
    Author and ninkuma
    Marshal
    Posts: 66158
    146
    IntelliJ IDE Java jQuery Mac Mac OS X
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    You're confusing the JSTL with the EL. The comparison is an EL operation -- it has little to do with the JSTL.

    It doesn't help that you are using really poor non-stadard naming (underscores? really?) rather than following javabean standards. While it might or might not be confusing the property access code, it certainly makes it hard to look at the code and understand what's going on. Conventions and standards are set into place for a reason. You should be following them.

    That said, you claim you are having problems with the property program.is_general_public_yn, but then say that program.is_general_public is "printing" as expected. So what? Those aren't the same properties, so the fact that the second one "prints" has nothing to do with the property you are using in the comparison expression.
     
    Bear Bibeault
    Author and ninkuma
    Marshal
    Posts: 66158
    146
    IntelliJ IDE Java jQuery Mac Mac OS X
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    P.S. With Tomcat 6 and above you should be using JSTL 1.2, but that will have no bearing n this particular issue.
     
    Paul Clapham
    Sheriff
    Posts: 22378
    42
    Eclipse IDE Firefox Browser MySQL Database
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Yes, using char properties is a pain as far as the EL is concerned. The char values are internally coerced to long values, but there is no way to express a char constant in EL. Both the single-quote and double-quote character delimit strings, unlike in Java where 'N' and "N" are different things. Your fn:contains workaround is at least more understandable than our workaround where we replaced 'N' by 78 (the Unicode value of the character 'N') -- along with a comment explaining why 78.
     
    Bear Bibeault
    Author and ninkuma
    Marshal
    Posts: 66158
    146
    IntelliJ IDE Java jQuery Mac Mac OS X
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Indeed, if you are using a char of N or Y to express a boolean, why aren't you using a boolean?
     
    Kerry Baer
    Ranch Hand
    Posts: 39
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Bear Bibeault wrote:You're confusing the JSTL with the EL. The comparison is an EL operation -- it has little to do with the JSTL.

    It doesn't help that you are using really poor non-stadard naming (underscores? really?) rather than following javabean standards. While it might or might not be confusing the property access code, it certainly makes it hard to look at the code and understand what's going on. Conventions and standards are set into place for a reason. You should be following them.

    That said, you claim you are having problems with the property program.is_general_public_yn, but then say that program.is_general_public is "printing" as expected. So what? Those aren't the same properties, so the fact that the second one "prints" has nothing to do with the property you are using in the comparison expression.


    This application is a couple years old and when I started the project I had very little knowledge of Java and other supporting technologies. It is in the plan to clean up the code to meet conventions, but that is not a very high priority on my list of things to do with this application.

    Is your suggestion for a fix to remove the underscore in the property name? If that is the case, I tried changing the property name from "is_general_public_yn" to "isGeneralPublicYN" and I get the same error message.

    "printing" means to output using EL and JSTL => <c:out value="${program.isGeneralPublicYN}" />

    How is that not the same object when doing a comparison as follows => <c:if test="${program.isGeneralPublicYN=='N'}"> ??

    I am just curious to understand the error and how to correct it. Thank you for your help.
     
    Kerry Baer
    Ranch Hand
    Posts: 39
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Bear Bibeault wrote:Indeed, if you are using a char of N or Y to express a boolean, why aren't you using a boolean?


    We decided on a character because we wanted to leave it open to expansion past a simple on/off switch in planned enhancements down the road.
     
    Kerry Baer
    Ranch Hand
    Posts: 39
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Paul Clapham wrote:Yes, using char properties is a pain as far as the EL is concerned. The char values are internally coerced to long values, but there is no way to express a char constant in EL. Both the single-quote and double-quote character delimit strings, unlike in Java where 'N' and "N" are different things. Your fn:contains workaround is at least more understandable than our workaround where we replaced 'N' by 78 (the Unicode value of the character 'N') -- along with a comment explaining why 78.


    The strange thing to me is that I didn't need to use a workaround in Tomcat 6. Do you have any understanding of why it behaves differently in Tomcat 7?

    I tried running Tomcat on both Java 6 and Java 7 and resulted in the same error, so I assume it's Tomcat 7 because Tomcat 6 was using the same Java 6 and worked.
     
    Kerry Baer
    Ranch Hand
    Posts: 39
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Ok. So I have done some more research trying to figure this out and have found some interesting information that I wanted to document here...

    In the Expression Language Specification Version 2.2 Maintenance Release documentation on Oracle.com in section 1.8.1 it states:

    A {<,>,<=,>=,lt,gt,le,ge} B
    ■ If A==B, if operator is <=, le, >=, or ge return true.
    ■ If A is null or B is null, return false
    ■ If A or B is BigDecimal, coerce both A and B to BigDecimal and use the return
    value of A.compareTo(B).
    ■ If A or B is Float or Double coerce both A and B to Double apply operator
    ■ If A or B is BigInteger, coerce both A and B to BigInteger and use the return
    value of A.compareTo(B).
    Chapter 1 Language Syntax and Semantics 13
    If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to
    Long and apply operator

    ■ If A or B is String coerce both A and B to String, compare lexically
    ■ If A is Comparable, then:
    ■ If A.compareTo(B) throws exception, error.
    ■ Otherwise use result of A.compareTo(B)
    ■ If B is Comparable, then:
    ■ If B.compareTo(A) throws exception, error.
    ■ Otherwise use result of B.compareTo(A)
    ■ Otherwise, error


    In particular I notice the following line of logic:
    If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to
    Long and apply operator


    This sort of explains what is going on. My ${program.is_general_public_yn} propery is being coerced from char to long. However, the 'N' in-line variable is being treated as a string and is not being converted to a long for the comparison. If I am reading the specification correctly, then 'N' should be converted to a long because A was detected as a character object and converted to a long. But that isn't what is happending. Only one side is converted while the other remains as String and the comparison causes an error.

    I went into my controller (using Spring MVC 2.5.6) and added a character object to the reference mapping:


    Then I went into the JSP and changed 'N' to the new object reference:



    The expression evaluates as expected in this case. Meaning that both A (program.isGeneralPublicYN) and B (charN) are being recognized as Character objects, coerced to Long and then compared as Long objects. So, either Tomcat has a bug or they are simply meeting the requirements specifications more accurately. I guess that depends on how you interpret the specification. Either way I have so far provided 2 workarounds, but have found no real "cause" of the issue. My research continues.
     
    Paul Clapham
    Sheriff
    Posts: 22378
    42
    Eclipse IDE Firefox Browser MySQL Database
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Kerry Baer wrote:In particular I notice the following line of logic:
    If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to
    Long and apply operator


    This sort of explains what is going on.


    Yes, that was what I had concluded too. But if you read the spec more, you'll see that "coercing String to Long" consists of (in your case) calling Long.valueOf("N") which will throw a NumberFormatException. Remember that char literals don't exist in EL, so Long.valueOf(String) gets called rather than Long.valueOf(char).

    In other words... if you didn't have to do a workaround before, then that was a bug which has been fixed now.
     
    Kerry Baer
    Ranch Hand
    Posts: 39
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Paul Clapham wrote:
    Kerry Baer wrote:In particular I notice the following line of logic:
    If A or B is Byte, Short, Character, Integer, or Long coerce both A and B to
    Long and apply operator


    This sort of explains what is going on.


    Yes, that was what I had concluded too. But if you read the spec more, you'll see that "coercing String to Long" consists of (in your case) calling Long.valueOf("N") which will throw a NumberFormatException. Remember that char literals don't exist in EL, so Long.valueOf(String) gets called rather than Long.valueOf(char).

    In other words... if you didn't have to do a workaround before, then that was a bug which has been fixed now.


    To meet the stated specification, before moving on to String conversion and upon the fail of Long.valueOf(char), Tomcat could attempt Long.valueOf(String.valueOf(char)). Tomcat should say "Hey, this is a character. I know characters can't be directly converted to a Long, but Strings can be. So I should convert the character to a String and then parse it as a long." But obviously it doesn't. It just moves on to the next check, leaving the character as a Long and converting the in-line character to a String before executing the comparison.

    I wonder if it is just that my interpretation of the specification is different than that of the Tomcat developers or if it is a bug in Tomcat 7. Likely it is the former.
     
    Kerry Baer
    Ranch Hand
    Posts: 39
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Kerry Baer wrote:

    To meet the stated specification, before moving on to String conversion and upon the fail of Long.valueOf(char), Tomcat could attempt Long.valueOf(String.valueOf(char)). Tomcat should say "Hey, this is a character. I know characters can't be directly converted to a Long, but Strings can be. So I should convert the character to a String and then parse it as a long." But obviously it doesn't. It just moves on to the next check, leaving the character as a Long and converting the in-line character to a String before executing the comparison.

    I wonder if it is just that my interpretation of the specification is different than that of the Tomcat developers or if it is a bug in Tomcat 7. Likely it is the former.


    Also; why does it evaluate properly when I use 2 objects from the reference mapping, but fails when I use an in-line variable? It seems to be conflicting logic.
     
    Rob Davis
    Greenhorn
    Posts: 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator

    We saw this exact same error when moving up one of our applications. The interesting thing is that it works in Tomcat 7.0.25 and fails in Tomcat 7.0.27. We have a Spring MVC application with the following in the controller:

    if (tabState == null) {
    model.addAttribute("hasPtpTab",'Y');
    } else {
    // Any state other then null means the tab functionality is OFF
    model.addAttribute("hasPtpTab",'N');
    }

    (yes I know it could/should be a boolean)

    and in the JSP:

    <!-- Add the PTP button, on its own row if the PTP tab is present -->
    <c:if test="${hasPtpTab == 'Y'}">
    ....

    </c:if>

    This dies on JSP/EL compilation with:

    ...big stack trace here in Tiles code...
    to:
    Caused by: javax.el.ELException: Cannot convert Y of type class java.lang.String to class java.lang.Long
    at org.apache.el.lang.ELSupport.coerceToNumber(ELSupport.java:304)
    ....

    Changing the JSP code to:

    <!-- Add the PTP button, on its own row if the PTP tab is present -->
    <c:if test="${hasPtpTab == 89}">
    ....

    </c:if>

    "Fixes" the issue and allows the application to work.

    My question would echo the thread above - isn't the fact that it's failing a bug? Not that using characters as booleans is a good thing, but shouldn't it not break? We have other applications that have similar character value equality checks in the JSP that seem to work fine, so I'm also a little confused (and worried) about when/where it's "wrong".

    Could this:

    https://issues.apache.org/bugzilla/show_bug.cgi?id=52666

    have something to do with the issue?
     
    Bear Bibeault
    Author and ninkuma
    Marshal
    Posts: 66158
    146
    IntelliJ IDE Java jQuery Mac Mac OS X
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    No. The fact that it's failing means that the bug that allowed it to (incorrectly) work in the first place has been fixed.
     
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!