JSF 2.1 View scoped managed bean re-created on every refresh - java

So i have a problem and i kindly need any info on how to resolve this.
We're using JSF 2.1 on JBoss 7.1 and we're using view scoped beans which have tables related to that view. The object represented as a row in that table is pretty big.
On every refresh of those views, a new instance of that bean is created.
To verify that this is happening, i have created a demo example:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:body>
<h:outputText value="#{viewScopedBean.i}" />
</h:body>
</html>
this template is then linked to a bean defined like this:
#ManagedBean
#ViewScoped
public class ViewScopedBean {
private int i = 0;
#PostConstruct
public void init(){
System.out.println("Init - " + i);
}
#PreDestroy
public void dest(){
System.out.println("Destroy - " + i);
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
Every time i refresh the view using the browser refresh button or by simply pressing enter in the browser address field, i clearly see the #PostConstruct method invocation.
If I leave the app alive for a very long time i see no #PreDestroy methods being called, and taking a heap dump shows me that ViewScopedBean has the same number of instances as the number i have reloaded the view, and the appear to remain on the heap even if i destroy the session.
This is a huge problem for me because if 500 users reloads the view with that large table, JBoss dies because it's heap space is full.
Is this the designed behavior of #ViewScoped beans or am i doing something wrong?

View scoped beans live only as long as a user interacts with a current view by returning postbacks to the same view (by returning null/void from UICommand action methods). Returning a current view id from an action method, firing a get request to the same view, refreshing the page, manually entering URL in browser's address bar and the events like these all cause the view to be recreated. Thus, you see view scoped beans reinstantiated on every such action.

ViewScoped beans are created every time the view created. So there it is.
But you complain that if 500 users come to the page JBoss will die. The only scope you can use to prevent that is ApplicationScope. But that is a bad idea.
Everybody will recommend you to use the narrowest scope.
So I think the solution to your problem is not another scope but a "lazy load" algorithm. You should load the table page by page. I don't think everybody will need the whole table at one read.
Here is a good description of scopes.
ManagedBeanScopes
If you want the bean to be destroyed you should choose requestScope. Because viewscoped beans stored in the session.

Related

JSF Binding component update before actionListener is called [duplicate]

I am using datatable on page and using binding attribute to bind it to my backing bean. This is my code :-
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.prime.com.tr/ui">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form prependId="false">
<h:dataTable var="item" value="#{testBean.stringCollection}" binding="#{testBean.dataTable}">
<h:column>
<h:outputText value="#{item}"/>
</h:column>
<h:column>
<h:commandButton value="Click" actionListener="#{testBean.action}"/>
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
This is my bean :-
package managedBeans;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.html.HtmlDataTable;
#ManagedBean(name="testBean")
#ViewScoped
public class testBean implements Serializable {
private List<String> stringCollection;
public List<String> getStringCollection() {
return stringCollection;
}
public void setStringCollection(List<String> stringCollection) {
this.stringCollection = stringCollection;
}
private HtmlDataTable dataTable;
public HtmlDataTable getDataTable() {
return dataTable;
}
public void setDataTable(HtmlDataTable dataTable) {
this.dataTable = dataTable;
}
#PostConstruct
public void init(){
System.out.println("Post Construct fired!!");
stringCollection = new ArrayList<String>();
stringCollection.add("a");
stringCollection.add("b");
stringCollection.add("c");
}
public void action(){
System.out.println("Clicked!!");
}
}
Please tell me why is the #PostConstruct firing each and every time i click on button? It should fire only once as long as i am on same page beacause my bean is #ViewScoped. Further, if i remove the binding attribute then everything works fine and #PostConstruct callback fires only once. Then why every time when i use binding attribute? I need binding attribute and want to perform initialisation tasks like fetching data from webservice, etc only once. What should i do? Where should i write my initialisation task?
Interesting, when you're using component binding on a view scoped bean, the view scope breaks.
I am not sure if that is a bug in JSF2, I would have to read the entire JSF2 specification first. As far now your best bet is to drop the component binding for now and pass the selected item via new EL 2.2 method argument syntax:
<h:dataTable var="item" value="#{testBean.stringCollection}">
<h:column>
<h:outputText value="#{item}"/>
</h:column>
<h:column>
<h:commandButton value="Click" action="#{testBean.action(item)}"/>
</h:column>
</h:dataTable>
See also:
How can I pass selected row to commandLink inside dataTable?
Invoke direct methods or methods with arguments / variables / parameters in EL
Benefits and pitfalls of #ViewScoped
Update (Dec 2012): this is indeed a bug in JSF2. It's a chicken-egg issue. The view scoped beans are stored in the JSF view state. So the view scoped beans are only available after restore view phase. However, the binding attribute runs during restore view phase, while the view scoped beans are not available yet. This causes creation of a brand new view scoped bean instance, which is then later replaced by the real view scoped bean which was stored in the restored JSF view state.
This is reported as JSF issue 1492 and JSF spec isssue 787 which will be fixed for JSF 2.2. Until then, your best bet is to use binding on request scoped beans exclusively, or to look for alternate ways for the particular functional requirement.
Update (Mar 2015): The JSF 2.2 fix was backported to Mojarra 2.1.18. So if you're still using JSF 2.0/2.1, you'd best upgrade to at least that version. See also a.o. What is component binding in JSF? When it is preferred to be used? and JSTL in JSF2 Facelets... makes sense?
As other said, I would say that the best thing to do is to drop component binding (you don't need it here).
But I would add that you can achieve the same as you're trying to do in a more object-oriented fashion by using action parameters, like this:
<h:commandButton value="Click" action="#{testBean.action(item)}"/>
... and in your java code:
public void action(Item item){
System.out.println("Clicked!!" + item);
}
If you have a viewscoped bean and if you want to retain values that were entered on the form or don't want postconstruct fired, you should return null from your action method.
If you return some outcome (e.g. invalid) and then point the invalid outcome to the same page using faces-config.xml, then the viewscoped bean gets recreated and thus it causes postconstruct to fire again.
Other solution:
Binding the HtmlDataTable in a request scope bean.
Inject this request scope bean in the view scope bean.
JBoss Seam use this solution for binding JSF componentes to a conversation scope component.
The balusc's answer helped me a lot, i would like to say that i had that bug with mojarra version 2.1.7, i am currently using 2.1.29-01 released in january-2015 and this bug is fixed, my problem was binding a tabview to a viewscoped bean. With this version I dont have that bug and binding and postconstruct is working fine.
I use Jboss 5.2 and i have to use mojarra 2.1.x so i hope this answer help other people in the same situation.
http://mvnrepository.com/artifact/com.sun.faces/jsf-api/2.1.29-01
http://mvnrepository.com/artifact/com.sun.faces/jsf-impl/2.1.29-01

JSF/Primefaces - CommandButton: open new browser tab without ViewScoped bean getting killed

I'm trying to open a new browser tab with a JSF view (in a portlet, deployed in Liferay) from within a view backed by a ViewScoped bean. Using a normal action redirect kills the bean.
I've tried the method provided here and here, but unfortunately without success.
The button looks more or less like this:
<p:commandButton value="#{msg.label}" onclick="target='_blank'"
action="#{sessionScopedBean.action(param)}" ajax="false" />
Moving the target='_blank' to the form attribute did not help. I've tried both returning null and void with no success. Changing ajax to true broke the navigation, didn't open a new tab but also did not kill the ViewScoped bean.
The action method content looks like this:
public void action(String param) throws IOException {
//some business logic
FacesContext.getCurrentInstance().getExternalContext().redirect("viewName.xhtml");
}
The view does not contain tag handlers like <c:if test="..."> or <ui:include src="...">. It did contain a <ui:repeat id="..." value="#{viewScopedBean.collection}"
var="..." varStatus="..."> tag, but removing it changed noting.
The form is enclosed in <ui:composition> and <ui:define> tags.
The view I redirect to has no connection with the ViewScoped bean. Any ideas? :)
The view scope broke because you're with the redirect action basically instructing the client to fire a brand new GET request on the given URL. You should instead be returning null or void and conditionally render the results in the same view.
See also:
How to choose the right bean scope?
The solution was already given in the links you found: put the data of interest in the flash scope before redirect and obtain them from the flash scope in the bean associated with target view. If this isn't working for you for some reason, an alternative would be to generate an unique key (java.util.UUID maybe?) and store it in the session scope as key associated with some data you'd like to retain in the redirected request,
String key = UUID.randomUUID().toString();
externalContext.getSessionMap().put(key, data);
and then pass that key along as request parameter in the redirect URL
externalContext.redirect("nextview.xhtml?key=" + key);
so that you can in the postconstruct of the bean associated with the target view obtain the data:
String key = externalContext.getRequestParameterMap().get("key");
Data data = (Data) externalContext.getSessionMap().remove(key);
// ...

Linking JSF inputText with backing bean's field without showing its value

I have backing bean like this:
#ManagedBean
#SessionScoped
public class TestBean {
private String testString;
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
}
And my xhtml page pretty simple too:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<h:head></h:head>
<h:body>
<h:form>
<h:inputText value="#{testBean.testString}"/>
<h:commandButton action="#{testController.testAction}"/>
</h:form>
</h:body>
</html>
Everything I want - to render my h:inputText element without value (empty).
I'm new to JSF, so, could you help me?
With best regards!
UPD!
It's simplified code, I'm using testString in other places and testString have value, which I want to hide! And I want to keep this value.
Provided that it's really a request/view scoped bean, you're likely victim of browser's builtin autocomplete/autofill feature. You can turn it off by adding autocomplete="off" to the input component in question.
<h:inputText ... autocomplete="off" />
Note again that it's not JSF who has filled the inputs, but the webbrowser itself. Clear the browser cache and you'll see that the browser won't do it anymore. Depending on browser make/version you can also reconfigure it to autocomplete a bit less eagerly.
Update: as per your question update, your bean turns out to be session scoped. This is not the normal scope for request/view based forms. A session scoped bean instance is shared across all browser windows/tabs (read: all requests/views) in the same HTTP session. You usually store only the logged-in user and its preferences (language, etc) in the session. You will only get a brand new instance when you shutdown and restart the entire browser, or use a different browser/machine.
Change it to be request or view scoped. In this particular simple example, the request scope should suffice:
#ManagedBean
#RequestScoped
See also:
How to choose the right bean scope?
Update 2 based on the comment,
Oh, you right, it's better for me to use #RequestScoped. But it doesn't resolve my problem - I want to keep this value, but I don;t want to show it in textInput. This value is important in context of request-response cycle.
the concrete functional requirement is now much more clear (in future questions, please pay attention to that while preparing the question, I had no idea that you was initially asking it like that). In that case, use a view scoped bean with 2 properties like this:
#ManagedBean
#ViewScoped
public class TestBean {
private String testString;
private String savedTestString;
public void testAction() {
savedTestString = testString;
testString = null;
}
// ...
}
You can alternatively also store it in the database or a property of an injected managed bean which is in turn actually in the session scope, for example.
You should bind the input text to some other field in your backing bean. And if you want to use that field for yourtestString, copy the entered value to testString in the testAction method.
<h:form>
<h:inputText value="#{testBean.copyTestString}"/>
<h:commandButton action="#{testController.testAction}"/>
</h:form>
public String testAction()
{
testString = copyTestString;
return "destinationPage";
}
Some Browsers ignore autocomplete - it can help to put autocomplete in form tag:
<h:form autocomplete="off">

Dynamic ui:include [duplicate]

This question already has answers here:
How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)
(3 answers)
Closed 5 years ago.
I wrote this question:
https://stackoverflow.com/questions/8589315/jsf2-dynamic-template
but BalusC and casperOne told that i wrote it bad so I try to explain better my problem.
As I wrote, I have my project in this structure:
in web root 3 xhtml pages: index, include and welcome;
all others xhtml pages in a subfolder into WEB-INF called jsf.
I suppose that it is a good thing, but I create all pages using the netbeans' wizard "New JSF Pages From Entity Classes..." and for using this structure with this wizard, I can't link directly the xhtml pages saved into jsf forlder, as created by wizard, and I created the include.xhtml and modify all methods for redirecting to this page, as I will explain later, the include.xhtml contains only this code:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
</h:head>
<h:body>
Utente connesso:<h:outputText value="#{userBean.cognome}"/>
<h:outputText value="#{userBean.nome}"/>
<br/&gr;
<ui:include src="#{logicBean.pageIncluded}"/>
</h:body>
</html>
As I said, I modified all methods to call action method in LogicBean that contains the setPageIncluded and return "include.xhtml" something like this:
PageController.java:
public void prepareList() {
recreateModel();
LogicBean l = (LogicBean) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("logicBean");
l.action(url+"List.xhtml");
}
LogicBean.java:
public String action(String value) {
setPageIncluded(value);
return "include";
}
Now this code works quite well and I can navigate from to all pages.
There is only a problem, the bean LogicBean is stored into the session!
This means that if I tried to open a new windows for navigate different part of the project in the same time I can't do that because LogicBean can contains only a single value of pageInclude!
I tried to use the ViewScope but or I don't understand how it works, and in this case it isn't useful, or I wrong something and it doesn't work properly!
Some one can help me?
I found this question that seems could help me:
dynamic ui:include with el-expression?
but I don't know if it could help and how modify the c:forEach and action method for using it for my situation!
I hope that this time I explain better my problem and, if it so, I thank you for help!
You use a JEE6 certified server, so you can use CDI for bean management. There is a CDI extension library called MyFaces CODI that has a Window scope bundle in it and you can use it to scope your beans instead of using session scope. This will solve your problem with the bean scoping.
Home page - http://myfaces.apache.org/extensions/cdi/

Component attributes doesn't set after "reRender" on AJAX request

Currently i'm working on some complex web front-end and implement it using:
JSF 1.2
Facelets 1.1.15
RichFaces 3.3.3.Final
I have created a custom JSF component which enables validation of inputText fields using pure JavaScript. This component have only one attribute: type. This attribute is responsible for validation algorithm which will be applied at time when user presses a keyboard key.
At restoreView phase when initial view is created this attribute is set by JSF (actually by Facelets). This means that i have a component class with setter and getter for attribute 'type'. And a 'type' setter called with value specified in xhtml document.
Component object is recreated each time at restoreView phase if i specify them in reRender attribute. But when it is recreated my required attribute type is not set.
It's simply creates new component objects... and it's all. May be i don't understand something and this is normal behavior, but how to get attribute values in this case?
Code:
Simple test page:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:u="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:a="http://richfaces.org/a4j"
xmlns:r="http://richfaces.org/rich"
xmlns:v="http://nobodyhere.ru/jsf/validation">
<head>
<title>Test Page</title>
</head>
<body>
<h:form id="testForm">
<h:inputText id="textInput" value="test">
<v:keyValidator type="time"/>
</h:inputText>
<a:commandButton value="Make AJAX request" reRender="testForm"/>
</h:form>
</body>
</html>
Component class:
public class KeyValidator extends UIComponentBase
{
public KeyValidator()
{
System.out.println("new KeyValidator");
}
public KeyValidatorType getValidatorType()
{
return type;
}
public String getType()
{
return getValidatorType().toString();
}
public void setType(String type)
{
this.type = KeyValidatorType.valueOf(type.toUpperCase());
}
#Override
public String getFamily()
{
return KeyValidator.class.getName();
}
private KeyValidatorType type;
}
When i press "Make AJAX request" button my component is recreated. But attribute 'type' is not set in component.
The main problem starts at renderView phase in component renderer when encodeBegin is called it tries to get this attribute and of course it gets null instead of correct value.
So, the more precise question probably:
How to get attribute values of component on AJAX request at renderView phase?
Any help will be greatly appreciated.
You must override saveState and restoreState in component to save and restore needed attributes.
Good Luck!

Categories