1

I have defined a JSF composite component, with relevant portions shown below. Update: Improved the example to use JSF 2.2 namespace declarations as recommended by Kukeltje, though problem still exists.

/resources/functiontest/functiontest.xhtml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:composite="http://xmlns.jcp.org/jsf/composite"
    xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
>
<composite:interface>
    <composite:attribute name="value" type="java.util.Date"/>
    <composite:attribute name="timeZone" type="java.util.TimeZone"/>
    <composite:attribute name="update" type="java.lang.String"/>
</composite:interface>

<composite:implementation>
    <div id="#{cc.clientId}">
        clientId class=#{cc.clientId['class'].name}<br/>
        clientId=#{cc.clientId}<br/>
        value=#{cc.attrs.value}<br/>
        timeZone=#{cc.attrs.timeZone}<br/>
        update=#{cc.attrs.update}<br/>
        <br/>

        WORKS - fn:toLowerCase - #{fn:toLowerCase("hellOOOOOO")}<br/>
        WORKS - whoName = #{myBean.user}<br/>
        WORKS - fn:toUpperCase1 = #{fn:toUpperCase(myBean.user)}<br/>
        WORKS - fn:toUpperCase2 = #{fn:toUpperCase(myBean.toString())}<br/>
        WORKS - fn:toUpperCase3 = #{fn:toUpperCase(myBean['class'].simpleName)}<br/>
        FAILS - fn:toUpperCase4 = #{fn:toUpperCase(cc.clientId['class'].name)}<br/>
        FAILS - fn:toUpperCase5 = #{fn:toUpperCase(cc.clientId)}<br/>
        FAILS - fn:toUpperCase6 = #{fn:toUpperCase(cc.attrs.value.toString())}<br/>
        FAILS - fn:toUpperCase7 = #{fn:toUpperCase(cc.attrs.update)}<br/>
    </div>
</composite:implementation>
</html>

/src/FunctionTestBean.java

import java.util.Date;
import java.util.TimeZone;

import javax.faces.bean.ManagedBean;

@ManagedBean(name="myBean")
public class FunctionTestBean
{
    public String getUser() { return "UserX"; }
    @Override public String toString() { return "toString method output"; }
    private Date now = new Date();
    public Date getNow() { return now; }
    public TimeZone getTimeZone() { return TimeZone.getDefault(); }
}

/testFunctionPage.xhtml (using template - doesn't work beginning with JSF 2.2.5)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:composite="http://xmlns.jcp.org/jsf/composite"
    xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
    xmlns:fntest="http://xmlns.jcp.org/jsf/composite/functiontest"
>

<ui:composition template="/template/PublicPageTemplate.xhtml">
<ui:define name="body">
    Function test:<br/>
    <ui:repeat var="loopVar" value='#{"a,b,c".split(",")}'>
        LoopVar=#{loopVar}<br/>
        <fntest:functiontest id="tst2" value="#{myBean.now}" timeZone="#{myBean.timeZone}" update="UpdateOrDontUpdate"/>
    </ui:repeat>

</ui:define>
</ui:composition>
</html>

/template/PublicPageTemplate.xhtml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
</h:head>
<h:body>

<div style="padding-left:5%; padding-right:5%;">
    <ui:insert name="body">DEFAULT BODY!</ui:insert>
</div>
</h:body>
</html>

/testFunctionPageWithHBody.xhtml (using h:body - WORKS!)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:composite="http://xmlns.jcp.org/jsf/composite"
    xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"
    xmlns:fntest="http://xmlns.jcp.org/jsf/composite/functiontest"
>

<h:head>
</h:head>
<h:body>
    Function test:<br/>
    <ui:repeat var="loopVar" value='#{"a,b,c".split(",")}'>
        LoopVar=#{loopVar}<br/>
        <fntest:functiontest id="tst2" value="#{myBean.now}" timeZone="#{myBean.timeZone}" update="UpdateOrDontUpdate"/>
    </ui:repeat>

</h:body>
</html>

It appears that attempting to pass any part of cc.* to a function fails, even though they are obviously working just to print out the value. I have tried this for both JSTL functions and my own functions. Here is the error:

javax.el.ELException: Function 'fn:toUpperCase' not found

My best guess is that there are limitations to using cc that I haven't learned, and I'm hoping someone can illuminate what they are and what is going on. This appears to have been introduced in JSF 2.2.5; all variations work with JSF 2.2.1 through 2.2.4.

Here is some additional strange behavior. If I load testFunctionPage.xhtml first, it fails. If I load testFunctionPageWithHBody.xhtml, it succeeds (as expected). Then if I load testFunctionPage.xhtml, it will now succeed, and will continue to do so until the web server (Tomcat) is restarted!

Edit: The environment I'm running with is Windows 7, Java 1.7.0_80, Tomcat 7.0.68 with its version of EL, JSF Mojarra 2.2.8-14, jstl 1.2, Eclipse Mars.2 (4.5.2).

Pixelstix
  • 702
  • 6
  • 21
  • 1
    start by using all jsf 2.2 namespace declarations: http://stackoverflow.com/questions/7593603/jstl-xmlns-namespace-differences-between-jsf-1-2-and-jsf-2-x. Not sure if it plays a role, but it is never wrong – Kukeltje Apr 11 '16 at 15:57
  • Good catch/correction, @Kukeltje, I definitely should be using those new namespace declarations for JSF 2.2. However, that appears to _not_ be the cause of the problem. I still get the "Function 'fn:toUpperCase' not found" `ELException` on page load. – Pixelstix Apr 11 '16 at 16:41
  • what if you do some 'string concatenation' with an empty string first for the 'cc'? Does it work then or do you get empty strings? – Kukeltje Apr 12 '16 at 07:46
  • I get the expected strings if I pass in either `""`, `"abc"`, `cc`, `cc['class'].simpleName`, or `"abc".concat(cc)`. I get `javax.el.ELException: Function 'fn:toUpperCase' not found` if I pass in `cc.toString()`, `cc.attrs`, `cc.attrs.value`, `cc.attrs.timeZone`, `"".concat(cc.attrs.timeZone)` – Pixelstix Apr 12 '16 at 15:27
  • EL has no strong typing and (maybe?) no automatic conversion to String. So cc.attrs (returning a list?) makes it the wrong signature for the function. As does cc.attrs.timeZone (TimeZone) also for the 'concat' example). Why cc.toString() fails is unclear to me. And cc.attrs.value might return an 'object' instead of String... It at least looks that way for the call to the jstl function. Maybe you now have more info to investigate – Kukeltje Apr 12 '16 at 15:40
  • Passing `cc` returns the class name @ HEX value, so it seems like it's matching fn:toUpperCase by automatically using a `toString()` method in that case. Passing `cc.attrs.toString()` results in `javax.el.ELException: Illegal attempt to pass arguments to a composite component lookup expression (i.e. cc.attrs.[identifier]).`, so that looks like a special case. Everything I've tried with `.toString()` except `cc.attrs.toString()` results in `'fn:toUpperCase' not found`. I just don't see the pattern. – Pixelstix Apr 12 '16 at 16:29
  • FYI: I reproduced it, but the logic is kind of convoluted and I didn't have time to naildown the cause. I only suspect this bug was introduced in a newer Mojarra version in order to solve another bug. I recall older bugs related to EL function namespace resolving in composites which were fixed last year or two. Try e.g. Mojarra 2.2.2 and see what happens. If it fails the same way, try e.g. 2.1.20. If you find the exact version where it starts to fail, then we can check the release notes what exactly changed there. – BalusC Apr 20 '16 at 09:16
  • Thanks so much. I'm trying to pin this down more, including testing with different versions. I've found some simplified conditions under which I can get it to work. I'll report back when I've found more, but it won't be before your tomorrow. – Pixelstix Apr 20 '16 at 14:19
  • That was quicker than I thought it would be. It works from 2.2.1 through 2.2.4, then versions 2.2.5 and 2.2.8-14 don't work (I didn't try other versions between). On newer versions, if I just have and on the containing page it seems to work, but not if it's in a . But sometimes through some sequence of modifications (change to using , it works, change back to using and it still works), it will temporarily work with until I restart Tomcat. Strange, indeed! (I'll incorporate this into the question.) – Pixelstix Apr 20 '16 at 15:37
  • Great. Here are the [2.2.5 release notes](https://java.net/jira/secure/ReleaseNote.jspa?projectId=10204&version=16651) (note, it's quite a list but it also includes "wontfix" and "norepro"). – BalusC Apr 21 '16 at 07:42
  • I'm not familiar at all with the JSF code base, but after 45 minutes of looking at code diffs between 2.2.4 and 2.2.5, my best guess is that it would have something to do with the changes in com\sun\faces\facelets\el\ELText.java since it has to do with EL parsing and execution. But I don't see a way that would change behavior when called from a template vs called from a regular page. Maybe it's a bug in Tomcat's EL ExpressionFactory, which just wasn't used in this case before 2.2.5, or maybe something somewhere else isn't initializing things correctly. – Pixelstix Apr 21 '16 at 17:15

0 Answers0