In our JavaEE6 project (EJB3, JSF2) on JBoss 7.1.1, it seems we have a memory leak with SeamFaces #ViewScoped.
We made a little prototype to check the fact :
we use JMeter to call a page 200 times;
the page contains and calls a viewscoped bean which injects a stateful EJB;
we fix the session timeout at 1 minute.
At the end of the test, we check the content of the memory with VisualVM, and here what we got:
with a #ViewScoped bean, we still get 200 instances of the stateful MyController - and the #PreDestroy method is never called;
with a #ConversationScoped bean, #preDestroy method is called a the session end and then we got a clean memory.
Do we badly use the view scope, or is it truly a bug?
Here's the XHTML page:
<!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:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:s="http://jboss.org/seam/faces">
<f:metadata>
<f:viewParam name="u" value="#{myBean.uselessParam}" />
<s:viewAction action="#{myBean.callService}" />
</f:metadata>
<h:body >
<f:view>
</f:view>
</h:body>
</html>
Now the included bean myBean. For the #ConversationScoped variant, all commented parts are uncommented.
#ViewScoped
// #ConversationScoped
#Named
public class MyBean implements Serializable
{
#Inject
MyController myController;
//#Inject
//Conversation conversation;
private String uselessParam;
public void callService()
{
//if(conversation.isTransient())
//{
// conversation.begin();
//}
myController.call();
}
public String getUselessParam()
{
return uselessParam;
}
public void setUselessParam(String uselessParam)
{
this.uselessParam = uselessParam;
}
}
And then the injected stateful bean MyController:
#Stateful
#LocalBean
public class MyController
{
public void call()
{
System.out.println("call ");
}
#PreDestroy
public void destroy()
{
System.out.println("Destroy");
}
}
I see many developers are satisfied with #ViewAccessScoped in Myface CODI.
Could you please give it a try and tell the feedback.
I have faced the above mentioned problem in JSF managed #ViewScoped bean. After referring to few blogs I understood that JSF saves view bean states in http session and gets destroyed only when session is invalidated. Whenever we click on the jsf page every time new view scope bean referred in page is created. I did a work around using Spring Custom View Scope. It works fine. Below is the detail code.
For JSF 2.1:
Step 1: Create a View Scope Bean Post Construct Listener as follows.
public class ViewScopeBeanConstructListener implements ViewMapListener {
#SuppressWarnings("unchecked")
#Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
if (event instanceof PostConstructViewMapEvent) {
PostConstructViewMapEvent viewMapEvent = (PostConstructViewMapEvent) event;
UIViewRoot viewRoot = (UIViewRoot) viewMapEvent.getComponent();
List<Map<String, Object>> activeViews = (List<Map<String, Object>>)
FacesContext.getCurrentInstance().getExternalContext().getSessionMap(). get("com.org.jsf.activeViewMaps");
if (activeViews == null) {
activeViews = new ArrayList<Map<String, Object>>();
activeViews.add(viewRoot.getViewMap());
FacesContext.getCurrentInstance().getExternalContext().getSessionMap(). put("com.org.jsf.activeViewMaps", activeViews);
} else {
activeViews.add(viewRoot.getViewMap());
}
}
}
Step 2: Register event listener in faces-config.xml
<system-event-listener>
<system-event-listener-class>
com.org.framework.custom.scope.ViewScopeBeanConstructListener
</system-event-listener-class>
<system-event-class>javax.faces.event.PostConstructViewMapEvent</system-event-class>
<source-class>javax.faces.component.UIViewRoot</source-class>
</system-event-listener>
Step 3: Create a Custom View Scope bean as follows.
public class ViewScope implements Scope {
#Override
public Object get(String name, ObjectFactory objectFactory) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
List<Map<String, Object>> activeViewMaps = (List<Map<String, Object>>)
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("com.org.jsf.activeViewMaps");
if (activeViewMaps != null && !activeViewMaps.isEmpty()
&& activeViewMaps.size() > 1) {
Iterator iterator = activeViewMaps.iterator();
if (iterator.hasNext()) {
Map<String, Object> oldViewMap = (Map<String, Object>)
iterator.next();
oldViewMap.clear();
iterator.remove();
}
}
Object object = objectFactory.getObject();
viewMap.put(name, object);
return object;
}
}
Note : Other overridden methods can be empty.
For JSF 2.2:
JSF 2.2 saves the navigated view maps in http session in 'com.Sun.faces.application.view.activeViewMaps' as key. So add the below code in Spring Custom View Scope. No need of listeners as in JSF 2.1
public class ViewScope implements Scope {
public Object get(String name, ObjectFactory objectFactory) {
Map<String, Object> viewMap =
FacesContext.getCurrentInstance().getViewRoot().getViewMap();
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
LRUMap lruMap = (LRUMap) FacesContext.getCurrentInstance().
getExternalContext().getSessionMap().get("com.sun.faces.application.view.activeViewMaps");
if (lruMap != null && !lruMap.isEmpty() && lruMap.size() > 1) {
Iterator itr = lruMap.entrySet().iterator();
while (itr.hasNext()) {//Not req
Entry entry = (Entry) itr.next();
Map<String, Object> map = (Map<String, Object>) entry.getValue();
map.clear();
itr.remove();
break;
}
}
Object object = objectFactory.getObject();
viewMap.put(name, object);
return object;
}
}
Chances are this is a bug. Honestly the Seam 3 implementation wasn't all that great and the CODI one (and also what will be in DeltaSpike) is much better.
Related
The JSF component uses binding and backing bean is of View scope. The component have validator and valueChangeListener set. And when component's value is changed partial request is sent to server. And validator and valueChangListener are called many times but not once within request.
How to make them to be called once during request?
If remove binding the methods are called correctly once.
But is it possible to do not remove binding and make listeners be called once?
Sample of used code is next:
<h:inputText id="txt"
validator="#{myBean.validateValue}"
valueChangeListener="#{myBean.valueChanged}"
binding="#{myBean.txtInput}">
<f:ajax event="valueChange"
execute="#this"
render="#this"/>
</h:inputText>
#ViewScoped
#ManagedBean
public class MyBean {
private HtmlInputText txtInput;
public void valueChanged(ValueChangeEvent ve) {
...
}
public void validateValue(FacesContext context, UIComponent component, Object value) {
...
}
public HtmlTextInput getTxtInput() {
return txtInput;
}
public void setTxtInput(HtmlTextInput txtInput) {
this.txtInput = txtInput;
}
}
The same issue takes place for actionListener and commandLink component if it uses binding and backing bean has View scope.
The possible solution could be just to override class which is used by UIInput and UICommand to store validators and listeners. The class is javax.faces.component.AttachedObjectListHolder.
This class straightforward add new listener to backing list not checking if the same listener already is there.
Thus the solution is to check if listener exists and not to add it then.
So take javax.faces.component.AttachedObjectListHolder from jsf-api-2.1.<x>-sources.jar and add it to your project to the corresponding package. Replace method add with such one:
void add(T attachedObject) {
boolean addAttachedObject = true;
if (attachedObject instanceof MethodExpressionActionListener
|| attachedObject instanceof MethodExpressionValueChangeListener
|| attachedObject instanceof MethodExpressionValidator) {
if (attachedObjects.size() > 0) {
StateHolder listener = (StateHolder) attachedObject;
FacesContext context = FacesContext.getCurrentInstance();
Object[] state = (Object[]) listener.saveState(context);
Class<? extends StateHolder> listenerClass = listener.getClass();
for (Object tempAttachedObject : attachedObjects) {
if (listenerClass.isAssignableFrom(tempAttachedObject.getClass())) {
Object[] tempState = (Object[]) ((StateHolder) tempAttachedObject).saveState(context);
if (((MethodExpression) state[0]).getExpressionString().equals(((MethodExpression)tempState[0]).getExpressionString())) {
addAttachedObject = false;
break;
}
}
}
}
}
clearInitialState();
if (addAttachedObject) {
attachedObjects.add(attachedObject);
}
}
After that validators, valueChangeListeners and actionListeners will be triggered only once even when component binding and "View", "Session" or "Application" scopes are used.
From one of my previous questions on this site I realized I don't know anything about custom converter life cycle. I searched a bit on internet and found nothing useful.
I would like to understand if custom converters are created once for all and recycled any time they are needed or if they are created on the fly and destroyed.
I suppose their main purpose is to perform some easy and light tasks, so it would make no difference if the a new instance of the converter is created each time it is found inside a jsf page sent to the user.
But I would like to use a custom converter to solve a common task in what it is an unpaved way. My custom convert would have an heavy initialization logic, so I have to be sure about its life-cycle. It must be created once for all and not every time it is needed. Is it possible ?
Depending on the answers I will receive I can abort the idea of using custom converter or decide to move the heavy initialization logic in a singletone.
Converters are created once for each time you reference them when using #FacesConverter annotation. That means if you execute slow code there it'll bring you into problems.
Alternatively, you can annotate them as #ManagedBean with the scope you want and use them with an EL reference instead of raw converter id. If you want to initialize them in some way, the solution for you would be setting them the scope for the whole application and making them eagerly initialized, so they'll be created when application starts up:
Converter:
#ManagedBean(eager = true)
#ApplicationScoped
public class WorkerConverter implements Converter {
public WorkerConverter() {
System.out.println("Building converter...");
}
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
Integer id = Integer.parseInt(value);
if (id == 1) {
return new Worker(1, "John");
} else {
return new Worker(1, "Larry");
}
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
return ((Worker) value).getId().toString();
}
}
Managed bean:
#ManagedBean
#ViewScoped
public class SelectWorkerBean {
public static class Worker {
Integer id;
String name;
public Worker(Integer id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Worker other = (Worker) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
#Override
public int hashCode() {
return id;
}
#Override
public String toString() {
return "Worker [name=" + name + "]";
}
}
private Worker selectedWorker;
private List<Worker> workers = Arrays.asList(new Worker(1, "John"),
new Worker(2, "Larry"));
public Worker getSelectedWorker() {
return selectedWorker;
}
public List<Worker> getWorkers() {
return workers;
}
public void send() {
System.out.println(selectedWorker + " selected");
}
public void setSelectedWorker(Worker selectedWorker) {
this.selectedWorker = selectedWorker;
}
}
Page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Test page</title>
</h:head>
<h:body>
<h:form>
<h:selectOneMenu value="#{selectWorkerBean.selectedWorker}"
converter="#{workerConverter}">
<f:selectItems value="#{selectWorkerBean.workers}" var="worker"
itemLabel="#{worker.name}" />
</h:selectOneMenu>
<h:commandButton value="send" action="#{selectWorkerBean.send}" />
</h:form>
</h:body>
</html>
I created an ejb
#Stateless
#LocalBean
public class BasitBean {
public String helloBasit() {
return "Basit";
} //end of helloBasit()
} //end of class BasitBean
I am calling it from JSF like
<h:body>
<h:outputLabel value="#{helloBasit.callBasit()}" />
</h:body>
#ManagedBean
#SessionScoped
public class HelloBasit {
#EJB
private BasitBean basitBean;
/** Creates a new instance of HelloBasit */
public HelloBasit() {
}
public String callBasit() {
return basitBean.helloBasit();
} //end of callBasit()
} //end of class HelloBasit
This code is working fine. But when i change the code like this
<h:body>
<h:outputLabel value="#{helloBasit.label}" />
</h:body>
#ManagedBean
#SessionScoped
public class HelloBasit {
#EJB
private BasitBean basitBean;
String label;
/** Creates a new instance of HelloBasit */
public HelloBasit() {
System.out.println();
String label = basitBean.helloBasit();
System.out.println(label);
}
public BasitBean getBasitBean() {
return basitBean;
}
public void setBasitBean(BasitBean basitBean) {
this.basitBean = basitBean;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
} //end of class HelloBasit
Then i get the exception
SEVERE: Error Rendering View[/index.xhtml]
com.sun.faces.mgbean.ManagedBeanCreationException: Cant instantiate class: pk.mazars.basitMahmood.HelloBasit.
at com.sun.faces.mgbean.BeanBuilder.newBeanInstance(BeanBuilder.java:193)
at com.sun.faces.mgbean.BeanBuilder.build(BeanBuilder.java:102)
......
Why i am getting this exception? The flow should be what i understand is when my page encounters #{helloBasit.label} then my constructor get call, instance variable get initialized, injected the bean instance into the basitBean, then the bean method should call. But i am getting null in the bean instance in this case why? Why previous code is working and it is not ? How can i call bean from the constructor ?
Thank you.
try to move your content of the constructor into a post constructor instead...
like this
#PostConstruct
private void init() {
System.out.println();
String label = basitBean.helloBasit();
System.out.println(label);
}
Cause the ejb bean should be injected only after the managed bean has been initiated
The #PostConstruct is being run after the constructor (after that the managed bean itself was created by the JSF) and only then the EJB is being injected into the bean and can be accessed...
Your idea is correct, but I see some things that may be fixed.
#LocalBean annotation is not required if your EJB is not directly implementing an interface. In this case, with or without the #LocalBean annotation you have the same effect. You may leave that if you want to make it explicit though. See this.
Make sure both #ManagedBean and #SessionScoped import from javax.faces.bean package.
Please, see this working sample:
EJB
import javax.ejb.Stateless;
#Stateless
public class PersonService {
public String getName() {
return "Cloud Strife";
}
}
Managed Bean
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class PersonBean {
#EJB
private PersonService ps;
private String name;
#PostConstruct
public void init() {
name = ps.getName();
}
public String getName() {
return name;
}
}
XHTML 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:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<f:view contentType="text/html">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h1>Welcome, #{personBean.name}</h1>
</h:body>
</f:view>
</html>
If your value should be loaded only once, say at your bean construction, always prefer a method with #PostConstruct annotation instead of the constructor.
Also, in order to call bean methods before rendering a view you could use a f:event tag, for example:
<f:event type="preRenderView" listener="#{personBean.init}" />
I hope it helps!
I've tried to learn the JSF 2.0 with bean validation at the class level as the following: -
The utility
#Singleton
public class MyUtility {
public boolean isValid(final String input) {
return (input != null) || (!input.trim().equals(""));
}
}
The constraint annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.TYPE,
ElementType.ANNOTATION_TYPE,
ElementType.FIELD
})
#Constraint(validatedBy = Validator.class)
#Documented
public #interface Validatable {
String message() default "Validation is failure";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
The constraint validator
public class Validator extends ConstraintValidator<Validatable, MyBean> {
//
//----> Try to inject the utility, but it cannot as null.
//
#Inject
private MyUtility myUtil;
public void initialize(ValidatableconstraintAnnotation) {
//nothing
}
public boolean isValid(final MyBean myBean,
final ConstraintValidatorContext constraintContext) {
if (myBean == null) {
return true;
}
//
//----> Null pointer exception here.
//
return this.myUtil.isValid(myBean.getName());
}
}
The data bean
#Validatable
public class MyBean {
private String name;
//Getter and Setter here
}
The JSF backing bean
#Named
#SessionScoped
public class Page1 {
//javax.validation.Validator
#Inject
private Validator validator;
#Inject
private MyBean myBean;
//Submit method
public void submit() {
Set<ConstraintViolation<Object>> violations =
this.validator.validate(this.myBean);
if (violations.size() > 0) {
//Handle error here.
}
}
}
After running I've faced the exception as java.lang.NullPointerException at the class named "Validator" at the line return this.myUtil.isValid(myBean.getName());. I understand that the CDI does not inject my utility instance. Please correct me If I'm wrong.
I'm not sure if I'm doing something wrong or it is a bean validation limitation. Could you please help to explain further?
Your right, Hibernate Constraint Validator is not registered as a CDI-Bean by default (and though cannot receive dependencies).
Just put the Seam-Validation module on your classpath, and everything should run fine.
BTW: studying the source-code of the module is an excellent example of the elegance and simplicity of CDI extension. It's doesn't need more than a few dozens lines of code to bridge from CDI to hibernate validations...
The first "nonpostback" request to viewBean, someValue property in sessionBean is null.
Now, in a postback request, I am setting a user input to someValue. The problem is that someValue is always null in any "nonpostback" request.
Here is my code:
#ManagedBean
#ViewScoped
public class ViewBean implements Serializable {
#ManagedProperty(value = "#{sessionBean}")
private SessionBean sessionBean;
private String inputText;
#PostConstruct
public void init() {
if (sessionBean.getSomeValue() != null) // ALWAYS NULL
doSomething(sessionBean.getSomeValue());
}
private void doSomething(String s) {}
public void action(final ActionEvent ae) {
sessionBean.setSomeValue(getInputText());
doSomething(getInputText());
}
GETTERS/SETTERS
}
#ManagedBean
#SessionScoped
public class SessionBean implements Serializable {
private String someValue;
GETTER/SETTER
}
I feel I am doing something wrong. I am using Mojarra 2.1.2
Any advice is appreciated. Thank you.
UPDATE:
Using evaluateExpressionGet on both methods (init and action) works fine:
FacesContext context = FacesContext.getCurrentInstance();
SessionBean sessionBean = context.getApplication().evaluateExpressionGet(context,
"#{sessionBean}", SessionBean.class);
This is a known issue:
SessionScoped bean inside a ViewScoped bean is resolved as different bean depending on the expression used
I just changed the state saving method in my web.xml:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
I use GAE (Google App Engine) and need to set javax.faces.STATE_SAVING_METHOD to client. This problem can have workaround. After the action, just call refreshSession() with new value force the session object persist
protected void refreshSession(){
saveSession(CeaConst.SESSION_ATTR_NAME_LAST_REFRESH_TIME, new java.util.Date());
}