How to create a custom EL function to invoke a static method?
Asked Answered
C

1

52

Im new to JSF 2. My question is related to BalusC's answer to this question jsf2 ajax update parts based on request parameters I tried the kickstart code BalusC posted and I encountered an EL parsing error:

 /nameofpage.xhtml @12,64 rendered="#{bean.panels.contains('u1')}"
 Error Parsing: #{bean.panels.contains('u1')}

I guess that this is caused because I'm not running a Servlet 3.0 / EL 2.2 capable container with a /WEB-INF/web.xml declared as per Servlet 3.0 spec. I'm using Tomcat 6.

BalusC suggested in his answer to create a custom EL function. But how do I accomplish this using a custom EL function? Or can this be fixed by just configuring certain parts of my project?

Below is my web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>    
  </context-param>      
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
</web-app>
Continue answered 16/8, 2011 at 14:38 Comment(0)
S
90

First create a final class with a public static method which does exactly the job you want:

package com.example;

import java.util.Collection;

public final class Functions {

    private Functions() {
        // Hide constructor.
    }

    public static boolean contains(Collection<Object> collection, Object item) {
        return collection.contains(item);
    }

}

Then define it as a facelet-taglib in /WEB-INF/functions.taglib.xml:

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib 
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
    version="2.0">
    <namespace>http://example.com/functions</namespace>

    <function>
        <function-name>contains</function-name>
        <function-class>com.example.Functions</function-class>
        <function-signature>boolean contains(java.util.Collection, java.lang.Object)</function-signature>
    </function>
</facelet-taglib>

Then familarize Facelets with the new taglib in the existing /WEB-INF/web.xml:

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/functions.taglib.xml</param-value>
</context-param>

(note: if you already have the javax.faces.FACELETS_LIBRARIES definied, then you can just add the new path semicolon separated)

Then define it in the Facelets XHTML file as new XML namespace:

<html 
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:func="http://example.com/functions"
    ...
>

Finally you can use it as intended:

rendered="#{func:contains(bean.panels, 'u1')}"

As a completely different alternative, you can also include JBoss EL in your project. It works on Tomcat 6.0 and you'll be able to invoke non-getter methods in EL. Drop jboss-el.jar file in /WEB-INF/lib and add the following to your web.xml:

<context-param>     
    <param-name>com.sun.faces.expressionFactory</param-name>
    <param-value>org.jboss.el.ExpressionFactoryImpl</param-value>   
</context-param>

Since EL 2.2 there's another approach: create an @ApplicationScoped bean with methods in turn referring to those static functions. See also a.o. Utility methods in application scoped bean.

Shows answered 16/8, 2011 at 14:50 Comment(13)
Thanks for the quick reply BalusC !!! I tried the jobss-el.jar approach. The error is now gone, it displays 3 check boxes u1, u2, u3 but when i choose any of them nothing seems to happen (Chrome,Firefox) I also tested in Internet Exporer and after checking any of the check box it says: Webpage error details Message: 'mojarra' is undefined Line: 9 Char: 1 Code: 0 URI: localhost:8080/myproject/ajaxcall.jsf Am I missing anything? Thanks for your patience and thank you in advance BalusCContinue
You're welcome. Your new question is actually a different question which needs to be asked by Ask Question button on the right top. Your initial question has been answered. Anyway, here's a hint: <h:head>.Shows
Thanks again BalusC the jobss-el.jar approach worked like a charm! I'll ask my new question using the Ask Question.Continue
Hi BalusC. Is there a way to add description to the custom EL, so like when we inside Netbean for example, we can Ctrl + space to show to description of the function?Vibrant
Yes, you can use <description> tag for this: <function><description>Your description here.</description>...</function>.Shows
@Thank you BalusC. Just one more question. Can we do custom EL with overload methods. I have two overload methods(same method name, different number of parameters), JSF generates error as below: Function 'util:transform' specifies 2 params, but 1 were declared. I have them as two separated <function>.Vibrant
@Thang: no, that isn't possible. Each overloaded EL method must have a different EL function name.Shows
Thank you. That is definitely a very bad limitation on JSF/JSP.Vibrant
Hello, can I ask when do you recommend which approach? jBoss or the final class etc, etc, etc.? In what do they differ?Condole
@Roger: JBoss EL saves EL functions boilerplate code. Note that whatever JBoss EL does is also supported in EL 2.2. So if you happen to use Servlet 3.0 / EL 2.2 already, then you can just invoke non-getter methods in EL.Shows
Hello BalusC, does this work if we don't use facelets for rendering, and we have jsp:root ? I mean if it also works in #{} not in ${}Telepathy
@Cosmin: just create a JSP TLD file instead of Facelets taglib XML file. Syntax is very similar. You can find an example of the JSP TLD file at the bottom of our EL wiki page under the section "EL functions": stackoverflow.com/tags/el/infoShows
yes but I want to use it in Deffered EL, with #{}, not with ${} :(Telepathy

© 2022 - 2024 — McMap. All rights reserved.