i have already spent 3 days searching for a possible workaround, so my ViewScoped beans do not lead to an OutOfMemoryException, but without any luck.
First my environment:
JBoss AS 7.1.1.final with
Mojarra 2.1.7
I thought, that ViewScoped beans would be destroyed on a session expiration, but they will not get destroyed (checked with heap dumps). I found the following new feature for Mojarra 2.1.16, which fixes this problem, but sadly upgrading to this version is currently not an option:
http://java.net/jira/browse/JAVASERVERFACES-2561
This problem is also related to the following thread:
Destroying view-scoped beans when session ends
Is there anything i can do, to remove all created ViewScoped beans, when a session ends (logout or session expired)? Saving viewMap in a SessionScoped bean and call clear() also not destroy those beans.
Thanks in advance,
Daniel
To nail down this bug i have stripped my code to the following parts:
One JSF2 page that is very simple:
<!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">
<h:head>
<f:facet name="first">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Simple OOM Check</title>
</f:facet>
</h:head>
<h:body>
<h:outputText value="#{oomCheckBean.testString}"/>
</h:body>
</html>
And the used #ViewScoped bean oomCheckBean:
#ViewScoped
#ManagedBean(name = "oomCheckBean")
public class OomCheckBean implements Serializable {
private static final long serialVersionUID = 6325712415478215045L;
private String testString;
#PostConstruct
public void init() {
testString = "Hello World";
System.out.println("I will survive ....");
}
#PreDestroy
public void destroy() {
System.out.println("... klaaatsch!");
}
/**
* #return the testString
*/
public String getTestString() {
return testString;
}
/**
* #param testString the testString to set
*/
public void setTestString(String testString) {
this.testString = testString;
}
}
After this i changed my JMeter testcase to login, call this oomCheck.xhtml page and logout. After a short time i stopped test execution, and used JMX to first call manually the garbage collector (java.lang.Memory --> Operations --> gc()). After this i call method to create a heap dump (com.sun.management.HotSpotDiagnostic --> Operations --> dumpHeap()).
Result is like i mentioned in my question, there are a lot OomCheckBean objects in memory. The owner object is org/jboss/as/web/deployment/ConcurrentReferenceHashMap$HashEntry. Any help to fix this problem is appreciated. Like i mentioned above, these #ViewScoped beans will be destroyed, if JSF version is upgraded to 2.1.16, leaving other "bugs" like missing doctype and AJAX problems. So i would be happy, if someone knows how to destroy #ViewScoped beans, when session becomes invalid.
Related
I can not get an f:ajax listener working on a simplest JSF 2.2 page. Values are assigned, but the listener is deaf. Strangely, the very same code is working perfectly fine if I replace h:selectOneRadio with h:selectOneMenu. Here is the html:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form id="f" >
<h:selectOneRadio id="r" value="#{test.mode}">
<f:selectItem itemValue="One"/>
<f:selectItem itemValue="Two"/>
<f:selectItem itemValue="Three"/>
<f:ajax render="#form" execute="#form" listener="#{test.listener2()}"/>
</h:selectOneRadio>
<br/>
<h:outputText id="out" value="#{test.mode}"/>
</h:form>
</h:body>
</html>
and the bean:
#Named
#SessionScoped
public class Test implements Serializable {
private final static Logger LOG = Logger.getLogger(Test.class.getName());
private String mode;
public String getMode() {
return mode;
}
public void setMode(String mode) {
this.mode = mode;
LOG.info("Mode setter: " + mode);
}
public void listener1(AjaxBehaviorEvent event) throws AbortProcessingException {
LOG.info("Mode listener 1: " + mode);
}
public void listener2() {
LOG.info("Mode listener 2: " + mode);
}
}
Neither of the listener method types are fired for h:selectOneRadio. Making the bean as #ManagedBean and using different ajax event types were to no help either.
The issue appeared after upgrading of Apache Tomee to version 7.0.1 (MyFaces 2.2.10, JSF 2.2). Same problem with MyFaces to 2.2.11.
The web app is bundled inside an ear, no other JSF libraries are loaded, no Primefaces and similar, no servlet filters, no nothing at all - a pure JSF 2.2 application.
Any ideas?
There's a bug in MyFaces, will be fixed in MyFaces 2.2.12.
Should you need an immediate solution, use MyFaces snapshots, they seem to be working fine, just drop them in Tomee's lib folder instead of the stock versions of myfaces-api-* and myfaces-impl-*.
https://issues.apache.org/jira/browse/MYFACES-4068
I'm using Mojarra 2.2.4 on GlassFish 4 with Java 7.
As I understand from BalusC's answer to How and when is a #ViewScoped bean destroyed in JSF?, #ViewScoped beans should be destroyed in three cases:
Post-back with non-null outcome
Session expiration
Maximum number of logical views in session exceeded
My beans are being destroyed in the first two cases, but not when the maximum number of logical views is exceeded. I have verified that the beans do expire when the maximum is exceeded (I get a ViewExpiredException), but they are still not destroyed until the session itself expires.
For memory consumption reasons, I would like to have the beans destroyed in this third case, especially since they are not usable after expiration.
Questions
Why are the beans not destroyed when they expire?
Is this a bug or expected behavior?
What would be a clean work-around to make sure the beans get destroyed?
Update: OmniFaces ViewScoped annotation destroys beans as soon as they expire.
Minimal Example
Here is my bean:
#javax.inject.Named("sandboxController")
#javax.faces.view.ViewScoped
public class SandboxController implements Serializable {
private static final Logger log = Logger.getLogger(SandboxController.class.getName());
#PostConstruct
public void postConstruct() {
log.log(Level.INFO, "Constructing SandboxController");
}
#PreDestroy
public void preDestroy() {
log.log(Level.INFO, "Destroying SandboxController");
}
public String getData() {
return "abcdefg";
}
}
and my sandbox.xhtml:
<?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://xmlns.jcp.org/jsf/html">
<body>
<h:form>
<h:outputText value="#{sandboxController.data}"/>
</h:form>
</body>
</html>
and part of my web.xml:
<context-param>
<param-name>com.sun.faces.numberOfLogicalViews</param-name>
<param-value>3</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.numberOfViewsInSession</param-name>
<param-value>3</param-value>
</context-param>
If I refresh the sandbox.xhtml 50 times, I get 50 copies of INFO: Constructing SandboxController in the log. The beans are not destroyed, regardless of how many times I refresh. VisualVM confirms that the beans are still referenced by the UIViewRoot's ViewMap. In my full-size bean, which maintains a fair bit of state, I quickly get an OutOfMemoryException.
When I manually expire the session, I get 50 copies of INFO: Destroying SandboxController.
If I add a submit button to sandbox.xhtml and load it up in 4 different tabs, then try to submit the first one, I get a ViewExpiredException, as expected, but the bean is still not destroyed.
The behavior is the same if I instead use the javax.faces.bean.ManagedBean and javax.faces.view.ViewScoped annotations. However, the OmniFaces annotation org.omnifaces.cdi.ViewScoped works properly.
To clarify...
My #ViewScoped beans are being destroyed on session expiration, unlike problems described in related questions such as Linked ViewScoped beans lead to memory leaks
I am not asking why each bean is not destroyed immediately on subsequent refresh as questioned here: JSF 2.1 ViewScopedBean #PreDestroy method is not called. I want to know why it is that even when they expire, and are no longer useful, they are still not destroyed, and thus continue consuming memory.
I was able to find a clean work-around by using the OmniFaces #ViewScoped annotation (org.omnifaces.cdi.ViewScoped) instead of the standard #ViewScoped (javax.faces.view.ViewScoped).
The OmniFaces ViewScoped correctly destroys the beans as soon as they expire.
See here for more details:
http://showcase.omnifaces.org/cdi/ViewScoped
I have a very simple app. One bean annotated with #Named #SessionScoped and 2 web pages. home.xhtml and list.xhtml. When I set searchForm.hostanme on the first page, and access it again on the 2nd page. I can see from sysout statements that hostname is getting set, but there is a space where that value should be on the list.xhtml page. It's disappearing between home and list. Why?
home.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head></h:head>
<h:form>
<h:outputText value="Hi Mama" />
<h:inputText value="#{searchForm.hostname}" />
<h:commandButton action="list?faces-redirect=true" value="Devices" />
<h:commandButton action="#{searchForm.reset}" value="Reset" immediate="true" type="reset" />
</h:form>
</html>
list.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:outputText value="Device is #{searchForm.hostname}" />
</html>
SearchForm.java
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
#Named
#RequestScoped
public class SearchForm {
String hostname;
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
System.out.println("hostname is "+ hostname);
this.hostname = hostname;
}
public String reset(){
System.out.println("RESETTING");
System.out.println("hostname is "+ hostname);
return "/index.xhtml";
}
}
I'm thinking it might be a scoping issue, but I've tried several scopes and still searchForm.hostname is not avaible by the time I navigate to the list.xhtml page.
I've been reading and reading and reading up on JSF2, but I still can't resolve I hope is a simple problem.
I have a beans.xml file is in WEB-INF.
I come from a seam 2 background so decided I was comfortable with ConversationScoped in this context. It worked like a charm.
Some more thoughts that might help a confused person like me.
As I commented above, I grappled with a similar issue before in this question. As the comments above suggest, this is a scope issue. I realized that it is not only a scoping issue, it is an issue of confusion between CDI and JSF2 managed beans.
Both have a unique set of scopes, but there is some overlap, and in order to not be scratching your head over what should really not be a difficult problem, it is important to
Decide whether to use CDI beans or JSF2 managed beans
Thoroughly learn the scopes assocated with either type of bean
Know which one you are selecting when eclipse offers you 2 choices!
That last one has messed me up more times than I should admit.
Whether to use CDI or JSF2 just google "cdi vs. jsf2". I chose CDI since most things I read said to use CDI. I'm not sure it really makes a difference for my simple app.
Learn your scopes!! There are many articles about scopes, unfortunately I don't understand them as well as I should.
This is a classic for JSF2 users: Communication in JSF2 and I found this helpful as well. (notice same author - BalusC is a good resource for anything JSF2)
As for CDI just stick with the tutorial on Oracle's website.
And as for which library to import -- both JSF and CDI have a SessionScoped, and I think also RequestScoped:
CDI scopes are in javax.enterprise.context.SessionScoped
JSF scopes are in javax.faces.bean.SessionScoped
I consistently just take the first thing eclipse offers me. It can really screw you up so pay attention!
I am new to JSF and still learning. I tried searching for a solution to my specific problem described below but I could not find anything. If it because I was searching for the wrong things, please point me in the right direction, but hopefully it is something that hasn't been answered and an answer can benefit everyone.
The following example illustrates the problem I came across. The example is simplified to focus on the problem and to hide the complexities of the actual project in which the problem occurred.
Consider the following pages / classes:
/resources/test/custom.xhtml;
/test/CharsetProvider.java;
/test/CharsetHello.java;
/testWith.xhtml;
/testWithout.xhtml;
/resources/test/custom.xhtml
This is composite component with one attribute with a default value. The component simply takes the attribute value and passes it as an argument to the CDI bean described below in order obtain the model object used for output.
<?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:cc="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html">
<cc:interface>
<cc:attribute name="charset"
default="#{charsetProvider.defaultCharset}"
type="java.nio.charset.Charset" />
</cc:interface>
<cc:implementation>
<h:outputText value="#{charsetProvider.createCharsetHello(cc.attrs.charset).hello}"/>
</cc:implementation>
</html>
test/CharsetProvider.java
This is a CDI bean that simply contains a default value used throughout the application and has a method that creates an object used as the model for a component. The reason I use a CDI bean instead of a backing bean is because in my specific project the default value needs to be injected at runtime, but backing beans are not candidates for injection.
package test;
import java.nio.charset.Charset;
import javax.annotation.PostConstruct;
import javax.faces.bean.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class CharsetProvider {
private Charset defaultCharset;
#PostConstruct
protected void postConstruct() {
this.defaultCharset = Charset.forName("UTF-8");
}
public Charset getDefaultCharset() {
return defaultCharset;
}
public Charset getCharsetForName(String name) {
return Charset.forName(name);
}
public CharsetHello createCharsetHello(Charset cs) {
return new CharsetHello(cs);
}
}
test/CharsetHello.java
This is the "model" object. It simply converts "Hello world!" to a byte array and back using the given charset.
package test;
import java.nio.charset.Charset;
public class CharsetHello {
private static final String HW = "Hello World!";
private final byte[] data;
private final Charset cs;
public CharsetHello(Charset cs) {
this.cs = cs;
this.data = CharsetHello.HW.getBytes(this.cs);
}
public String getHello() {
return new String(this.data, this.cs);
}
}
testWith.xhtml
This is a test page that uses the composite component defined above by specifying a value for the component's attribute. The page renders properly, i.e. "Hello World!" prints on the screen.
<?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:test="http://java.sun.com/jsf/composite/test">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<test:custom charset="#{charsetProvider.getCharsetForName('UTF-16')}" />
</h:body>
</html>
testWithout.xhtml
This is a test page that does not pass a custom value to the component's attribute, intending to use the default value.
<?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:test="http://java.sun.com/jsf/composite/test">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<test:custom />
</h:body>
</html>
The page above results in a JSF error page with the following message:
/resources/test/custom.xhtml #14,94 value="#{charsetProvider.createCharsetHello(cc.attrs.charset).hello}": Cannot convert UTF-8 of type class java.lang.String to class java.nio.charset.Charset
It seems that in the last case the default value is converted to a java.lang.String before being passed to the method.
First off, is this the expected behaviour and why?
If this is the expected behaviour, can you suggest a different implementation?
Thank you in advance!
This problem has exactly the same ground as this problem: FacesConverter forClass don't work with Composite Componet. The composite attribute value type is in Mojarra incorrectly been evaluated as java.lang.Object instead of the actual model type.
It's been reported as Mojarra issue 2568. It works in MyFaces 2.1.9.
Deploying on JBoss AS 7.1.0.Final.
I have a very simple test app. It was working as expected until the other day (famous last words) and is no longer doing the most basic thing, namely setting the value of the input component and using it in the action component. I have stripped this thing down to the basics and can not figure out what is going on.
index.xhtml is here
<!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:h="http://java.sun.com/jsf/html">
<h:head>
<title>contacts</title>
</h:head>
<h:form>
<h:outputLabel value="Message:" />
<h:inputText value="#{contactView.siteCode}" />
<h:commandButton action="#{contactView.save}" value="Save" />
</h:form>
</html>
ViewScoped bean is here
#Named
#ViewScoped
public class ContactView implements Serializable {
public ContactView() {
}
private String siteCode;
public String getSiteCode() {
System.out.println("getSiteCode: "+ siteCode);
return siteCode;
}
public void setSiteCode(String siteCode) {
System.out.println("setSiteCode: "+ siteCode);
this.siteCode = siteCode;
}
public String save(){
System.out.println("Saving sitecode: " + siteCode);
return "index.jsf";
}
}
What am I doing wrong? When I click on the save button I get this in the output
10:50:37,663 INFO [stdout] (http--0.0.0.0-8080-2) setSiteCode: 22
10:50:37,663 INFO [stdout] (http--0.0.0.0-8080-2) Saving sitecode: null
10:50:37,663 INFO [stdout] (http--0.0.0.0-8080-2) getSiteCode: null
That's because the bean is managed by CDI #Named, not by JSF #ManagedBean. JSF scope annotations of the package javax.faces.bean only works on beans managed by JSF. On a CDI managed bean, you need to use CDI annotations from javax.enterprise.context instead. However, CDI doesn't have a concept of the view scope. Closest is #ConversationScoped, but this is more complex to manage. When you don't specify a scope on a CDI managed bean, it will default to the request scope.
Make sure that your bean is managed by JSF whenever you want to use #ViewScoped.
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class ContactView implements Serializable {
// ...
}
Further, you also need to make sure that your action methods return null or void whenever you want to retain the view scope.