JSF selectBooleanCheckbox not calling the associated setter method if it is conditionally rendered - jsf

I'm using Mojarra 2.0.3 on JBoss 6.1.0 final. I'm having a problem with the value setter method for an h:selectBooleanCheckbox not being called if conditional rendering is specified in the tag.
Specifically:
JSF:
<h:selectBooleanCheckbox value="#{somebean.checked}" rendered="#{somebean.render}" />
Example bean code:
private Boolean checked = new Boolean(false);
public Boolean getChecked() {return checked;}
public void setChecked(Boolean checked) {this.checked = checked;}
public boolean getRender() {return true;}
The problem is that the setChecked() method is never called when the form is submitted. If I remove the "rendered" attribute, the setter will be called as expected. It's only when the tag is conditionally rendered that it is not processed on submit. The getter is called normally in either case.
If I set a valueChangeListener, this is also not invoked if the selectBooleanCheckbox is conditionally rendered. Finally, enclosing the h:selectBooleanCheckbox tag in a container like an :panelGrid and setting the conditional rendering on the "outside" component results in the same behavior. It seems that the checkbox won't be processed if it is conditionally rendered.
This seems like kind of a basic thing, so I'm assuming there is something I'm missing. Does anyone have any similar experience with this?
UPDATE: The managed bean in this case is a CDI ConversationScoped bean. Further debugging seemed to suggest that another instance of the bean instantiated and used somewhere in the JSF lifecycle. So I modified the bean to set the conversation to non-transient in the postConstruct method. This caused JSF to throw the following exception while evaluating the EL expression used in the "rendered" attribute:
23:41:12,179 WARNING[javax.enterprise.resource.webcontainer.jsf.lifecycle] /admin/edit_user_profile.xhtml #41,72 rendered="#{profileEditor.isCurrentUser}": java.lang.IllegalStateException: javax.el.ELException: /admin/edit_user_profile.xhtml #41,72 rendered="#{profileEditor.isCurrentUser}": java.lang.IllegalStateException
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111) [:2.0.3-]
at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:190) [:2.0.3-]
at javax.faces.component.UIComponentBase.isRendered(UIComponentBase.java:417) [:2.0.3-]
.
.
.
Caused by: java.lang.IllegalStateException
at com.sun.faces.context.FacesContextImpl.assertNotReleased(FacesContextImpl.java:635) [:2.0.3-]
at com.sun.faces.context.FacesContextImpl.getExternalContext(FacesContextImpl.java:135) [:2.0.3-]
at com.sgi.tds.web.admin.beans.TdsAdminBean.getCurrentUser(TdsAdminBean.java:36) [:]
at com.sgi.tds.web.admin.beans.UserProfileEditorBean.getIsCurrentUser(UserProfileEditorBean.java:153) [:]
at com.sgi.tds.web.admin.beans.UserProfileEditorBean$Proxy$_$$_WeldClientProxy.getIsCurrentUser(UserProfileEditorBean$Proxy$_$$_WeldClientProxy.java) [:]

As part of attack safeguard, the rendered attribute is re-evaluated when JSF postprocesses the form submit. That it evaluates false in your particular case can only mean that your bean is request scoped and that you didn't preserve the attribute value in bean's (post)constructor.
If you can't preserve it, then you need to put the bean in the view scope.
#ManagedBean
#ViewScoped
public class Somebean {
// ...
}
This way the bean instance lives as long as you're interacting with the same view. See also this related question/answer: commandButton/commandLink/ajax action/listener method not invoked or input value not updated

Related

JSF 2.2: Passing bean attribute as ValueExpression to composite

I want to pass a non-managed (non-String) object as an attribute on a dynamically added composite component, and have it survive the session.
The JSF 2.2 ViewDeclarationLanguage#createComponent handles dynamic non-string attribute values to composite components differently than the older Mojarra dependent code (Application#createComponent). I can't find the approach that works completely with the JSF 2.2 technique, but it's probably me.
[I'm trying to remove Mojarra dependencies by converting to MyFaces (and also working around some other Mojarra issues). I'm using JSF 2.2, CDI Weld 2.2.16, Tomcat v8.0]
I'm instantiating different composite components like these programmatically (notice the bean attribute):
<cc:interface>
<cc:attribute name="bean" required="true" type="com.aadhoc.cvc.spikes.extensionsapproach.ExtensionBeanInterface"/>
</cc:interface>
<cc:implementation>
<h:panelGrid columns="2">
<h:outputText value="Title:"/>
<h:inputText value="#{cc.attrs.bean.title}"/>
</h:panelGrid>
</cc:implementation>
In the older Mojarra dependent approach, I instantiate the non-managed bean object, and add it directly to the composite component as an attribute and it works great (I'm using #BalusC's great but Mojarra dependent sample code from OmniFaces Component#includeCompositeComponent):
ExtensionBeanInterface bean = Class.forName(className).newInstance();
attributes = new HashMap<String, Object>();
attributes.put("bean", bean); // Using bean object itself
[..]
UIComponent composite = application.createComponent(context, resource);
composite.getAttributes().putAll(attributes);
[..]
In JSF 2.2, I've found that I must pass a String ValueExpression instead of my bean object directly. I'm currently using this technique, and can't get it quite right:
FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();
ValueExpression beanValExp = context.getApplication().getExpressionFactory()
.createValueExpression(elContext, "#{customBeanVE}", ExtensionBeanInterface.class);
beanValExp.setValue(elContext, bean);
String beanValExpStr = beanValExp.getExpressionString();
attributes = new HashMap<String, Object>();
attributes.put("bean", beanValExpStr); // Using VE instead of bean object
UIComponent composite = context.getApplication().getViewHandler()
.getViewDeclarationLanguage(context, context.getViewRoot().getViewId())
.createComponent(context, taglibURI, tagName, attributes);
[..]
This works great on the first "add composite", but on any following form submit, I get:
/resources/com/aadhoc/cvc/spikes/extensionsapproach/components/House.xhtml
#16,49 value="#{cc.attrs.bean.title}": Target Unreachable, 'bean'
returned null
I've verified that the composite's required and type attributes are working fine, and that the #{cc.attrs.bean.title} is initially showing the bean's title. I verified with a static use of the composite component that refreshes work fine.
What's the deal, and how can I handoff the bean object so that it survives with the composite across the session?
I had this working great in Mojarra. I could put the bean object in the attribute value, and all was wonderful. Trying MyFaces, I needed to change/update my approach, and I now needed to use EL strings instead of direct object references.
Since all was working with just putting bean object into attributes Map, I wanted a magical yet elegant place to put bean objects and have them survive. I could have put them into the "global" session Map (like this: How to save an object into JSF Session), but it wasn't clean enough. I then put them into my one main session state bean (modelerBean), and it was right. This is how I saved the bean, and how I pointed to it with an EL string. No need to create ValueExpression or MethodExpression objects or register special mappings. This JSF 2.2 compatible approach worked for me in both Mojarra and MyFaces.
public void onAdd(ActionEvent ev) {
[..]
ChosenBean chosenBean = new ChosenBean();
chosenBean.setComponentId(id);
chosenBean.setBean(bean);
modelerBean.addChosen(chosenBean);
[..]
String el = "#{modelerBean.getChosen('"+id+"').bean}"
attributes.put(MODELER_EXTENSION_BEAN_ATTRIBUTE_NAME, el);
[..]
I decided on this after reading #BalusC's post: How do I get and set an object in session scope in JSF?
Note, my experience with #BalusC's two "add composites dynamically" approaches (How to programmatically or dynamically create a composite component in JSF 2) is that you should definitely use the JSF 2.2 approach if you can. The old Mojarra approach does work if you aren't in JSF 2.2. Once I modified my code to have the JSF 2.2 approach work, the old Mojarra approach would break in strange ways.

View scope: java.io.NotSerializableException: javax.faces.component.html.HtmlInputText

There is an error each time a button calls an action from the backing-bean.
Only applies to beans with a view scope and I haven't found a way to fix it without regression over other modules in the code.
DefaultFacele E Exiting serializeView - Could not serialize state: javax.faces.component.html.HtmlInputText
java.io.NotSerializableException: javax.faces.component.html.HtmlInputText
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
Or also:
com.ibm.ws.webcontainer.servlet.ServletWrapper service SRVE0014E: Uncaught service() exception
root cause Faces Servlet: ServletException: /jspFiles/jsf/Deployments/editQueue.faces No saved view state could be found for the view identifier /jspFiles/jsf/Deployments/editQueue.faces
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:205)
Caused by: javax.faces.application.ViewExpiredException: /jspFiles/jsf/Deployments/editQueue.faces No saved view state could be found for the view identifier: /jspFiles/jsf/Deployments/editQueue.faces
at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute (RestoreViewExecutor.java:128)
faces-config.xml
<managed-bean>
<managed-bean-name>pc_EditQueue</managed-bean-name>
<managed-bean-class>pagecode.jspFiles.jsf.deployments.EditQueue</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
<managed-property>
<property-name>queueDeploymentBean</property-name>
<value>#{queueDeploymentBean}</value>
</managed-property>
</managed-bean>
web.xml
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
<param-value>true</param-value>
</context-param>
bean:
#ManagedBean
#ViewScoped
public class EditQueue extends PageCodeBase implements Serializable {
private static final long serialVersionUID = -1L;
public String doButtonAddAction() {
// calls manager (long)
FacesUtil.setViewMapValue("queueDeploymentBean", queueDeploymentBean);
return "";
}
I read this suggestion to set SERIALIZE_STATE_IN_SESSION to false and indeed this solution works for this view scope bean. However this fix comes at a high cost: many existing modules in the application don't work anymore so I cannot use this fix there. Some of the regression observed are:
// returns null must be changed with FacesUtil.getSessionMapValue("userId");
getSessionScope().get("userId");`
// returns null must be changed with FacesUtil.getViewMapValue("linkerBean");
linkerBean = (Linker) getManagedBean("linkerBean");`
// NPE so must be changed with FacesContext.getCurrentInstance().addMessage(...)
getFacesContext().addMessage(...)`
So my questions are:
why the NotSerializableException even though the bean implements Serializable ?
is there a way to apply the SERIALIZE_STATE_IN_SESSION param over only a subset of the beans or not ?
is there another solution to have my view scope bean to work (without having to change them to request scope or else) ?
WebSphere 8.0.0.3,
Java 1.6.0,
JSF 2.0,
RichFaces 4.2.3.Final
why the NotSerializableException even though the bean implements Serializable ?
Not only the bean needs to be serializable, but all of its properties (and all their nested properties etc) must also be serializable. The name of the offending non-serializable class can easily be found in the exception message:
java.io.NotSerializableException: javax.faces.component.html.HtmlInputText
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
This suggests that you're binding a <h:inputText> component to the bean like below:
<h:inputText binding="#{bean.fooInput}" ...>
private UIComponent fooInput;
This is indeed illegal when the bean is not in request scope. UIComponent instances are request scoped and may not be shared across multiple requests. Moreover, UIComponent instances are not serializable. Only their state is, but JSF will worry about that all by itself.
You must remove the fooInput property and you need to look for a different solution for the problem for which you incorrectly thought that binding the component to a view scoped bean would be the right solution.
If you intend to access it elsewhere in the view, e.g. #{bean.fooInput.value}, then just bind it to the Facelet scope without the need for a bean property:
<h:inputText binding="#{fooInput}" ...>
It'll be available elsewhere in the same view via #{fooInput.xxx}.
<h:inputText ... required="#{empty fooInput.value}" />
If you intend to set some component attribute programmatically inside the bean, e.g. fooInput.setStyleClass("someClass"), or fooInput.setDisabled(true), then you should be binding the specific attribute in the view instead of the whole component:
<h:inputText ... styleClass="#{bean.styleClass}" />
...
<h:inputText ... disabled="#{bean.disabled}" />
If you are absolutely positive that you need to get a hand of whole UIComponent instance in the bean for whatever reason, then manually grab it in method local scope instead of binding it:
public void someMethod() {
UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot();
UIComponent fooInput = view.findComponent("formId:fooInputId");
// ...
}
But better ask a question or search for an answer how to solve the concrete problem differently without the need to grab a whole component in the backing bean.
See also:
How does the 'binding' attribute work in JSF? When and how should it be used?
As to the ViewExpiredException, this has different grounds which is further elaborated in javax.faces.application.ViewExpiredException: View could not be restored.

View-scoped bean recreated on POST when URL parameters not used

I have a view-scoped JSF-managed bean that's backing an xhtml view where I read one parameter from the URL using f:viewParam.
The view presents a form to the user. However, when the user submits the form by pressing the p:commandButton it seems that the view-scoped bean is recreated (I added a #PostConstruct annotation to verify this) and so doesn't remember the instance variable read from the f:viewParam (invId in the code below).
I originally navigate to the view with a GET that includes a URL parameter but the POST message that's send when the user presses the p:commandButton doesn't include the URL parameter. So I am thinking that when the JSF runtime doesn't see the URL parameter on the POST it considers this to be a different view and is recreating the JSF-managed bean. When I change the view scope to session-scoped the code works.
Here's the code:
view
<f:metadata>
<f:viewParam name="invId" value="#{registerBean.invId}"/>
</f:metadata>
<h:form id="registrationForm">
....
<p:commandButton value="register" action="#{registerBean.register}"
icon="ui-icon ui-icon-newwin" ajax="false"/>
</h:form>
backing bean
#ManagedBean
#ViewScoped
public class RegisterBean implements Serializable {
#ManagedProperty(value="#{invId}")
private String invId;
...
update
It turns out that this wasn't related to the URL parameters at all. Following BalusC advice below I removed the c:when tags my view was using (relying on rendered attributes instead for the same effect), and now the view-scoped bean is no longer recreated and the invId field is properly retained.
The problem is not visible in the code posted so far, but it's for JSF 2.0/2.1 a known issue that a view scoped bean will be recreated when one of its properties is been bound to an attribute of a taghandler like JSTL <c:if> or Facelets <ui:include> or a view build time attribute of JSF component, such as id and binding, while partial state saving is enabled (as by default).
The background explanation is that any EL expressions in those attributes are executed during building and restoring the view. As view scoped beans are stored in the view and thus only available after restoring the view, such an EL expression evaluation would cause a brand new and separate view scoped bean to be created. This is basically a chicken-egg issue. It's fixed in the upcoming JSF 2.2.
There are basically 3 solutions:
Change the view accordingly so that those EL expressions are only evaluated during view render time. E.g. replace <c:if>/<c:choose> by rendered.
Or bind those attributes to a request scoped bean (design notice: you can just inject a view scoped bean as a managed property of a request scoped bean).
Turn off partial state saving, if necessary only for the particular view.
See also:
JSTL in JSF2 Facelets... makes sense?
#ViewScoped fails in taghandlers

JSF component binding - some confusion

From web pages like this one,
http://www.jsftutorials.net/components/step5.html
I understand that the binding attribute in JSF tag/view component is to bind the view component to a Java instance of the UI component in the backing bean.
E.g., that's what is done in the following code:
<h:inputText value="#{ myBean.someProperty}" binding="#{ myBean.somePropertyInputText}"/>
But sometimes I see code like this:
<h:commandButton id="t1" binding="#{foo}" value="Hello, World!" onclick="alert('I am #{id:cid(foo)}'); return false;" />
where id:cid is a taglib function which is defined as follow:
public static String cid(UIComponent component) {
FacesContext context = FacesContext.getCurrentInstance();
return component.getClientId(context);
}
In the above code, binding="#{foo}" does not bind to "a Java instance of the UI component in the backing bean".
So what is the meaning of expressions such as binding="#{foo}" ?
It just binds the component to the current Facelet scope. This is particularly useful if you don't need it in the backing bean at all. This saves your backing bean code from useless properties which aren't been used in any of the other methods at all. Note that it also works that way in JSF 1.2. Not sure about JSF 1.0/1.1 though as it uses a different and JSF-proprietary EL API.
See also:
JSF component binding without bean property

h:inputText value not set in bean

The <h:inputText> value is not been set in a request scoped bean. If the value is preserved from DB, it works fine.
view:
<h:inputText id="receipient" size="90" styleClass="text" readonly="#{!bean.enable}" value="#{bean.recipient}" />
bean:
public class Bean {
private String recipient;
Bean(){
recipient = //load from db
}
//set
//get
}
What is the problem and how can I solve this?
Ensure that readonly="#{!bean.enable}" evaluates the same as it was in initial view. If it evaluates true during apply request values phase of the form submit, then the component's value won't be processed (because it is marked as read only).
An easy test is to put the bean in session scope. If that fixes it, then you know the culprit. If you're using JSF 2.0, just put bean in view scope by using #ViewScoped instead of #RequestScoped. If you're still on JSF 1.x, use Tomahawk's <t:saveState> component to let the bean act like the JSF 2.0 view scoped one.
<t:saveState value="#{bean}" />

Resources