Conversation scope unexpected behavior - java

I have a JSF page where users can enter their car into my database. The form has three input fields:
Manufacturer
Model
Registration
The Manufacturer and Model field are both autocompletion fields. In order to complete the Model field, I need to know what value the user selected in the Manufacturer field prior to submitting the form. To that end, I set up an AJAX call to inform the managed bean of the selected value:
<p:autoComplete id="manufacturer"
minQueryLength="3"
completeMethod="#{carController.complete}"
forceSelection="true">
<p:ajax event="itemSelect"
listener="#{carController.manufacturerSelected}" />
</p:autoComplete>
And in the managed bean:
public void manufacturerSelected(SelectEvent se) {
manufacturer = se.getObject().toString();
}
The autocomplete field and handler method for the model look about the same, with slighly different values.
To retain the manufacturer value across the multiple XHR requests, I have set the bean to ConversationScoped and begin the conversation in a #PostConstruct annotated method:
#Named
#ConversationScoped
public class CarController implements Serializable {
#Inject
private Conversation conversation;
#PostConstruct
private void init() {
conversation.begin();
}
What I would expect is the bean getting only instantiated once for the page because the conversation has not been ended yet, and retaining the value in the manufacturer field. This, however, does not hold true, and the bean is instantiated again for each XHR request, causing the manufacturer field to be null as well. Setting a breakpoint in the PostConstruct method revealed that it is in fact getting called and so is the manufacturerSelected method.
I suspect this has something to do with the fact that I am not manually propagating the conversation ID but the documentation says that this ID should automatically be propagated with any faces request. Is this in fact true, and, does that mean that XHR requests are not necessarily faces requests?
Edit: Setting breakpoints at various locations in the bean has revealed that each XHR request has a new bean (conversation.getId() keeps changing) so I am obviously not propagating the ID right. How would one propagate the ID with p:ajax and where can I get it with EL?

Since you're not really using a conversation (at least not in the example you give), why not use the view scope? This will do exactly what you want, without the overhead of having to propogate a conversation id.
Do note that for #ViewScoped to work, you have to change the bean to a JSF managed bean (remove #Named). This is due to a design bug/spec oversight in Java EE 6. If you want to keep using CDI, then there's a Java EE 6 extension from Seam 3 available that fixes this bug.

Yup. This is an issue with JSF 2 and Primefaces together not propagating the conversation id. You can try the below workaround. (This is for other who will end up here due to the same issue).
<p:autoComplete id="manufacturer"
minQueryLength="3"
completeMethod="#{carController.complete}"
forceSelection="true" onchange="updateManufacturer([{name:'cid',value:'#{javax.enterprise.context.conversation.id}'}])">
</p:autoComplete>
<p:remoteCommand name="updateManufacturer" action="#{carController.manufacturerSelected}"
process="manufacturer" />

Related

JSF2-Behaviour of viewParam on viewscoped bean after postback

I have a viewscoped bean wich receives parameters through f:viewparam and set values in the model using them. After a page postback, the parameters dissapear from the url but the model values setted by the initial params are retained somehow.
Is this a safe way of keeping parameters or should I rewrite the url on postback so it keeps the initial parameters subsequent calls?
How are the parameter retained? In the viewstate?
Here's what the f:viewParam documentation says:
[...] this tag causes a UIViewParameter to be attached as metadata for the current view. Because UIViewParameter extends UIInput all of the attributes and nested child content for any UIInput tags are valid on this tag as well.
This means that the <f:viewParam> value attribute will be updated with the request parameter, stored in the viewstate and re-set/revalidated when you submit the page again.
Arjan Tijms has the full story here: http://arjan-tijms.omnifaces.org/2011/07/stateless-vs-stateful-jsf-view.html
In short: using a #ViewScoped bean is perfectly fine, but if you use an expensive validator/converter on that value be aware that it will be called again on each postback from that page. If you want to avoid that, have a look at this article from BalusC, which explains o:viewParam, OmniFaces' stateless alternative.

is there any session bean (Java/ JSF managed bean and not browser session) which is different for each tab

What I want is different different session bean per tab/ window. I know session bean works but I want to create new sessionbean for new tab. Below is reason why I want new session bean.
Suppose on page1, I have list of users. On clicking user, I get details of user in page2.
Now suppose I have two tab. In both pages I have page1.xhtml.
On tab1, I clicked on User A. I see details of User A on page2.xhtml
Now I come on tab2.
On tab2, I clicked on User B. I see details of User B on page2.xhtml
Problem is here now. Now I come back to tab1 and refresh the page2.xhtml, I see User B details which is incorrect because earlier I was seeing User A details.
Hence what I want is new sessionbean per new tab/ window.
Is there any way to create new sessionbean per tab/ window? In primefaces or icefaces maybe?
I thought ViewScope was solution, but that was not. Referring BalusC article.
#ViewScoped: a bean in this scope lives as long as you're interacting with the same JSF view in the browser window/tab. It get created upon a HTTP request and get destroyed once you postback to a different view.
With SessionBean, I meant Java/ JSF managed session managed beans and not browser session (history).
If you are using CDI Conversation scope could help.
From the Java EE docs: ConversationScoped
These tutorials might help.
CDI Conversations Part 1
CDI Conversations Part 2
Hope this helps !!!
#ViewScoped is the solution to this problem, indeed. The problem looks to be that you're setting the UserX data (id, detail or something else) into session when navigating from page1.xhtml and then loading this session data in page2.xhtml. So, if this is your current situation, not even CDI #ConversationScoped will help you (since it uses cid query string param).
In order to solve this, you should make a GET request of your page2.xhtml and send the userId (or another useful parameter) to recover the details of your UserX.
More info:
How to choose the right bean scope?
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Note that you can encrypt the parameter (let's say, the ID) to add some security to your parameters. Please refer to the second proposed link in order to achieve this.

Perform action when jsf validation errors occur

Is it possible to perform some actions (some cleanup) if jsf validation errors occur?
Fields are validated with tags in xhtml, for example 'required="true"', 'f:validateRegex pattern="\d*"', 'f:validator validatorId="someValidator"'.
I need to set some property field of managed bean to null (when there are any failures on the page).
But if validation fails, then JSF goes to Render Response Phase and managed bean method is not invoked.
Listener (using f:actionListener tag) also is not invoked in that case.
Now I'm thinking about to replace xhtml validation by validation using bean annotations like #AssertTrue, #Size, etc.
Then in some of this validating methods it would be possible to make a cleanup:
#ManagedBean
class SomeBean {
...
#AssertTrue
public void isClenup() {
cleanup();
}
...
}
But it seems not a good solution to me. Also I noticed that several methods annotated with #AssertTrue are called in undefined order. Therefore switching from xhtml validation to bean annotations validation is getting not so easy.
Is it possible to define some order of calling methods annotated with #AssertTrue?
In fact I have quite ordinary task: there is a page with search functionality.
On search success with no errors the result should be shown, but if validation errors occur (during next search), then previous result should not be shown (but it was visible and that was a problem).
My plan was as follows: to check for validation failures in initialize() method using facesContext.isValidationFailed() and if it is true, then to hide (delete) previous search results:
public void initialize() {
FacesContext context = FacesContext.getCurrentInstance();
boolean validationFailed = context.isValidationFailed();
if(validationFailed) {
clearPreviousSearchResult();
}
}
But then I found out that validation using bean annotations (like #AssertTrue) does not set facesContext.validationFailed();!
I.e. after this
#AssertTrue(message = "Some error message")
public boolean isValidateSomeField() {
return validate(getSomeFieldValue());
}
you get context.isValidationFailed() == false when fails occur (though I expected true)
(xhtml validation or validator or f:validator do set facesContext.validationFailed() as expected)
Therefore it is necessary to set context failed manually:
#AssertTrue(message = "Some error message")
public boolean isValidateSomeField() {
if(!validate(getSomeFieldValue())) {
FacesContext.getCurrentInstance().validationFailed();
return false;
}
return true;
}
But due to How to check in after phase of Validations phase if validation has failed? I realized that the problem can be resolved much more easier! Just to wrap with couple of lines:
<h:panelGroup rendered="#{!facesContext.validationFailed}">
...block not to show if validation errors occur...
</h:panelGroup>
And no need to use annotations bean validation and some initialize() method!
If a validation exception is thrown during the JSF validation phase then none of the submitted form values will be applied to the model. Also none of the invoked events that would otherwise execute during the Invoke Application JSF phase will be called. You do notice however that Render will still occur, and any components that need to be rendered or updated will still do so.
In theory this should be sufficient for rolling back most any user submissions if any of those submissions were invalid, however there are a few edge cases where additional cleanup will need to occur. I urge you however to closely evaluate the design decisions that led you to this need in the first place, as it is possible that perhaps there is a better way to meet your business requirements without having to resort to this.
With that being said, I would perform a Pre Render event that will execute on every postback and check for certain validations and perform necessary business and presentation logic.
<f:event listener="#{managedBean.initialize()}" type="preRenderView" />

Persist user name and page name to database

I have a small JSF application and the scope of the pages are request. I would like to have a method in my JSF's managed bean for logging user_id and page name to database. When I added this method to the constructor of bean, details are getting logged for every method is being called due to the fact that scope of the page is request.
What is the best way to log the above details only once when user first access the pages after logging into my application? Idea is which all pages user visited for each logging.
What is the best way to achieve this?
My JSF version is 1.1.
Thanks
You can add a method to the bean and invoke it from the page when the page is loaded. For example
add this into the page:
<h:outputText value="#{fooBean.logUser}"/>
bean method:
public void logUser(){
//log user and page
}
This way the method is only invoked once the page is loaded. If you get a method not found
error, then another option is to use a hidden form hack. For example:
<body onload="document.getElementById('formId:buttonId').click()">
<h:form id="formId" style="display: none;">
<h:commandButton id="buttonId" value="submit" action="#{fooBean.logUser()}" />
</h:form>
...
</body>
So, you want to log into a database table all accesses of all users to the different pages of your site but, avoiding logging post-backs, right? (a post-back happens when a user performs an action in one of your pages and the URL that receives the action is the same than the one from where the action was launched - it's very common in JSF, specially on versions prior to 2.x)
So, basically, what you need is to recognize when a post-back happens and avoid logging under that condition. The ResponseStateManager will provide you with a isPostback(...) method that returns true when processing post-back requests:
FacesContext context = FacesContext.getCurrentInstance();
ResponseStateManager rsm = context.getRenderKit().getResponseStateManager();
if (!rsm.isPostback(context)) {
// do whatever you need here
}
By the way, I wouldn't recommend to put that code in the bean constructor at all as different JSF implementations may perform some bean lifecycle management tasks right after invoking the bean's constructor. If you think that logic should be part of the "bean initialization process" I suggest putting that code in a separate public method annotated with a #PostConstruct annotation.
However, if you want that logic to be run in every single access to the given pages, using a managed bean will force you to either copy & paste that logic in every bean or define an abstract/base class with that logic and make all your beans extend that one. I like to keep all my managed beans as close as possible to the POJO concept so, as an alternative, I would consider implementing the same logic but using a PhaseListener tied to the RENDER_RESPONSE phase. It will give you a under-the-hood (much cleaner) and versatile approach: if some day you want to log actions instead of renders then you tie your listener to the INVOKE_APPLICATION phase, etc.
P.D.: I hope you are using some kind of cache or 'batching' technique to log those records to your database, otherwise you will have a noticeable performance impact when too many users are surfing your site. Also, in my own opinion, you should update your JSF version to, at least, 1.2, and you'll avoid some annoying bugs or unexpected behaviour...

JSF: How to redirect a user to a another page according to the value of a specific FacesContext session attribute

In my JSF application, I need to redirect the user from Page A to page B if a session attribute such as userRole has a value of "contributor", and to a page C, for example, if it is "author".
I'm told that I have to implement a page listener, or a session listener perhaps. Although I understand writing a listener class is quite straightforward and standard, I don't know how to set it up on the JSF page itself (to listen to the session).
Anybody?
A session listener (HttpSessionListener) is unsuitable since it doesn't have a reference to the current HTTP request/response, which are mandatory in order to change the request/response destination.
Use a filter. To learn more about filters, check our servlet-filters tag info page. Note that session scoped JSF managed beans are by itself stored as HttpSession attribute with the managed bean name as key. You could access them in doFilter() method as follows:
Bean bean = (Bean) ((HttpServletRequest) request).getSession().getAttribute("bean");
Or when it's to be determined based on a POST action, just return a different outcome in the managed bean action method. Then just make use of (implicit) JSF navigation. Pseudo:
public String submit() {
if (user is contributor) return "pageB";
if (user is author) return "pageC";
return "pageA";
}

Categories

Resources