We have an application (Mojarra 2.3) where a user can change a global filter of the data it sees. When that happens, I want to keep the session, but invalidate the active views (which are server side).
I found this question which enables you to count the number of views: How do I count the number of views in a user's JSF session (JSF 2.2)?
Based on that I figured I could remove the attribute in which the views are stored. I came up with this method:
public static void invalidateViews() {
final HttpSession session = Faces.getSession();
List.of("com.sun.faces.application.view.activeViewContexts",
"com.sun.faces.application.view.activeViewMaps",
"com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap",
"org.jboss.weld.context.ConversationContext.conversations")
.forEach(session::removeAttribute);
Faces.redirect(Faces.getRequest().getRequestURL().toString());
}
The com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap was mentioned in the linked question's answer. I kind of guessed that it would not hurt to remove the other attributes. The answer also mentions that it is Mojarra only.
It seems to work so far, but I would like to ask this: should one clear server side views like this? And, if so, how can I support MyFaces as well?
It seems to work so far, but I would like to ask this: should one clear server side views like this?
Given that there is no standard API call for this, this is about the best you can do, yes. I have during the JSF 2.3 work requested for a more specific variant of this functionality to exist in the standard API because I needed to be able to destroy view scoped beans associated with a specific JSF view state (for the OmniFaces view scope unload functionality, see Hacks#removeViewState()). But this has unfortunately not yet been fleshed out because of difficulties with Portlets.
And, if so, how can I support MyFaces as well?
As seen in the OmniFaces Hacks helper class, the session attribute key for MyFaces 2.x is org.apache.myfaces.application.viewstate.ServerSideStateCacheImpl.SERIALIZED_VIEW and 4.x org.apache.myfaces.application.viewstate.StateCacheServerSide.SERIALIZED_VIEW.
By the way,
Faces.redirect(Faces.getRequest().getRequestURL().toString());
this is shorter:
Faces.refreshWithQueryString();
Related
I am creating a web application using JSF 2.2.20 in which I am implementing a "kinda wizard" flow which lets the user filling input fields and go back and forth the view pages through navigation. I am using a single bean for all these views.
Let's say I have views A.xhtml, B.xhtml, C.xhtml and D.xhtml, all managed by the same bean MyBean.java
I want my application to be "browser tab scoped", which means that
I do not want my bean's data be re-instantiated after every HTTP Request as it happens with #RequestScoped beans or after view changing as it happens with #ViewScoped, I want the data of my bean to be kept between view changes and redirections so the user can go back and forth between pages without losing the data he has already given.
I do not want to use the #SessionScoped scope since each time the user opens a new tab I want the bean to be re-instantiated starting from page "A.xhtml.
Is there any built-in way to achieve the scenario described above using the current JSF version? In case there is not any, could you please propose any workarounds?
Thanks in advance!
I think #ViewScoped is what you are looking for, but it depends on your exact usage.
Couple of notes:
Use javax.faces.view.ViewScoped. Don't use the deprecated managed bean annotation as it works differently.
#ViewScoped works by storing the beans in the view. So each time you load the page you get a view and a viewId that corresponds to that view. So effectively each load of the page (could be read as 'each browser tab') gets its own bean.
#ViewScoped is a passivating scope. That means your beans, and their injected Dependencies, do need to be Serializable.
Use a recent, up-to-date version of your app server, or if you bring in MyFaces manually, use the latest release. I found a number of older versions implementations buggy 5+ years ago, but it seems to work flawlessly now.
If there is a Page Navigation occurring, you probably want to use FlowScoped. This is a multi-page bean that stays alive until you end the 'flow'.
If neither of these two work, you can always implement your own scope which is surprisingly easy with CDI.
I have a internet shop application. I wanted to improve "removing from the cart" feature. I don't want to ask an user if he wants to "really remove" the position from the cart, instead I'd like to allow him cancel the operation.
Currently when user clicks "X" button at the product in the cart, row of table changes to "removed, click to cancel". In the back there is a list of CartPosition objects every with flag removed. Removed product is still on list, but the flag is set to true. It makes product still visible in proper position on the list, but on the view side I can render it another way. When user clicks another "X", previous one is premamently removed from the list and new one is marked as removed.
Shopping cart is session scoped CDI bean. It means, when I mark some position removed and go somewhere else, then go back to the cart, I'll still have one row marked as removed. I just don't want this.
I thought about changing the bean to JSF one, then create another view scoped which could keep record marked as removed and check if it's empty, if so I could remove the one from list. I have no idea if it's going to work. I'll try this approach, but I'd rather keep my beans managed by CDI.
Is there a way to handle this without implementing my above idea?
EDIT: #BalusC, your assumptions are wrong. You should rather suppose, that I don't exactly know how can I mix CDI and JSF. I actually use JSF 2.2, what means that javax.faces.view.ViewScoped annotation is available for me. Anyway I still don't understand the idea. I understood, that I have to rely on some ViewScoped bean. It's clear, but how it has to be composed. Can you explain your idea? I tried few things, but it looked it didn't work correctly. For example my #PreDestroy method of ViewScoped bean was never called and it's constructed lazily only when I try to use it.
Do the actual remove job in #PreDestroy annotated method of the session or view scoped bean.
#PreDestroy
public void destroy() {
// Do the actual remove job here.
}
Given that your question implies that you don't have CDI based javax.faces.view.ViewScoped available, which is new since JSF 2.2, this in turn suggests that you're still on JSF 2.0/2.1 and thus only have JSF based javax.faces.bean.ViewScoped at hands, then it should be noted that its #PreDestroy is broken in several circumstances. You'd then best do the #PreDestroy in the CDI based #SessionScoped bean. Alternatively, you could use OmniFaces #ViewScoped which has fixed and improved the #PreDestroy of a #ViewScoped bean in several ways. It even gets invoked on window unload.
As far as I understood your problem is the session. One way to solve the problem is to set the list of removed products in a request attribute and remove it from the session bean so that on the next request they will disappear.
Hope that helps
I am working on a JSF application which is supposed to support a big scale of users logged in at the same time. And when we tried with our stress testing, we have observed that a large portion of CPU time is spent on rebuilding and traversing through the component tree.
My first thought was to try to make specific parts of the page stateless and thus be excluded from the component tree. But if I wrap a form element with the f:view being marked as transient:
<f:view transient="true">
<h:form>
....
</h:form>
</f:view>
, all my other forms on the page are also stateless (the hidden input field that is supposed to hold the state has for 'value' attribute value 'stateless': ).
Is there a way to make only specific forms on the page stateless, or the whole page can be either stateless or stateful?
Thanks for any kind of help!
EDIT:
For implementation we are using Mojarra 2.2.7, along with Primefaces 4.0 as a component library and Omnifaces 1.7 for some utility functionalities.
Based on what Balusc has said on this link, applying the transient on a view tag will make the entire view (i.e page) stateless which makes sense because setting transient to true calls the setTransient() on the UIViewRoot object. This can not be accomplished with your current setup. I'm not sure if there is a hack or work around to achieve a single page with multiple states some alternative way.
Let's say I've got a register page & a register confirm page. I enter user
details into the register page, navigate to the register confirm page where
I can return back to the register page if there are any mistakes.
I'm going to use view parameters to make the registration data available
from the register page to the confirm page, and vice versa.
Supposing there are 20 items of data to be moving from page to page, that's
a lot of view parameters and a lot of setPropertyActionListeners, especially
as all the data is going to end up nicely packaged in a User object.
So what I want to do is input the data on the register page into the
properties of a User record and send a reference to it to the register
confirm page. What gave me an idea was seeing the BalusC WeakHashMap
converter. This is a JSF converter which has a static weak hash map and
generates a uuid as the value for a map entry and the object reference as
the key. So by specifying this as a converter for f:viewParam you send
the uuid in the query string.
This works fine. The issue I have is that on the register page I have to
get an instance of a User class with new. Then I can do:
<h:inputText value="#{bean.user.firstname}"/>
(etc...), and pass the user instance as a view parameter. It works fine from
the register to the confirm page. The issue is that when I perform the
reverse, sending the user reference back to the register page from the
confirm page I absolutely cannot prevent the register page backing bean
from re-instantiating the user object, after the setter has been called
as a result of the view parameter.
So the converter does it's job and retrieves the User object from the
hash map, calls setUser() in the backing bean, and then I see the
constructor for the User class firing.
I've tried calling new User() from the bean constructor, in #PostConstruct,
in a preRenderView (also checking if an ajax request), but nothing I try
prevents the work of the view parameter from getting wiped out if new is
involved. I'm sure there's a simple solution but I just can't see it right
now.
I'd be grateful for any suggestions for how to solve this problem.
The issue I have is that on the register page I have to get an instance of a User class with new.
So what code is initially creating this new User instance then? If you do this in the preRenderView handler, then you can simply check for null, can't you?
If the view parameter and converter haven't done their job, user would still be null and you create a new instance. The bean constructor and #PostConstruct won't do you any good here, since they both run before the view parameter does its thing, but the preRenderView event is guaranteed to run after it.
#ManagedBean
public class Bean {
private User user;
public void onPreRenderView() {
if (user == null) {
user = new User();
}
}
}
(Something to additionally consider is that the conversation scope already does exactly what you're trying to do here. This is part of CDI not JSF, but if you're running in a Java EE 6 Web Profile compliant AS (JBoss AS 6 or 7, Glassfish V3, Resin 4, ...) you already have it. Otherwise it's just an extra jar.)
After several attempts over more than a year to find a solid long term solution
to this problem, at last! I've found one. The solution comes in the form of the
Apache Myfaces CDI extensions project, aka Myfaces CODI.
This provides additional scopes such as the #ViewAccessScoped which ensures that
if a bean is referenced by a page then it is available for that page. Also
provided is support for conversation groups. In the scenario where I want to
pass an object reference from a register page to a register confirm page, the
confirm page can just access the registerView bean directly on the next request.
Alternatively you can #Inject one bean into another and access it on the next
request, or use f:setPropertyActionListener from the source page.
Myfaces CODI works fine with Mojarra and also with ajaxified component libraries
such as primefaces. The concept is similar to what is provided by Jboss Seam,
though I've found the additional scope support to be better thought out and I've
tested this on glassfish 3.1.1 with no problems.
If you're using #ManagedBean and scope annotations from the javax.faces.bean
package in your code, codi intercepts these annotations and uses it's own
CDI based versions, so you can convert to CDI simply by adding codi as a
dependency to your project and not changing any code.
For me this is like moving from black and white TV to colour TV, I wish I'd
found this stuff sooner.
CODI documentation
We are using JSF in our project (im pretty new to it) were every page have a back bean Java file.
In order to move (redirect) from one page to another, i need to put all the parameters (search criteria) in the request scope before redirecting and then retrieve it back in the next page constructor. When you have few pages deep and you want to come back to the top, it becomes really annoying to maintain.
For example, if i have page 1 with advanced search filters, which redirects to page 2, depending on the chosen item, and from page 2, you get another list were you can go to page 3 for details. Now each time i need to put all the params in the request scope/read them again, store them in hidden fields and get them back.
Whats exactly wrong with this method and whats a better way to do it in JSF?
EDIT: the environment is IBM Rational Application Developer (RAD), which have its own JSF implementation. Not sure if that makes a difference.
Putting request scoped data in session scope will bite you (very) hard if you're going to open the same page in multiple windows/tabs. Only use the session scope if the data itself is also really session scoped (excellent examples are the "logged-in user" and the "shopping cart", you want it to be exactly the same throughout the entire session). Again, don't put request scoped data in the session scope. It hurts both you and the enduser.
Just design your beans smart (it makes no sense to have different beans containing the same data) and make use of h:inputHidden where needed, if necessary in combination with managed property injection. It's indeed a bit a pain to code and maintain. You can on the other hand also just grab Tomahawk <t:saveState> if the to-be-passed data is actually as big as a "whole" managed bean. It costs only a single line in the JSF page and has always been of great assistance.
*For example, if i have page 1 with advanced search filters, which redirects to page 2, depending on the chosen item, and from page 2, you get another list were you can go to page 3 for details. Now each time i need to put all the params in the request scope/read them again, store them in hidden fields and get them back.
Whats exactly wrong with this method and whats a better way to do it in JSF?*
There's nothing wrong with this method. Maybe you coded it the wrong way which caused that it looks unnecessarily overcomplicated. I can't tell much as long as you don't post details about the code used.
As per your edit:
EDIT: the environment is IBM Rational Application Developer (RAD), which have its own JSF implementation. Not sure if that makes a difference.
This is not true. IBM doesn't have any JSF implementation. It has just a component library (the poorly maintained hx prefixed components, also known as "Faces Client Framework"). WSAD/RAD ships with Sun JSF RI (Mojarra) as standard JSF implementation, although it's usually a heavily outdated version. Ensure that you keep it updated.
I'm only starting out with JSF too to be honest, but I thought you can save managed beans in the session scope, thus being able to access the bean on each request? You can also save the state client-side avoiding nastiness about session stickyness and stuff.
So you could save the data you are currently passing as request parameters in a session-scoped managed bean, and it will be available to any requests in that user's session, destroyed when the session times out or is deliberately invalidated (say on user logout).
I don't think JSF currently supports conversation state which I think might be the exact solution to your problem, maybe a session scoped managed bean would be the pragmatic solution?
Make your managed-bean session scoped.
If you are using MyFaces you can use PageFlowScope. If using Seam then use Conversation scope.
If pageflowscope or conversation scope is not available, then use session scoped beans. In addition you can use PhaseListener to initialize or execute specific methods before the page gets called. In you case if the flow is page1 -> page2 -> page3, then initialize the session scoped bean in PhaseListener if page1 gets called.
I'll update with more info if you need.