eclipse-ee4j / mojarra

Mojarra, a Jakarta Faces implementation
Other
158 stars 107 forks source link

Can not eval expression in f:attribute #5378

Closed rsoika closed 6 months ago

rsoika commented 6 months ago

Describe the bug

I have a component like this one:

    <h:inputText value="#{workitem.item[item.name]}" >
        <f:converter converterId="ImixsDateConverter" />
        <f:attribute name="org.imixs.date.timeZone" value="CET" />
    </h:inputText>

It is not possible to set the f:attribute value to a EL expression like this :

        <f:attribute name="org.imixs.date.timeZone" value="#{message.timeZone}" />

where 'timeZone' is defined in a JSF resource bundle. Also evaluating bean values is not possible.

The value will not be part of the component attribute map that you can access form the @FacesConverter

Example Code:

@FacesConverter("ImixsDateConverter")
public class ImixsDateConverter implements Converter, Serializable {

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
            String value = component.getAttributes().get('org.imixs.date.timeZone');
            // map does NOT contain the attribute if the value is provided by a EL expression

    }

Only pure string values are transferred into the attribute map.

To Reproduce

Run the example tag in a Wildfly 27.0.1.Final-jdk11

Desktop (please complete the following information):

Additional context

I am running the official Wildfly Docker immage 27.0.1.Final-jdk11

BalusC commented 6 months ago

We need more context. This works fine for me on Mojarra 4.0.5.

rsoika commented 6 months ago

I wonder which Mojarra Version we have in Wildfly 27.0.1 - If I am not wrong it is 4.0.0.SP01. But this is just guessing

I reduced my code into a minimal example to reproduce the problem:

TestBean.java

@Named
@ConversationScoped
public class TestBean implements Serializable {
    String value;
    //Just a simple string provider
    public String getTimeZone() { return "UTC+1"; }
    public String getValue() { return value; }
    public void setValue(String value) {this.value = value;}
    public void submit() {System.out.println(value);}
}

The Converter Class which seems to cause the issue:

import java.io.Serializable;
import java.util.Map;
import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.convert.Converter;
import jakarta.faces.convert.FacesConverter;

@FacesConverter("TestConverter")
public class TestConverter implements Converter, Serializable {

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {

        if (value == null || value.trim().isEmpty()) {
            return ""; // Convert null to empty string
        }

        value = value.trim();
        Map<String, Object> map = component.getAttributes();

        // show all attributes
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            System.out.println("Attribute - " + entry.getKey() + ":" + entry.getValue());
        }

        return value;

    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (value == null || "".equals(value)) {
            return ""; // Convert null to empty string
        }
        return value.toString();
    }
}

The Test xhtml page:

<?xml version="1.0" encoding="UTF-8"?>
<!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">

<h:body>
    <f:view>
        <h:form>

            <h1>Test f:attributes</h1>

            Timezone: #{testBean.timeZone}

            <hr />
            <h:inputText value="#{testBean.value}">
                <f:converter converterId="TestConverter" />
                <f:attribute name="org.imixs.date.timeZone" value="CET" />
                <f:attribute name="org.imixs.date.test" value="#{testBean.timeZone}" />
            </h:inputText>
            <h:commandButton value="Submit" actionListener="#{testBean.submit}" />

        </h:form>
    </f:view>
</h:body>

</html>

The expectation is that the converter prints out the two attributes org.imixs.date.timeZone and org.imixs.date.test.

But only the first attribute is detected. The second is not part of the list. And this is only because the Value tag has an EL expression. If the attribute values are provided as static strings, all works as expected.

BalusC commented 6 months ago

The problem is here

// show all attributes
for (Map.Entry<String, Object> entry : map.entrySet()) {
    System.out.println("Attribute - " + entry.getKey() + ":" + entry.getValue());
}

The entrySet() doesn't return attributes which are actually value expressions because they are not static.

Use String value = component.getAttributes().get('org.imixs.date.timeZone') instead like as in your first code example.

See also:

rsoika commented 6 months ago

Thanks, yes that works. This wasn't obvious to me and I really spent a lot of time testing it out....