This week's book giveaway is in the Mac OS forum.
We're giving away four copies of a choice of "Take Control of Upgrading to Yosemite" or "Take Control of Automating Your Mac" and have Joe Kissell on-line!
See this thread for details.
The moose likes JSF and the fly likes Change component attributes at RENDER_RESPONSE Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


JavaRanch » Java Forums » Java » JSF
Bookmark "Change component attributes at RENDER_RESPONSE" Watch "Change component attributes at RENDER_RESPONSE" New topic
Author

Change component attributes at RENDER_RESPONSE

Steven De Groote
Greenhorn

Joined: Nov 23, 2006
Posts: 16
Hi,

I have a regular JSF1.2 project with facelets, but I now have to add configurable field-level security.
It should be possible for a user to somewhere configure to make a field read-only.

Now, I have tried implementing this with a PhaseListener and doing this:

UIComponent comp = viewRoot.findComponent("testform:inputfield");
comp.getAttributes().put("style","display:none");

Unfortunately, this doesn't appear to work:
- in beforePhase, the viewroot is still not filled up, so I cannot find any component, hence I'm unable to set any attribute
- in afterPhase, viewroot is filled, the code runs perfectly, but the actual view (rendered xhtml) isn't changing.

I'm now out of ideas of how to do this.
Adding a disabled property to the fields seperately isn't appropriate either, as it would have to be done for every field, and then also for rendered or value as I would eventually want to change these as well.

I'm curious to know if any of you do have any ideas about this. I'd greatly appreciate the help.


Thank you,
Steven


Web developer / SCEA / Owner http://www.f1technical.net
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16095
    
  21

You cannot do true security on a web page, regardless of the framework. That's because the web page is in the hands of potentially unfriendly people who can hack it before returning the response.

However, you can keep people honest by tinkering with the page display. Just be sure that the final decisions on data modification are done on the server where the Bad Guys can't get in (we hope!).

To actually make a control read-only, use the "readonly" attribute on the associated input control. What you were attempting wouldn't actually make it component read-only, it would have made it invisible. And by invisible, I mean that it would actually be still part of the client-side page, and still read-write, just not visually "there".

To disable a control, use the "disabled" attribute. To hide it altogether, use the "rendered" attribute. Changing the display style can only be done via JavaScript client-side, and remember what I said about the security (or total lack thereof) of client-side data.

In any event, when using any of the above attributes, you usually have to do a page update if the attribute value changes. You can minimize the visual "hit" by doing partial page updates of the affected controls using AJAX.


Customer surveys are for companies who didn't pay proper attention to begin with.
Steven De Groote
Greenhorn

Joined: Nov 23, 2006
Posts: 16
Well I know what each attribute is for, that is not the problem really.
I do have a system where the admin can say, look on this page I now want this field disabled until I enable it again.

I have the system in place to store, and get these rules in a phaselistener to apply them on my page, but unforunately when I need them the component tree is not filled in yet. That really is my only - but apparently BIG - problem.

Maybe it's not possible to do such thing through a phaselistener, but then I want to know how it is. There must be a way...
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16095
    
  21

I'm afraid I don't understand why the perceived need for complexity. If you're thinking that JSF Views are something that are maintained via a full-time interactive 2-way connection, that's wrong. HTTP doesn't maintain a continual connection - it's a request/response protocol, and the only time there's an actual connection between client and server is when the client is sending down a request and the server is returning the response. The response is not something that's mutable over time based on application logic, it's formed as a unit and transmitted as a unit. At which time the client disconnects until it wants to initiate another request/response cycle. And under NO circumstances does HTTP support servers pushing out unsolicited responses. EVERY response is a result of a request.

In an AJAX environment, you can have dynamic page updating, and that can give the illusion of a continuous connection, but it's only an illusion. Each AJAX action is itself a mini request/response cycle. And since the JSF lifecycle is the same for AJAX requests as for full-page requests, that means that the same basic mechanisms and attributes apply.

Or, in short, a person's security role isn't going to change in the middle of rendering a response - a response is something that normally takes milliseconds to output - so using phase listeners and other esoterica won't help. All that's required is a backing bean, a little EL, and no JSF-specific code to speak of.

A person's security role MAY change BETWEEN request/response cycles (due to third-party actions), but HTTP can't update the view, since unsolicited responses are forbidden. Thus, the update to the UI occurs based on the bean's properties when the next request is made and a new response (View) is generated. That's also the case when the JSF action itself changes the role, although in that case, it's the security state properties that exist after the action method has modified them that apply.
Dennis Hopfer
Ranch Hand

Joined: Dec 02, 2010
Posts: 43
Seems to me he's saying he wants to modify a value of an HTML component before the page is displayed. More specifically, depending the page's state, how would one disable an input field using a backing bean?

The OP seems to be saying that he can't modify its visibility because he cannot find the component by querying the UIRootView because the component tree hasn't been built.

Now, how does he solve this problem? I'd say that's a pretty serious design problem if it cannot be overcome easily.

Dennis
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16095
    
  21

A Page is only rendered as a Response to a Request. Therefore, all modifications to HTML are done as Response actions. This usually means that an Action method is the place to modify the page structure if dynamic modification is required. In the case of a direct (GET) URL request, an action method has not been fired (unless you're using a helper such as PrettyFaces), so it's a bit more challenging, although a PostConstruct method can often be used for this purpose. Or, on older platforms, a one-time side effect of a property-get method.

A Page is a static object. It does not have State. About as close as it can get to having a State is to be the subject of running client-side scripts in which case all or part of the page DOM can be used as a stateholder for the script(s). However, core JSF doesn't generate any client-side scripts of its own. Anything like that is up to the application developer.
Dennis Hopfer
Ranch Hand

Joined: Dec 02, 2010
Posts: 43
Steven,

I'm no expert but you could try adding a hidden field to your UIComponent tree that indicates whether your field should be visible or not.

In Javascript you could have your text input field, using DOM, have its visibility be a boolean reference to the hidden field's value.

<pseudocode>

<body onload="setFieldStatuses()">

...

<input type="hidden" id="inputFieldID" value="hidden">

</pseudocode>

And then you could try and set it dynamically. Yep, it's a nasty workaround but..hope it helps,

Dennis
Dennis Hopfer
Ranch Hand

Joined: Dec 02, 2010
Posts: 43
Tim could you give an example how the @PostConstruct could be used here? Would it look like this:

@PostConstruct
public void setFieldVisibility()
{
UIComponent comp = viewRoot.findComponent("testform:inputfield");
comp.getAttributes().put("style","display:none");
}

And does @PostConstruct guarantee the component tree is built already and available? How does the bean work here? Is the bean bound to some component somewhere?

Thanks,

Dennis
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16095
    
  21

@PostConstruct is a generic POJO annotation for use by bean-building frameworks such as Spring and JSF. There's a JSR that describes it, but I don't remember which one and I'm too lazy to ask Google.

The sequence that the bean-builder goes through is:

1. Invoke a constructor, based on the framework bean definition (managed-bean if it's JSF. In which case the no-arguments constructor is invoked).

2. Inject properties based on the framework bean definition (managed-property elements, for JSF)

3. Invoke PostConstruct. On some versions of some platforms, this may require gimmicking up the container in order to have the annotation honored. Tomcat is one such container.

Note that in none of the above is the View mentioned. For one thing, PostConstruct can be used in non-web environments. For another, in JSF the Managed Beans are Models. They are constructed (thus triggering the above sequence) on-demand when a View needs them. Thus, the View is already in existence in its internal form as a component tree when the Models are constructed. Views don't reference beans. They reference properties within beans. In particular, they can inject view components (UIComponents) into beans using the binding attribute as compiled from the prototype View as the indication of what backing bean property the UI component is to be bound to (inject into).

The upshot of all this is that a lot of functionality has become available with relatively little effort required from the developer.
Dennis Hopfer
Ranch Hand

Joined: Dec 02, 2010
Posts: 43
I gotta say Tim while very knowledgeable you are a little hard to understand.

For the sake of simplicity let's just get these questions answered:

1) When @PostConstruct is called are we at the point where the entire UI tree has been built and can be queried? Your post says that UI components have been created by the time @PostConstruct is called but not necessarily that they've been inserted into the UI tree.

2) Are you saying that we should bind the backing bean to the view in order to make all this 'magic' happen?

Thanks,

Dennis
Steven De Groote
Greenhorn

Joined: Nov 23, 2006
Posts: 16
Although I haven't read anything wrong, there is no real solution to my problem. I have tried several things to get it working, but I believe I've hit a considerable JSF architectural problem which I'm trying to overcome.

I see there was some confusion about security and such. Basically, all I want to do it change the properties of a component.
For instance, I have a component <h:inputText id="text"> (and as such many of this kind on the same page).
Now, what I also have is a Java class, with one method: boolean isRendered(String formname, String fieldname)

The point is that I need to call this method for every single component on my page, so that I can control its (in this case) "rendered" property.
What I could do is add a rendered property on every element, and create a getter for every component.
I think though this is extremely useless coding, and I want a system that can change an attribute value without me having to set the property manually.

Hence my idea to loop every component before rendering, do something with them and afterwards do the rendering.
Problem is that the component tree isn't built at that time (see OP).

As for your suggestions with @PostConstruct, I also fail to see how that is ever going to work.
It is possible that I'm showing a page with a Session managed bean, and then I'm nowhere with the postconstruct I think.

Anyway, I'm out of ideas
Hope you guys still have some.

Steven
Tim Holloway
Saloon Keeper

Joined: Jun 25, 2001
Posts: 16095
    
  21

Hey, I can confuse experts! Even I get confused by me.

@PostConstruct has nothing to do with JSF or JSF views. It's all about that particular bean and it's a directive to whatever factory is building the bean that after the bean has been constructed and its managed properties have been set that the method tagged PostConstruct should be invoked. The only relation to JSF is that the factory that does all this is the JSF bean manager factory instead of something like the Spring Framework's bean factory.

You can setup a method that obtains the FacesContext and uses it to find the UIViewRoot, which can then be traversed. For each interesting component in the UIView tree, you can invoked that component's "setRendered" method.

The rub is when to call that traversal method. Generally, I'd call it from an action processor as a response to an action. If you're attempting to set the properties prior to page rendering, I recommend consulting the JSF lifecycle graph for suitable alternatives. PostConstruct only gets called once, right after the bean is constructed, so unless the bean is request scope, it's probably not the best choice. And if the bean is request scope, it's probably not worth all the effort to run the UIView tree. Less work just to manage the rendering properties using the basic POJO techniques.
Mehran Falak
Greenhorn

Joined: Dec 16, 2013
Posts: 4
Steven De Groote wrote:Although I haven't read anything wrong, there is no real solution to my problem. I have tried several things to get it working, but I believe I've hit a considerable JSF architectural problem which I'm trying to overcome.


Hi Steven,
i have same problem i try phase listener and view handler but they are not working
are you find any solution for change view state during initial request ?
 
GeeCON Prague 2014
 
subject: Change component attributes at RENDER_RESPONSE