Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSF 2.0: When is encodeAll called and when is encodeBegin called?

Tags:

jsf

jsf-2

Consider a custom UIComponent (for test purposes only):

public class UITest extends UIComponentBase {

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        System.out.println("encodeBegin");
    }

    @Override
    public void encodeAll(FacesContext context) throws IOException {
        System.out.println("encodeAll");
    }

}

When I add it to page inside a composite component, the encodeBegin() method gets called. However, when add it to page outside a composite component, the encodeAll() method gets called instead.

Adding it inside other UIComponents makes no difference, only composite component wrapper seems to change the behavior.

Couldn't find info why it is so? A link to the spec?

like image 950
Tuukka Mustonen Avatar asked Nov 26 '10 09:11

Tuukka Mustonen


1 Answers

The spec is really messy in this case, stating that: "These methods are called during the Render Response phase of the request processing lifecycle. encodeAll() will cause this component and all its children and facets that return true from isRendered() to be rendered, regardless of the value of the getRendersChildren() return value. encodeBegin(), encodeChildren(), and encodeEnd()have the responsibility of creating the response data for the beginning of this component, this component’s children (only called if the rendersChildren property of this component is true), and the ending of this component, respectively."

However, this seems to be a mixture of new and old features, where the new functionality (encodeAll) seems to be incomplete in some ways:

I tried the following:

A) Calling the component directly in the page (no wrapper)

  1. extend UIComponentBase (or other UIComponent class such as UIInput, UIOutput.. etc), declare it as a tag, and use it in the UI. In this case the encodeAll method is called IF it is present (overridden), if not the encodeBegin and encodeEnd methods will be called!!

  2. Another thing to note is that you can create a custom Renderer for the component, so you can separate rendering logic from behaviour. (by creating another class that extends Renderer, and annotating it with @FacesRenderer) This is where it gets interesting; Renderer defines only encodeBegin, encodeChildren and encodeEnd (with no mention of encodeAll). Now the logic seems to go roughly like this: if (encodeAll is present) encodeAll is called (and the renderer is ignored!) else if(any of encodeBegin,Children,or end exist in the class that extends UIComponent) call the method that was found in that component else if(encodeBegin, children or end exist in the class that extends Renderer) call the corresponding method that was found.

So this means that implementing encodeAll (or encodeBegin.. etc ) in the class extending UIComponent causes the renderer to be ignored!

B) Wrapping the component (cc:implementation.. etc)

In this case the same thing happened as above, except that encodeAll was not called in any case, no matter what I did.

Conclusion: It seems that encodeAll is some kind of new functionality (or shortcut) to implement the rendering code, and it seems that cc:implementation has a bug in this case (it doesn't look for encodeAll).

I hope this is at least of some value to you, unfortunately I cannot provide a more thorough answer. :( It also seems that nobody else knows about this.

like image 95
Rami Ayoub Avatar answered Nov 03 '22 18:11

Rami Ayoub