JSF access private/protected property of a super class - jsf

I am very new to JSF and this is the first project I work on (got a bit too lazy with jsp) so please forgive me if the question is trivial.
So I have a Super Class Device
#Entity
#Table(name = "Devices")
public class Device
{
protected bool Authorized
public bool isAuthorized()
{ return this.Authorized;}
public void setAuthorized(bool Authorized)
{ this.Authorized = Authorized;}
}
and a sub class SensorDevice that extends the Super Class Device
public class SensorDevice extends Device
{
// has its own properties which dont matter
}
and a Managed Bean UIDeviceManager
#ManagedBean(name = "DeviceManager")
#SessionScoped
public class UIDeviceManager
{
private List<SensorDevice> Pending;
// in constructor, Pending List gets populated with the devices requiring Authorization
}
and an xhtml page which contains a Table for the Pending Devices
<p:dataTable var="device" value="#{DeviceManager.pending}">
<p:column headerText="Device Authorization">
<h:form>
<p:inputSwitch
value="#{device.isAuthorized()}"
binding="#{AuthorizationInputSwitch}"
offLabel="UnAuthorized"
onLabel="Authorized">
<p:ajax
event="change"
listener="#{device.setAuthorized(AuthorizationInputSwitch.value)}" />
</p:inputSwitch>
</h:form>
</p:column>
Now unless the syntax in the xhtml is completely messed up (I tried my best there and would appreciate guidance), the function setAuthorized for that particular device instance should be called (even with the wrong input, but will sort that later by modifying the setter function), but that doesnt happen, the Ajax doesnt get called. Instead, the inputSwitch tries to update its "value property source" and attempts to look for a property isAuthorized() in the class SensorDevice which it fails to find.
Now I am aware that this could be easily solved by making the Boolean Authorized public in the super class but as you can see it is also a JPA entity that is persisted in a database to keep track of the devices, so the only option is to keep it protected.
So how do I update parameters of a Super class from a sub-class instance in a Managed Bean from a public function rather than direct access to the parameter itself (I thought JSF looked for the setters and getters but whatever)
Btw value="#{device.isAuthorized()}"works correctly but if I try the property directly it fails ( I guess its obvious at this point )
One last thing, if the approach/architecture is wrong, please advise on what is the correct layout to achieve this functionality. I am sure there is a standard way to integrate JSF and JPA without duplicating entities and wrappers

I think you must use field name in value attribute of InputSwitch component like this:
<p:inputSwitch
value="#{device.authorized}"
binding="#{AuthorizationInputSwitch}"
offLabel="UnAuthorized"
onLabel="Authorized">
Instead of:
<p:inputSwitch
value="#{device.isAuthorized()}"
binding="#{AuthorizationInputSwitch}"
offLabel="UnAuthorized"
onLabel="Authorized">
JSF will use isAuthorized and setAuthorized method (uses Java Beans standard convention to recognize getter and setter methods)
So i think you don't need the ajax part to call setter method.

To emphasize Mojtaba's answer, this is how you access properties in JSF:
Facelets 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:h="http://java.sun.com/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:outputText value="#{myBean.entity.someBool}"/>
</h:body>
</html>
Managed bean:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class MyBean {
private SomeEntity entity = new SomeEntity();
public SomeEntity getEntity() {
return entity;
}
}
Entity classes:
public class SomeEntity extends SomeParent {
}
public class SomeParent {
private boolean someBool = true;
public boolean isSomeBool() {
return someBool;
}
public void setSomeBool(boolean someBool) {
this.someBool = someBool;
}
}
JSF doesn't know or care that your objects are JPA entities, and standard inheritance and access rules apply.
See also
JavaBean conventions

Related

Composite Component inside ui:repeat: How to correctly save component state

I have a custom component that implements UIInput and that needs to save some state info for later reuse in postback requests. Used standalone it works fine, but inside an <ui:repeat> the postback finds the saved state of the latest rendered row of data. The log output of an action call is
INFORMATION: myData is "third foo"
INFORMATION: myData is "third foo"
INFORMATION: myData is "third foo"
INFORMATION: ok action
where I would expect
INFORMATION: myData is "first foo"
INFORMATION: myData is "second foo"
INFORMATION: myData is "third foo"
INFORMATION: ok action
I understand that myComponent is a single instance inside of ui:repeat. So what is the best way to save component state so it is restored correctly for each row in the dataset?
My XHTML form:
<h:form>
<ui:repeat var="s" value="#{myController.data}">
<my:myComponent data="#{s}"/>
</ui:repeat>
<h:commandButton action="#{myController.okAction}" value="ok">
<f:ajax execute="#form" render="#form"/>
</h:commandButton>
</h:form>
My Bean:
#Named
#ViewScoped
public class MyController implements Serializable {
private static final long serialVersionUID = -2916212210553809L;
private static final Logger LOG = Logger.getLogger(MyController.class.getName());
public List<String> getData() {
return Arrays.asList("first","second","third");
}
public void okAction() {
LOG.info("ok action");
}
}
Composite component XHTML code:
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:cc="http://xmlns.jcp.org/jsf/composite">
<cc:interface componentType="myComponent">
<cc:attribute name="data"/>
</cc:interface>
<cc:implementation>
<h:panelGrid columns="2">
<h:outputLabel value="cc.attrs.data"/>
<h:outputText value="#{cc.attrs.data}"/>
<h:outputLabel value="cc.myData"/>
<h:outputText value="#{cc.myData}"/>
</h:panelGrid>
</cc:implementation>
</ui:component>
Composite Component backing class:
#FacesComponent
public class MyComponent extends UIInput implements NamingContainer {
private static final Logger LOG=Logger.getLogger(MyComponent.class.getName());
public String calculateData() {
return String.format("%s foo", this.getAttributes().get("data") );
}
public String getMyData() {
return (String)getStateHelper().get("MYDATA");
}
public void setMyData( String data ) {
getStateHelper().put("MYDATA", data);
}
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
this.setMyData( calculateData() );
super.encodeBegin(context);
}
#Override
public void processDecodes(FacesContext context) {
super.processDecodes(context);
LOG.log(Level.INFO, "myData {0}", getMyData() );
}
}
Just tried reproducing your issue and yes, now I get what you're after all. You just wanted to use the JSF component state as some sort of view scope for the calculated variables. I can understand that. The observed behavior is indeed unexpected.
In a nutshell, this is explained in this blog of Leonardo Uribe (MyFaces committer): JSF component state per row for datatables.
The reason behind this behavior is tags like h:dataTable or ui:repeat only save properties related with EditableValueHolder interface (value, submittedValue, localValueSet, valid). So, a common hack found to make it work correctly is extend your component from UIInput or use EditableValueHolder interface, and store the state you want to preserve per row inside "value" field.
[...]
Since JSF 2.1, UIData implementation has a new property called rowStatePreserved. Right now this property does not appear on facelets taglib documentation for h:dataTable, but on the javadoc for UIData there is. So the fix is very simple, just add rowStatePreserved="true" in your h:dataTable tag:
In the end, you have basically 3 options:
Use UIInput#value instead of something custom like MYDATA
As instructed by the abovementioned blog, just replace getMyData() and setMyData() by the existing getValue() and setValue() methods from UIInput. Your composite component already extends from it.
#Override
public void encodeBegin(FacesContext context) throws IOException {
this.setValue(calculateData()); // setValue instead of setMyData
super.encodeBegin(context);
}
#Override
public void processDecodes(FacesContext context) {
super.processDecodes(context);
LOG.log(Level.INFO, "myData {0}", getValue() ); // getValue instead of getMyData
}
And equivalently in the XHTML implementation (by the way, the <h:outputText> is unnecessary here):
<h:outputText value="#{cc.value}" /> <!-- cc.value instead of cc.myData -->
However, this didn't really work when I tried it on Mojarra 2.3.14. It turns out that Mojarra's implementation of the <ui:repeat> indeed restores the EditableValueHolder state during restore view (yay!), but then completely clears out it during decode (huh?), turning this a bit useless. I'm frankly not sure why it is doing that. I have also found in Mojarra's UIRepeat source code that it doesn't do that when it's nested in another UIData or UIRepeat. So the following little trick of putting it in another UIRepeat attempting to iterate over an empty string made it work:
<ui:repeat value="#{''}">
<ui:repeat value="#{myController.data}" var="s">
<my:myComponent data="#{s}" />
</ui:repeat>
</ui:repeat>
Remarkably is that nothing of this all worked in MyFaces 2.3.6. I haven't debugged it any further.
Replace <ui:repeat> by <h:dataTable rowStatePreserved="true">
As hinted in the abovementioned blog, this is indeed documented in UIData javadoc. Just replace <ui:repeat> by <h:dataTable> and explicitly set its rowStatePreserved attribute to true. You can just keep using your MYDATA attribute in the state.
<h:dataTable value="#{myController.data}" var="s" rowStatePreserved="true">
<h:column><my:myComponent data="#{s}" /></h:column>
</h:dataTable>
This worked for me in both Mojarra 2.3.14 and MyFaces 2.3.6.
This is unfortunately not supported on UIRepeat. So you'll have to live with a potentially unnecessary HTML <table> markup generated by the <h:dataTable>. It was during JSF 2.3 work however discussed once to add the functionality to UIRepeat, but unfortunately nothing was done before JSF 2.3 release.
Include getClientId() in state key
As suggested by Selaron in your question's comments, store the client ID along as key in the state.
public String getMyData() {
return (String) getStateHelper().get("MYDATA." + getClientId());
}
public void setMyData(String data) {
getStateHelper().put("MYDATA." + getClientId(), data);
}
Whilst it's a relatively trivial change, it's awkward. This does not infer portability at all. You'd have to hesitate and think twice every time you implement a new (composite) component property which should be saved in JSF state. You'd really expect JSF to automatically take care of this.

Implement autocomplete (typeahead with inputText) with BootsFaces

I develop a web application in Java EE, in this one there is an inputText allowing to search for a student according to his name.
However, I am faced with a problem that I cannot find the solution to.
I use an inputText with a typeahead (Bootsfaces), if I send it a List Etudiant (My List Object) it works however when I send it a List String no suggestion appears:/
In my controller (Java), I return a List containing the name and surname of each student and I would like to be able to make appear the suggestion of this list.
There is my xHtml code :
<b:inputText style="width:200px" value="" placeholder="Rechercher étudiant" typeahead="true" typeahead-values="#{etudiantController.getEtudiants()}"/>
There is my Controller (etudiantController) code :
public List<String> getEtudiants() {
etudiants = gestionEtudiant.selectAll();
List<String> listeNomPrenom = new ArrayList<String>();
for(Etudiant e : etudiants) {
listeNomPrenom.add(e.getNom() + " " + e.getPrenom());
}
return listeNomPrenom;
}
I hope not to disturb with my post, thanks in advance ;)
So there are several things to address here...
First of all, you need a backing bean value in order for the component to have a proper reference value. Not setting value might work for the auto completion depending on how the component is implemented, but you won't have access to what the user actually entered later in your controller. With some components it might make the component function in an undesirable way. So you need to connect it to a bean property.
Secondly, typeahead-values is expecting either a straight up string, or a bean property. Only in very special circumstances will you ever need to call the getter of a bean property - so you should reference the property.
Thirdly, instead of returning a new list of students, try to take advantage of Java's built in toString() functionality in your typeahead-values. Then you don't have to create a completely new list, but instead can rely on Java doing the conversion for you.
So a complete solution mimicing what you are trying to do and translated to English would look like this;
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:b="http://bootsfaces.net/ui">
<h:head>
<title>Autocomplete test</title>
</h:head>
<h:body>
<h:form>
<b:inputText value="#{studentBean.studentEntry}" placeholder="Search student"
typeahead="true" typeahead-values="#{studentBean.students}"/>
</h:form>
</h:body>
</html>
#Data
#Named
#ViewScoped
public class StudentBean implements Serializable {
private List<Student> students;
private String studentEntry;
#PostConstruct
public void init() {
students = new ArrayList<>();
students.add(new Student("Carl", "Sagan"));
students.add(new Student("Enrico", "Fermi"));
students.add(new Student("Jay", "Miner"));
}
#Data
#AllArgsConstructor
public class Student {
private String firstName;
private String lastName;
#Override
public String toString() {
return String.format("%s %s", lastName, firstName);
}
}
}
Note that this example uses Lombok - so the #Data annotation creates the needed setters and getters for the properties.
Also notice how toString() actually flips the name and puts the surname first
just like you do in your code.

Composite component causes unique ID error when binding to view scoped bean

We are migrating a JSF 2.1 application, from JBoss AS 7.2 to Wildfly and thus JSF 2.2. The problem We're having is the following: We have a compositecomponent that is included in a #ViewScoped bean. The component has to retain its value through multiple requests, so a Request Scoped bean is not a solution.
The exception we're getting is a multiple component id one. After the request JSF starts to render the component for the second time, and fails.
I made a simple demo for this:
MyViewBean.java
#ViewScoped
#Named
public class MyViewBean implements Serializable {
private Component component;
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
}
public String increment(){
component.setCounter(component.getCounter()+1);
return "";
}
}
Component.java
#FacesComponent(value = "composite")
public class Component extends UINamingContainer {
private Integer counter = 0;
public Integer getCounter() {
return counter;
}
public void setCounter(Integer counter) {
this.counter = counter;
}
}
compositeTest.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
template="/WEB-INF/templates/default.xhtml"
xmlns:pelda="http://xmlns.jcp.org/jsf/composite/component">
<ui:define name="content">
<h1>Composite component Test!</h1>
<h:form>
<pelda:composite binding="#{myViewBean.component}" />
<h:commandButton action="#{myViewBean.increment()}" value="Push me!"/>
</h:form>
</ui:define>
</ui:composition>
composite.xhtml
<cc:interface componentType="composite">
</cc:interface>
<cc:implementation>
<h:outputText id="id_hello" value="Helloka" />
<h:outputText id="id_counter" value="#{cc.counter}" />
</cc:implementation>
</html>
How to achieve that the counter can be incremented (with #RequestScoped bean it resets) and won't fail with idUniqueness error? We're using Mojarra 2.2.8 (Default in wildfly), also tried with Mojarra 2.2.12 (the latest as per writing this).
Thanks in advance!
UIComponent instances are inherently request scoped. You should never reference UIComponent instances beyond the request scope. Carefully read How does the 'binding' attribute work in JSF? When and how should it be used? for an elaborate explanation on that.
You only want to save its state in the JSF state via the inherited getStateHelper() method. This acts basically as the view scope.
#FacesComponent(value = "composite")
public class Component extends UINamingContainer {
public Integer getCounter() {
return (Integer) getStateHelper().eval("counter", 0);
}
public void setCounter(Integer counter) {
getStateHelper().put("counter", counter);
}
}
Don't forget to get rid of the binding attribute in the view.
See also:
How to save state when extending UIComponentBase

#PostConstruct method is not called

I wanted to use #PostConstruct to initialize a bean in my webapp but I can't get it to work.
I've recreated the problem in a new project and it still won't work.
Am I missing something obvious here? As far as I can tell my init() method fulfills all the requirements listed in #PostConstruct API reference.
MyBean.java:
#ManagedBean
#RequestScoped
public class MyBean {
#ManagedProperty(value="15")
private int number = 10;
#PostConstruct
public void init(){
number = 20;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
number.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Simple JSF Facelets page</title>
</h:head>
<h:body>
Number = #{myBean.number}
</h:body>
</html>
I would expect Number = 20 but I get Number = 15.
#PostConstruct seems to be called before the injection with #ManagedProperty, assuming you have MyFaces 2.0, as they say here.
Make sure you are using Mojarra 2.1 because it should work.
You might try to debug to know if your init() method is called before the injection, or never called.
By default, Spring will not aware of the #PostConstruct and #PreDestroy annotation. To enable it, you have to either register CommonAnnotationBeanPostProcessor or specify the <context:annotation-config /> in bean configuration file.

valueChangeListener method is called on selectOneMenu even though I have not changed the value

I have the following problem. When I click the button "Enviar", this calls another method that is associated to a selectOneMenu (in the attribute
valueChangeListener called "validarSelect"), and later, calls the method that this button has associated in the attribute actionListener called "validarBoton".
I wonder, why this happens. I expect the valueChangeListener to be not called since I have not changed the dropdown.
This is my page JSF:
<?xml version='1.0' encoding='windows-1252'?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html">
<html xmlns="http://www.w3.org/1999/xhtml">
<h:head></h:head>
<h:body>
<h:form>
<h:commandButton value="Enviar..." id="validar" actionListener="#{Domiciliacion.validarBoton}"/>
<h:selectOneMenu valueChangeListener="#{Domiciliacion.validarSelect}"
binding="#{Domiciliacion.selectCombo}">
<f:selectItems value="#{Domiciliacion.lista}"/>
<f:ajax event="valueChange" render="#this"/>
</h:selectOneMenu>
</h:form>
</h:body>
</html>
And this, is the ManagedBean:
package domiciliaciontest;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
#ManagedBean(name = "Domiciliacion")
#ViewScoped
public class MB0001 {
private HtmlSelectOneMenu selectCombo;
private List<String> lista = new ArrayList<String>();
public MB0001() {
super();
System.out.println("Entro al constructor...");
lista.add("Caracas");
lista.add("Bogota");
lista.add("Santiago");
}
public void validarBoton(ActionEvent actionEvent) {
System.out.println("Entro a validarBoton...");
// Add event code here...
}
public void validarSelect(ValueChangeEvent valueChangeEvent) {
// Add event code here...
System.out.println("Entro a validarSelect...");
}
public void setSelectCombo(HtmlSelectOneMenu selectCombo) {
this.selectCombo = selectCombo;
}
public HtmlSelectOneMenu getSelectCombo() {
return selectCombo;
}
public void setLista(List<String> lista) {
this.lista = lista;
}
public List<String> getLista() {
return lista;
}
}
this is the output when I click the button "Enviar":
Entro a validarSelect...
Entro a validarBoton...
The valueChangeListener method will be invoked when the submitted value is different from the initial value, regardless of whether you have changed it yourself or not. So, if the currently submitted value (which is "Caracas" in your case) is different from the initial value (which is null in your case), then the valueChangeListener method will be invoked.
See also:
When to use valueChangeListener or f:ajax listener?
Best way to add a "nothing selected" option to a selectOneMenu in JSF
Unrelated to the concrete problem, seeing this in combination with binding attribute gives me the impression that you're trying to achieve something which you've read in an article or answer targeted on JSF 1.x. This is namely recognizeable as part of a hack to populate child dropdowns in JSF 1.x. You do not need this approach for JSF 2.x. Further, your method names with "validar" ("validate") are misleading. Don't you actually need a fullworthy Validator? But as said, that's a different problem.
See also:
Make multiple dependent / cascading selectOneMenu dropdown lists in JSF

Resources