eclipse-ee4j / mojarra

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

Programmatic View with UIOutput #5452

Open melloware opened 1 month ago

melloware commented 1 month ago

Sorry to post this here but I can't seem to find in the spec what is the correct behavior between MyFaces and Mojarra.

Original report: https://github.com/melloware/quarkus-faces/issues/444

When creating a programmatic view with the following code:

package org.apache.myfaces.core.extensions.quarkus.showcase.view;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.annotation.View;
import jakarta.faces.application.StateManager;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIOutput;
import jakarta.faces.component.html.HtmlBody;
import jakarta.faces.component.html.HtmlCommandButton;
import jakarta.faces.component.html.HtmlForm;
import jakarta.faces.component.html.HtmlOutputText;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.facelets.Facelet;

@View("/facelet.xhtml")
@ApplicationScoped
public class FaceletView extends Facelet
{

    @Override
    public void apply(FacesContext facesContext, UIComponent parent)
    {
        if (!facesContext.getAttributes().containsKey(StateManager.IS_BUILDING_INITIAL_STATE))
        {
            return;
        }

        var components = new ComponentBuilder(facesContext);
        var rootChildren = parent.getChildren();

        var doctype = new UIOutput();
        doctype.setValue("<!DOCTYPE html>");
        rootChildren.add(doctype);

        var htmlTag = new UIOutput();
        htmlTag.setValue("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
        rootChildren.add(htmlTag);

        HtmlBody body = components.create(HtmlBody.COMPONENT_TYPE);
        rootChildren.add(body);

        HtmlForm form = components.create(HtmlForm.COMPONENT_TYPE);
        form.setId("form");
        body.getChildren().add(form);

        HtmlOutputText message = components.create(HtmlOutputText.COMPONENT_TYPE);
        message.setId("message");

        HtmlCommandButton actionButton = components.create(HtmlCommandButton.COMPONENT_TYPE);
        actionButton.setId("button");
        actionButton.addActionListener(
                e -> message.setValue("Hello, World! Welcome to Faces 4.0 on Jakarta EE 10"));
        actionButton.setValue("Greet");

        form.getChildren().add(actionButton);

        parent.getChildren().add(message);

        htmlTag = new UIOutput();
        htmlTag.setValue("</html>");
        rootChildren.add(htmlTag);
    }

    private static class ComponentBuilder
    {
        FacesContext facesContext;

        ComponentBuilder(FacesContext facesContext)
        {
            this.facesContext = facesContext;
        }

        @SuppressWarnings("unchecked")
        <T> T create(String componentType)
        {
            try
            {
                return (T) facesContext.getApplication().createComponent(componentType);
            }
            catch (ClassCastException e)
            {
                throw new IllegalArgumentException("Component type " + componentType + " is not valid.", e);
            }
        }
    }
}

Attached is the reproducer: programmatic_view.zip

Run mvn clean jetty:run -Pmojarra40 and go to http://localhost:8080/facelet.xhml and Mojarra does this

image

Run mvn clean jetty:run -Pmyfaces40 and go to http://localhost:8080/facelet.xhml and MyFaces does this image

the question comes down to "who is doing the right thing"? MyFaces seems to escape the following code but Mojarra does not

 var htmlTag = new UIOutput();
htmlTag.setValue("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
rootChildren.add(htmlTag);
melloware commented 3 weeks ago

OK according to this MyFaces is doing the right thing and Mojarra is not.

https://github.com/jakartaee/faces/issues/1796

BalusC commented 2 weeks ago

Not sure. See comment over there.