I tried to use view parameters in conjunction with a request scoped bean. I noticed that the model does get updated and when the page is rendered, the bean properties are set and are rendered with <h:outputText value="myBean.myParameter />. My question is, about the post back. I was under the impression that the view parameter values were retained as long as you don't post to a different view. I expected that the property would be set in the bean during the action method call automatically, but in practice I found this is not the case. Can someone explain why this is the case. I searched all around and I found many articles discussing viewParams but I couldn't find an explanation of exactly what the effect is (and why) in each scope. If anyone knows of an article that documents this already can you point me to it. Thanks in advance...
I was under the impression that the view parameter values were retained as long as you don't post to a different view.
This is only true when you're using a view scoped bean because they are remembered in the bean. The view scoped bean lives as long as you're interacting with the same view. I think that you're confusing the request scope with the view scope, given your impression.
A request scoped bean is trashed by end of the HTTP request/response cycle. So when the webbrowser is finished loading the page, the request scoped backing bean is already trashed for long. When you submit a form on that page, a new request scoped bean will be created, the <f:viewParam> will check if the to-be-set request parameters are available and if not, then just skip it (or validate; if you have required="true" set on them). As long as you don't retain view parameters by using <f:param> inside command links/buttons of the form on that page, then those parameters will get lost on subsequent requests and thus also in any involved request scoped bean.
See also:
Communication in JSF 2.0
Related
I've looked at some other threads here and I think I have the general idea of how to use a SessionScoped bean for maintaining a user's logged in state. However, as I am relatively new to JSF 2, I am trying to figure out the best way to use the bean during login and on subsequent pages to render a header/footer (using same page template, but different menus/links depending on login state and non-logged in state).
I have a typical JSF2 login form that calls a backing bean (request scoped) login() method. All of that works, and I am going to tie it in to the container security soon as I have a little more time to work on and figure that part out (realms, roles, etc).
In the old pre-JSF (jsp/struts) days, upon login, I'd add an object, usually a user ID Long value that would be easy to replicate across a cluster of sessions and restore a full user object from. This kept the httpsession data minimal (one Long value per user), and regardless of what server a user was routed, I could determine they were logged in and pull up their user object and go from there.
With JSF, I am not sure the right way to do this. I have a SessionScoped bean with a Long userid property. Upon the login() method succeeding, I use the ExternalContext to add the attribute of the SessionScoped object, something like:
User user = loginBean.login(username, password);
Session session = new Session();
session.setUserid(user.getId());
externalContext.getSessionMap().put("usersession", session);
externalContext.redirect(originalURL);
The Session is the SessionScoped bean, and it's CDI name is usersession. I hope that is the right way to stick the bean into the HttpSession so it can be used on pages, etc with EL.
The first thing I am not sure of is because the bean is SessionScoped, do I need to put it into the session after creating it in the above code? Or is that done automatically upon creation for me since it's scoped as session?
The second question is.. being picky that I am, I don't want a Session object in the session until the user logs in, even if the userid is null. So on a xhtml page, if I have something like:
<h:panelGroup render="#{usersession.loggedin}"...>
Will that create and put the Session bean into the session the first page that uses that? Or will that Session object not be put into the HttpSession until I do so with my code above? My guess is, if it gets put into the session upon creation, then the use of it on any page will create it and stick it in the session. I am less bothered by this..the object with just the Long property is very little memory use per user on the system, but like I said, I am being picky and more so wanted to understand exactly when a SessionScoped object gets stored into the session.
Thanks.
The first thing I am not sure of is because the bean is SessionScoped, do I need to put it into the session after creating it in the above code? Or is that done automatically upon creation for me since it's scoped as session?
The managed bean is autocreated in the given scope if referenced for the first time in EL by #{beanName} while it's not in the scope yet. So, no, you don't need to do it yourself. Just inject it as managed property in your login bean.
#ManagedProperty("#{usersession}")
private Session session;
// ...
User user = loginBean.login(username, password);
session.setUserid(user.getId());
externalContext.redirect(originalURL);
Will that create and put the Session bean into the session the first page that uses that? Or will that Session object not be put into the HttpSession until I do so with my code above?
It will do that once you reference it for the first time in EL by #{beanName}.
Unrelated to the concrete question, another way is
User user = loginBean.login(username, password);
externalContext.getSessionMap().put("user", user);
externalContext.redirect(originalURL);
and check for the logged-in user as follows
<h:panelGroup rendered="#{not empty user}">
See also:
Performing user authentication in Java EE / JSF using j_security_check
I have a sample use case: I have an edit page that use GET parameter "id".
eg. edit?id=1
This edit page is backed by a ViewAccessScoped (CODI) Bean.
In this edit page, I have a datatable with links that link to the same "edit" page, but with another id. (eg. edit?id=2)
<h:link value="#{mecaPart.id}" outcome="edit" target="_blank">
<f:param name="id" value="#{mecaPart.id}" />
</h:link>
The problem, is that the window open correctly, but it is the same bean that is used! And so I am editing the same part...
I have placed a log in #PostConstruct, and it is the same bean reference that is called multiple times. (even with the new ID!)
My question, how can I tell JSF to create a new ViewAccessScoped backing bean when I click the link, and not re-use the actually used one?
Finally, I discovered that #ViewScoped CODI bean did not preserved the backing bean from page refresh. So, I have to use ViewAccessScoped.
According to Gerhard Petracek: http://os890.blogspot.fr/2011/08/scopes-view-scope-vs-view-access-scope.html
the view-scope of jsf2+ is bound to a concrete jsf page. that means: as soon as you navigate to a different page, the state gets lost. that's better than nothing, but not useful for a lot of use-cases. the main use-case which needs it are ajax-requests on a page and the data used by them aren't needed on other pages, but it's pretty easy to break it e.g. with a browser-refresh on a page which stores the data in a view-scoped bean and has no form with input components. (in a previous blog post i described how to use the infrastructure provided by codi to create a session based view-scope to overcome such disadvantages cause by storing view scoped beans as part of the tree-state.)
like with the view-scope view-access-scoped beans are available on a page, but they also exist for the next page. that means: they are forwarded to the next page and get destroyed autom. if they don't get used during the first request of the next page. that's e.g. useful for wizards. if you have a wizard page which doesn't use the bean or you have to support the possibility to interrupt a wizard, you can use the grouped-conversation scope (and even the window-scope) provided by codi. however, due to the powerful api of codi you can also destroy the scope manually at any time (if needed).
So, to solve the problem of opening a new tab with another "ID", I had to set "CODI Client Side WindowHandler", according to the CODI Wiki.
https://cwiki.apache.org/confluence/display/EXTCDI/JSF+WindowHandler
So I added:
<alternatives>
<class>org.apache.myfaces.extensions.cdi.jsf.impl.scope.conversation.ClientSideWindowHandler</class>
</alternatives>
To the file beans.xml, and I used #ViewAccessScoped. Everything is working smoothly now.
You can use #ViewScoped which also works in CODI
Please note: This question is about CDI scopes as we are using CDI scopes in the app and not JSF scopes.
1) Controller Bean (TestController.java) which is in RequestScoped (enterprise context) is called index_cut.xhtml, when we come for first time on this page.
2) On button “Load”, we load the following method to populate the sapFinancialPeriodList which works fine and displays the data
3) After changing the content on the page and submitting, the sapFinancialPeriodList appears as NULL in the following method –
Any suggestions?
Your bean is request scoped and you're loading the data model on action only instead of on (post)construction. When the HTTP response after the action which loaded the data is finished, then the bean is garbaged. The subsequent request would get a brand new instance of the bean with all properties set to default. However, as the same data model isn't been preserved during (post)construct, it remains empty.
In JSF2 you'd solve this with using #ViewScoped. This way the bean will live as long as you're interacting with the same view by postbacks (which return null or void).
In CDI you'd need to solve this using #ConversationScoped, which in turn requires some additional #Inject Conversation boilerplate, complete with begin() and end() calls at the right moments. For a concrete example, see also What scope to use in JSF 2.0 for Wizard pattern?.
An alternative is to pass the parameters responsible for creating the data model to the subsequent request via <f:param> in the command link/button as follows
<h:commandButton value="save" ...>
<f:param name="period" value="#{bean.period}" />
</h:commandButton>
and then recreate exactly the same data model in (post)constructor of the request scoped bean as follows
String period = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("period");
List<SapFinancialPeriod> sapFinancialPeriodList = someservice.list(period);
(the above is by the way nicer to solve with #ManagedProperty if you were using standard JSF; as far as I know CDI doesn't have an annotation which enables you to set a HTTP request parameter as a bean property)
See also:
How to choose the right bean scope?
Unrelated to the concrete problem, the upcoming JSF 2.2 solves this functional requirement in a nicer way using the new "Faces Flow" feature with the new #FlowScoped annotation and the new xmlns:j="http://java.sun.com/jsf/flow" tags.
Let's assume we got simple page that accepts one parameter:
<f:viewParam name="name" value="#{bean.name}"/>
When user goes to http://localhost/myapp/?name=Joe, then #{bean.name} is set to Joe.
Then if user goes to http://localhost/myapp/ or http://localhost/myapp/?something=Else, then #{bean.name} is still set to Joe, but I want it to be null. How this can be done?
Use the right managed bean scope for the data it holds. You've apparently put it in the session scope. Put the bean holding the parameter in the request or view scope instead of session scope and when using view scope, make sure that you navigate by a normal link or by a post-redirect-get when performing an action.
See also:
How to choose the right bean scope?
I moved from JSF 1.2 to JSF 2.0 and it seems I missed something during the switch. I have following scenario:
There is a button on one page with actionListener set to one managed bean's method which adds an object to request by calling FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("foo", fooObject);
Navigation is properly handled to other page where other managed bean is initialized.
The constructor of other managed bean tries to retrieve passed object from request by calling FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("foo"); and to initialize itself with received values.
Both managed beans are request scoped. I notice that constructor can't retrieve proper value from request because request map doesn't contain "foo" key.
What am I doing wrong? Is there a better way to do this?
Thanks in advance.
In step 2, if there is a redirect the initial request scope is lost as the redirect would result in another request.