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).