You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@myfaces.apache.org by Nicklas Karlsson <ni...@gmail.com> on 2005/11/12 13:19:01 UTC

Component manipulation in phase listener

Hello,

I'm a JSF-noob(tm) and fresh convert from Struts so there might be something
with the lifecycle I don't understund but...

I'm trying to make a DB-configurable application using a phase listener that
iterates over all the components in the tree and applies attributes,
validators etc to them the id can be found in a map of description objects
(or hide the component if the user doesn't have access to it)

I can't however make the changes take effect when running the page for the
first time. I attached the listener to all phases and the debug code shows
that it is doing a setRendered(false) for a component but it still shows up!
When I navigate back to the page the component disappears. Something with
phases being skipped when there is nothing to restore? If so, is there a way
to get around this (by using some other "for every page" method)?

Help is appreciated since this would really add some nice configurability
for our application.

-Nik

Re: Component manipulation in phase listener

Posted by Nicklas Karlsson <ni...@gmail.com>.
> Another approach would be to try and intercept stuff as each component
> is rendered; for your purposes that's fine, yes? So how about this:
> * Override the default RenderKit with a wrapper of your own.
> * On each call to addRenderer(...), call:
> super.addRenderer(new MyRendererWrapper(r));
> so all the standard renderer objects have a custom wrapper around them
> * Your custom wrapper then intercepts encodeBegin(), and potentially
> updates the component's "rendered" state before delegating on to the
> real renderer.
>
>
> I can't think of anything else at the moment.
>
> I hope some of this helps.



Thanks for your extensive answer! I will consider the solution above but I
think I will get by in the beginning by hooking the components
rendered-attribute into a map containing booleans for the
hiding-of-components-part. I will also look into the Facelets to see if its
safe to use in production stuff. Fortunately the application I'm designing
is small enough to handle refactoring as I go along and learn more.

I tried to be clever(?) and add a backing-bean-bound dummy control at the
very end of the JSP page and then manipulate the components that had been
added to the tree earlier but It seems to be like you said that the
components are rendered as they go and not when the tree is complete :-/

BTW. Is it a feature of the JSF or the JavaBean specification that
rendered="#{securityBean.ACLMap.somepermission}" seems to result in true
when "somepermission" is not found in the map?

Re: Component manipulation in phase listener

Posted by Simon Kitching <sk...@obsidium.com>.
Nicklas Karlsson wrote:
>     Another approach would be to try and intercept stuff as each component
>     is rendered; for your purposes that's fine, yes? So how about this:
>     * Override the default RenderKit with a wrapper of your own.
>     * On each call to addRenderer(...), call:
>          super.addRenderer(new MyRendererWrapper(r));
>        so all the standard renderer objects have a custom wrapper around
>     them
>     * Your custom wrapper then intercepts encodeBegin(), and potentially
>        updates the component's "rendered" state before delegating on to the
>        real renderer.
> 
> 
> So if I understand you correctly, I should create a
> 
> public class MyKit extends HtmlRenderKitImpl that overrides addRenderer 
> with something like
>  
> public void addRenderer(String componentFamily, String rendererType, 
> Renderer renderer) {
>   super(componentFamily, rendererType, new MyRenderer(renderer));
> ...
> }
> 
> and then have a
> 
> public class MyRenderer extends Renderer {
>     private Render realRenderer;
> 
>     public MyRenderer(Renderer realRenderer) {
>         this.realRenderer = realRenderer;
>     }
> 
>     public void encodeBegin(FacesContext context, UIComponent component)
>         // Do magic with the component
>         realRenderer.encodeBegin(context, component);
>     }
> }

Yep, that's what I was suggesting.

> 
> Do I have to use super.calls for all other mehtods in Renderer also?

Well, not "super", but "realRenderer.something".

  Or
> did you mean I have to make a wrapper for each component class?
Nope, that wouldn't be practical. And I don't believe it is necessary; 
you want to do the same operation regardless of the actual component 
type, yes? So I think a single wrapper will work for all.

  And does
> that mean I would have to define them all in faces-config? What is the 
> way that results in a) minimal number of custom classes, b) minimal 
> number of additonal configuration in faces-config? Preferrably both ;-)

No, because you've overridden the renderKit you shouldn't need to make 
any other config changes; exactly the same standard renderers should be 
created and registered as when using the standard RenderKit, except that 
your custom one is wrapping them as they are created.

Actually, I believe my original email suggested overriding the 
Application rather than the RenderKit. But you might be right that the 
RenderKit is the correct thing to override to get this effect.

Please note that the approach I suggested is just *speculation*. I 
haven't done this myself as I haven't needed to. It just looks to me 
like an approach worth trying.

> 
> I also tried using Facelets for this as you suggested but was 
> unsuccessful. Tried both a phaselistener and by extending all the 
> mehtods in the facelet-viewhandler but was still unable to intercept :-/

Ah well.


Regards,

Simon


Re: Component manipulation in phase listener

Posted by Nicklas Karlsson <ni...@gmail.com>.
>
> Another approach would be to try and intercept stuff as each component
> is rendered; for your purposes that's fine, yes? So how about this:
> * Override the default RenderKit with a wrapper of your own.
> * On each call to addRenderer(...), call:
> super.addRenderer(new MyRendererWrapper(r));
> so all the standard renderer objects have a custom wrapper around them
> * Your custom wrapper then intercepts encodeBegin(), and potentially
> updates the component's "rendered" state before delegating on to the
> real renderer.


So if I understand you correctly, I should create a

public class MyKit extends HtmlRenderKitImpl that overrides addRenderer with
something like
public void addRenderer(String componentFamily, String rendererType,
Renderer renderer) {
super(componentFamily, rendererType, new MyRenderer(renderer));
...
}

and then have a

public class MyRenderer extends Renderer {
private Render realRenderer;

public MyRenderer(Renderer realRenderer) {
this.realRenderer = realRenderer;
}

public void encodeBegin(FacesContext context, UIComponent component)
// Do magic with the component
realRenderer.encodeBegin(context, component);
}
}

Do I have to use super.calls for all other mehtods in Renderer also? Or did
you mean I have to make a wrapper for each component class? And does that
mean I would have to define them all in faces-config? What is the way that
results in a) minimal number of custom classes, b) minimal number of
additonal configuration in faces-config? Preferrably both ;-)

I also tried using Facelets for this as you suggested but was unsuccessful.
Tried both a phaselistener and by extending all the mehtods in the
facelet-viewhandler but was still unable to intercept :-/

Re: Component manipulation in phase listener

Posted by Simon Kitching <sk...@obsidium.com>.
Nicklas Karlsson wrote:
> 
>     * the JSP page is executed. As each JSF tag is encountered, a
>        component object is created, then its properties set, then it is
>        added to the view tree, and then that component is immediately
>        rendered.
> 
> 
> 
> So I would basically have to compile my own version of the class that 
> does the
> adding of component to the tree and do the work there?
> 
> 

Well, what I was actually suggesting is that you might like to look into
using Facelets as the JSF layout technology rather than JSP. This
project seems to be quite popular; I haven't used it personally as the
project manager has basically mandated use of JSP for the project I'm
currently working on but from my brief investigations it seems much
nicer to work with than JSP - and in particular I believe it won't
suffer from the JSP-related problem I describe above because the entire
UIComponent tree is built before rendering on the first view of the page.

If you have to use JSP, then maybe you'll find this info useful:

It would be easy to intercept the creation of components; that's done by
the Application.createComponent method and you could fairly simply
provide your own Application wrapper. However that's no good, because
the component's ID is set after the component is created and you need
the id to do the filtering you want.

You could override Application.createComponent to always return a proxy
object generated by java.lang.reflect.Proxy, wrapping the real
UIComponent and intercepting the setId method. However that's horribly
inefficient and may not play well with saving/restoring the view state.

The JSF lifecycle is also easy to override, but that's no use in this
case, as the component is being both created and rendered within the
"render" phase of the lifecycle.

The point at which the component's id is assigned is
UIComponentTag.createComponentInstance, and the component is added to
the tree in UIComponentTag.findComponent. However there are no easy
hooks for customising this behaviour. It would be a maintenance
nightmare to actually use your own version of this class. Maybe the
myfaces developers would consider a patch to add a "component creation"
callback hook into this class for purposes like yours that want to
access every component as soon as it is added to its parent.

Adding is actually done via parentComponent.getChildren().add(child).
The getChildren method returns a special list type,
_ComponentChildrenList. However I can't imagine fiddling with this will
lead to any good. That seems to be a dead-end approach too.


Another approach would be to try and intercept stuff as each component
is rendered; for your purposes that's fine, yes? So how about this:
* Override the default RenderKit with a wrapper of your own.
* On each call to addRenderer(...), call:
     super.addRenderer(new MyRendererWrapper(r));
   so all the standard renderer objects have a custom wrapper around them
* Your custom wrapper then intercepts encodeBegin(), and potentially
   updates the component's "rendered" state before delegating on to the
   real renderer.


I can't think of anything else at the moment.

I hope some of this helps.


Regards,

Simon


Re: Component manipulation in phase listener

Posted by Nicklas Karlsson <ni...@gmail.com>.
> * the JSP page is executed. As each JSF tag is encountered, a
> component object is created, then its properties set, then it is
> added to the view tree, and then that component is immediately
> rendered.



So I would basically have to compile my own version of the class that does
the
adding of component to the tree and do the work there?

Re: Component manipulation in phase listener

Posted by Simon Kitching <sk...@obsidium.com>.
Nicklas Karlsson wrote:
> Hello,
> 
>   I'm a JSF-noob(tm) and fresh convert from Struts so there might be 
> something with the lifecycle I don't understund but...
> 
>   I'm trying to make a DB-configurable application using a phase 
> listener that iterates over all the components in the tree and applies 
> attributes, validators etc to them the id can be found in a map of 
> description objects (or hide the component if the user doesn't have 
> access to it)
> 
>   I can't however make the changes take effect when running the page for 
> the first time. I attached the listener to all phases and the debug code 
> shows that it is doing a setRendered(false) for a component but it still 
> shows up! When I navigate back to the page the component disappears. 
> Something with phases being skipped when there is nothing to restore? If 
> so, is there a way to get around this (by using some other "for every 
> page" method)?

Are you using JSP with JSF?

Assuming you are, the flow goes something like this:
* request received from browser
* "before restore view" phase listener executed
* JSF detects that no component tree currently exists for this view
* "after restore view" phase listener executed
* processing skips straight to the render phase (no component tree
   exists, so there are no components that need to fetch data from
   the posted request, and therefore no validation or model update or
   value-change processing can possibly occur).
* "before render" phase listener executed (still no component tree)
* the JSP page is executed. As each JSF tag is encountered, a
   component object is created, then its properties set, then it is
   added to the view tree, and then that component is immediately
   rendered.
* "after render" phase listener executed.

I therefore don't see any way your approach is going to work; on first 
view of a page there is no phase where all the components exist but they 
have not been rendered.

You might be able to get this approach working with Facelets instead of
JSP; Facelets always ensures that even on the first view of a page, the 
component tree is built first before rendering starts.

I can't for the moment think of any alternate approach that work work 
using JSP/JSF. You might want to check out the "role" features of the 
tomahawk components, though: this allows components to be disabled or 
not rendered depending upon whether a user is in a certain "role".


Regards,

Simon