Programmatically register taglib reference

2019-07-18 18:29发布

问题:

Is there a way to programmatically, in a java code, register custom taglib reference?
I am using JSF 1.2_09, rich faces 3.3.3, jsf-facelets 1.1.14.

Specifically:

In this code, jsf Expression Language is used to do some job for us, like concatenation of 2 results in one field or similar stuff..

FacesContext ctx = FacesContext.getCurrentInstance();
Application app = ctx.getApplication();
ExpressionFactory ef = app.getExpressionFactory();
ELContext elContext = ctx.getELContext();
ValueExpression valueExpression = new OverrideValueExpression(singleResult.getClass(), singleResult);
elContext.getVariableMapper().setVariable("row", valueExpression);

for (int i = 0; i < jsfDisplayValue.size(); i++){
    Object value = ef.createValueExpression(elContext, jsfDisplayValue.get(i), Object.class).getValue(ctx.getELContext());
//Do something with value...
}

E.g., elements of jsfDisplayValue can be:
"#{row.name} #{row.surname}", "#{row.age}", "#{tagfoo:fooFunction(row.age)}"...
The problem occurs when expression contains a function, like highlighted, tagfoo:fooFunction.

Stack trace:

javax.el.ELException: Function 'tagfoo:fooFunction' not found
    at org.apache.el.lang.ExpressionBuilder.visit(ExpressionBuilder.java:198)
    at org.apache.el.parser.SimpleNode.accept(SimpleNode.java:147)
    at org.apache.el.lang.ExpressionBuilder.prepare(ExpressionBuilder.java:155)
    at org.apache.el.lang.ExpressionBuilder.build(ExpressionBuilder.java:173)
    at org.apache.el.lang.ExpressionBuilder.createValueExpression(ExpressionBuilder.java:217)
    at org.apache.el.ExpressionFactoryImpl.createValueExpression(ExpressionFactoryImpl.java:67)

ELContext doesn't recognize custom function, and it can't resolve the function because tagfoo is unknown to ELContext. How can I register taglib reference in java class, so ELContext can recognise custom functions?

On a JSF page, I would do this:

<jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:tagfoo="http://tagfoo.org/tags">  

P.S.
My custom functions are working properly on jsf pages.

回答1:

I'm sorry I'm a bit late, but I just stumbled upon the same problem today.

Inspecting the ElContext and FunctionMapper instances, I have seen they definitely are custom implementations of the standard abstract class.

As I want to keep my code portable, I decided not to get involved into writing code for a specific implementation, and as the abstract class only defines the read contract and omits the write side of things, I just wrote a custom FunctionMapper. Here is the meaningfull code:

public void register(String prefix, String function, Method method) {
    register.put(prefix + ":" + function, method);
}

@Override
public Method resolveFunction(String prefix, String localName) {
    if (register.containsKey(prefix + ":" + localName)) {
        return register.get(prefix + ":" + localName);
    }
    for (FunctionMapper it : delegates) {
        final Method current = it.resolveFunction(prefix, localName);
        if (current != null) {
            return current;
        }
    }
    return null;
}

By wrapping the standard FunctionMapper, I can easily add my own functions and keep the standard environment ok. In my case, I had to write a custom ElContext implementation too, that basicaly clones the default ElContext and just wraps the FunctionMapper.

I'm pretty happy with the results, hope this helps.