Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically instantiating ValueExpressions

I am dynamically adding components on PreRenderViewEvent, using the following method http://blog.kennardconsulting.com/2010/10/safely-manipulating-component-tree-with.html .

It works fine for the component adding part, but I have a problem when I try to dynamically instantiate ValueExpression-s.

To be more specific, I have a problem when I try to forge a dynamic ValueExpression using a dynamically passed parameter.

Let's try to explain an example...

At the top level, I use a tag component (a component described in a tag file, not a composite and not a custom component.

<my:topComponent param=#{toto}"/>

In my:topComponent, I pass param to a nested component.

<my:nestedComponent param2=#{param}/>

This nestedComponent is using a custom component (in my case, a component I derive from primefaces Datatable), passing it param2 as another parameter...

<my:customComponent finalParam=#{param2}/>

In customComponent, I dynamically add some child components on PreRenderViewEvent and set some ValueExpression-s for customComponent.

Some of those expressions use finalParam. So, I unwrap finalParam value, then build a new ValueExpression :

String varName = getValueExpression("finalParam").getExpressionString().replace("#{", "").replace("}", "");

Then I instantiate my dynamic value expression using this helper function :

public static ValueExpression createValueExpression(String expression, Class clazz) {
        FacesContext fc = FacesContext.getCurrentInstance();
        ELContext elContext = fc.getELContext();
        ExpressionFactory expFactory = fc.getApplication().getExpressionFactory();
        ValueExpression ret = expFactory.createValueExpression(elContext, expression, clazz);
        return ret;
    }

Example :

ValueExpression dynExpression = JSFUtils.createValueExpression("#{" + varName + ".code" + "}"), Object.class);

In this example, the value expression is "#{param2.code}"

I can then set this valueExpression to my component :

this.setValueExpression("rowKey", dynExpression);

All this code is in the custom component class. I use the renderer of the base class.

However, the programmatically instantiated ValueExpression is not properly evaluated during rendering. For instance, when primefaces datatable renderer tries to compute the rowKey, #{param2.code} is evaluated to "null", as param2 seems to be unknown.

What should I do to correct this ? While debugging, I noticed that the getValueExpression("finalParam") has a VariableMapper set, while dynExpression has none (null value)

If I get it right, this VariableMapper is used to translate param2 to param.

How can I instantiate my dynamic expression so that the VariableMapper(s) chain is preserved ? The question is the same for FunctionMapper.

Thanks in advance.

UPDATE I agree with Richard Kennard reply : it seems to be the same bug.

As I can not wait years for a fix, I use the following kludge to recursively resolve variables. It works for simple cases with my MyFaces 2.1.9 / CODI 1.0.5 / OWB 1.1.6 / Tomcat 7 stack.

    public static String getValueExpressionExpression(ValueExpression valueExpression) {
        return valueExpression.getExpressionString().replace("#{", "").replace("}", "");
    }

    public static String getMappedValueExpression(ValueExpression valueExpression) {
        ContextAwareTagValueExpression ctxAware = (ContextAwareTagValueExpression)valueExpression;
        if(ctxAware != null) {
            return getMappedValueExpression((WrappedValueExpression)ctxAware.getWrapped());
        }
        return getValueExpressionExpression(valueExpression);
    }

    public static String getMappedValueExpression(WrappedValueExpression wrappedExpression) {
        String exprString = wrappedExpression.getExpressionString().replace("#{", "").replace("}", "");
        String ret = exprString;
        try {

            Field valueExpression = WrappedValueExpression.class.getDeclaredField("valueExpression");
            valueExpression.setAccessible(true);
            ValueExpressionImpl vei = (ValueExpressionImpl) valueExpression.get(wrappedExpression);
            Field varMapper = ValueExpressionImpl.class.getDeclaredField("varMapper");
            varMapper.setAccessible(true);
            VariableMapperImpl vmi = (VariableMapperImpl) varMapper.get(vei);
            if(vmi != null) {
                String[] components = exprString.split("\\.");
                components[0] = getMappedValueExpression(vmi.resolveVariable(components[0])); 
                ret = "";
                for(int i = 0 ; i < components.length ; i++) {
                    if(i != 0) {
                        ret += ".";
                    }
                    ret += components[i];
                }
            }
        } catch (Exception ex) {
            logger.error("Exception lors du mapping de l'expression EL " + exprString, ex);
        } finally {
            return ret;
        }
    }

It would be great to have cleaner versions of this workaround in MyFaces or Mojarra...

like image 738
Ludovic Pénet Avatar asked Nov 13 '22 00:11

Ludovic Pénet


1 Answers

This is a known bug in JSF.

It has been filed here with the Mojarra team: https://java.net/jira/browse/JAVASERVERFACES-2089 and here with the MyFaces team: https://issues.apache.org/jira/browse/MYFACES-3168. I realize the phrasing of these bug reports is not exactly what you'd expect, but it is the same bug. If you look at the comments:

Hi guys,

I just hit this bug again while working on a client's code, and
noticed Manfred had closed it as 'resolved'?

Why was this considered 'resolved'? It's a real problem for me,
stopping me doing things like...

<h:dataTable value="#{companyImport.importFiles}" var="_importFile">
    <h:column>
        <h:outputText value="#{_importFile.name}:"/>
    </h:column>
    <h:column>
        <m:metawidget value="#{_importFile.import}"/>
    </h:column>
</h:dataTable>

...because the m:metawidget cannot 'see' the #{_importFile} var.

Can we please reopen, or at least explain why it is resolved?

Neither of these bug reports are making much progress. You may want to comment on them, vote on them, and lend weight to the issue.

like image 172
Richard Kennard Avatar answered Nov 15 '22 12:11

Richard Kennard