I am reading Java EE 7 tutorial. In chapter 13.12, there is an example application, ajaxguessnumber. I run the example in Glassfish 4 and everything works fine. I then put System.out.println in bean constructor and I realized that the constructor is being called twice during initial page loading. Why is that so, even for #SessionScoped bean?
Here is the xhtml file
<!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://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<h:outputStylesheet library="css" name="default.css"/>
<title>Ajax Guess Number Facelets Application</title>
</h:head>
<h:body>
<h:form id="AjaxGuess">
<h:graphicImage value="#{resource['images:wave.med.gif']}"
alt="Duke waving his hand"/>
<h2>
Hi, my name is Duke. I am thinking of a number from
#{dukesNumberBean.minimum} to #{dukesNumberBean.maximum}.
Can you guess it?
</h2>
<p>
<h:inputText
id="userNo"
title="Type a number from 0 to 10:"
value="#{userNumberBean.userNumber}">
<f:validateLongRange
minimum="#{dukesNumberBean.minimum}"
maximum="#{dukesNumberBean.maximum}"/>
</h:inputText>
<h:commandButton id="submit" value="Submit" >
<f:ajax execute="userNo" render="outputGroup" />
</h:commandButton>
</p>
<p>
<h:panelGroup layout="block" id="outputGroup">
<h:outputText id="result" style="color:blue"
value="#{userNumberBean.response}" rendered="#{!facesContext.validationFailed}"/>
<h:message id="errors1"
showSummary="true"
showDetail="false"
style="color: #d20005;
font-family: 'New Century Schoolbook', serif;
font-style: oblique;
text-decoration: overline"
for="userNo"/>
</h:panelGroup>
</p>
</h:form>
</h:body>
</html>
Here is the bean DukesNumberBean
package javaeetutorial.ajaxguessnumber;
import java.io.Serializable;
import java.util.Random;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class DukesNumberBean implements Serializable {
private Integer randomInt = null;
private long maximum = 10;
private long minimum = 0;
public DukesNumberBean() {
System.out.println("Inside DukesNumberBean constructor");
Random randomGR = new Random();
long range = maximum+minimum+1;
randomInt = (int) (minimum + randomGR.nextDouble()*range);
System.out.println("Duke's number: " + randomInt);
}
public long getMaximum() {
return (this.maximum);
}
public void setMaximum(long maximum) {
this.maximum = maximum;
}
public long getMinimum() {
return (this.minimum);
}
public void setMinimum(long minimum) {
this.minimum = minimum;
}
/**
* #return the randomInt
*/
public Integer getRandomInt() {
return randomInt;
}
/**
* #param randomInt the randomInt to set
*/
public void setRandomInt(Integer randomInt) {
this.randomInt = randomInt;
}
}
And here's the bean UserNumberBean
package javaeetutorial.ajaxguessnumber;
import java.io.Serializable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
#Named
#RequestScoped
public class UserNumberBean implements Serializable {
#Inject
DukesNumberBean dukesNumberBean;
private Integer userNumber = null;
String response = null;
public UserNumberBean()
{
System.out.println("Inside constructor");
}
public void setUserNumber(Integer user_number) {
userNumber = user_number;
}
public Integer getUserNumber() {
return userNumber;
}
public String getResponse() {
if ((userNumber != null) && (userNumber.compareTo(dukesNumberBean.getRandomInt()) == 0)) {
return "Yay! You got it!";
}
if (userNumber == null) {
return null;
} else {
return "Sorry, " + userNumber + " is incorrect.";
}
}
}
Any help would be much appreciated. Thank you.
This happens because the scoped objects and injected by CDI using proxies. CDI has first to create a proxy of your object that is a subclass of your real object - this is the first time your constructor gets called because when you instantiate a subclass the parent constructor is always called. And then CDI instantiates your real object in order to inject it - this is the second time your constructor gets called.
A better approach is to put your initialization login in a #PostConstruct method like this.
#PostConstruct
public void init() {
Random randomGR = new Random();
long range = maximum+minimum+1;
randomInt = (int) (minimum + randomGR.nextDouble()*range);
System.out.println("Duke's number: " + randomInt);
}
It will be called only once - when the real object gets instantiated. More info about the proxies you can find here.
EDIT:
I just found another explanation of the problem - here.
Related
Given the component: https://www.primefaces.org/showcase/ui/input/oneMenu.xhtml
Full source for my test is at: https://github.com/dannymk/PrimefacesTest
Can't get the component to work with an Object using a converter. Not sure how to solve this one.
package org.primefaces.test;
import java.io.Serializable;
public class Player implements Serializable {
private Integer id;
private String name;
Player(Integer id, String name){
this.id = id;
this.name = name;
}
/**
* #return the id
*/
public Integer getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(Integer id) {
this.id = id;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
package org.primefaces.test;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
*
* #author Daniel Maldonado
*/
public class PlayerService implements Serializable{
private List<Player> available = new ArrayList<>();
public PlayerService() {
available.add(new Player(1, "One"));
available.add(new Player(2, "Two"));
available.add(new Player(3, "Three"));
available.add(new Player(4, "Four"));
available.add(new Player(5, "Five"));
}
/**
* #return the available
*/
public List<Player> getAvailable() {
return available;
}
/**
* #param available the available to set
*/
public void setAvailable(List<Player> available) {
this.available = available;
}
}
package org.primefaces.test;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.inject.Inject;
import javax.inject.Named;
/**
*
* #author Daniel Maldonado
*/
#Named
#FacesConverter(value = "playerConverter", managed = true)
public class PlayerConverter implements Converter<Player> {
#Inject
PlayerService service;
#Override
public Player getAsObject(FacesContext context, UIComponent component, String value) {
if (value != null && value.trim().length() > 0) {
Player found = service.getAvailable().stream()
.filter(player -> player.getName().equals(value))
.findAny()
.orElse(null);
return found;
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Player o) {
if (o != null){
return o.getName();
}
return null;
}
}
package org.primefaces.test;
import java.io.Serializable;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import lombok.Data;
#Data
#Named
#ViewScoped
public class TestView implements Serializable {
private String hello;
private List<Player> available;
private Player selected;
#Inject
PlayerService service;
#PostConstruct
public void init() {
setHello("Welcome to PrimeFaces!!!");
this.available = service.getAvailable();
}
/**
* #return the available
*/
public List<Player> getAvailable() {
return available;
}
/**
* #param available the available to set
*/
public void setAvailable(List<Player> available) {
this.available = available;
}
/**
* #return the selected
*/
public Player getSelected() {
return selected;
}
/**
* #param selected the selected to set
*/
public void setSelected(Player selected) {
this.selected = selected;
}
/**
* #return the hello
*/
public String getHello() {
return hello;
}
/**
* #param hello the hello to set
*/
public void setHello(String hello) {
this.hello = hello;
}
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>PrimeFaces Test - SelectOneMenu</title>
<h:outputScript name="test.js" />
</h:head>
<h:body>
<h1>#{testView.hello}</h1>
<h:form id="frmTest">
<p:panelGrid id="playerPanel" columns="2">
<p:outputLabel value="Selected player: #{testView.selected.name}" />
<p:selectOneMenu id="playerContainer" value="#{testView.selected}" var="p" converter="playerConverter" >
<f:selectItem itemLabel="Select One" itemValue="" />
<f:selectItems value="#{testView.available}" var="actual" itemLabel="#{actual.name}" itemValue="#{actual}" />
<p:column>
#{p.name}
</p:column>
<p:ajax event="valueChange" update="playerPanel" />
</p:selectOneMenu>
</p:panelGrid>
</h:form>
</h:body>
</html>
I get errors like:
java.lang.ClassCastException: java.lang.String cannot be cast to org.primefaces.test.Player
at org.primefaces.test.PlayerConverter.getAsString(PlayerConverter.java:38)
It does not seem to matter what I change and believe me I have tried plenty of implementations of the converter and in the view using "#{palyerConverter}" instead of "playerConverter" and still can't get it to work.
Your problem is at this line:
<f:selectItem itemLabel="Select One" itemValue="" />
Currently you are passing an empty string as the itemValue, which is not expected by your converter. Either use itemValue="#{null}" or totally remove the itemValue from the no select option.
See also:
Best way to add a "nothing selected" option to a selectOneMenu in JSF
Finally got it to work with just a few changes thanks to #Jasper de Vries:
<?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://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>PrimeFaces Test - SelectOneMenu</title>
<h:outputScript name="test.js" />
</h:head>
<h:body>
<h1>#{testView.hello}</h1>
<h:form id="frmTest">
<p:panelGrid id="playerPanel" columns="2">
<p:outputLabel value="Selected player: #{testView.selected.name}" />
<p:selectOneMenu id="playerContainer" value="#{testView.selected}" var="p" converter="playerConverter">
<f:selectItems value="#{testView.available}" var="actual" itemLabel="#{actual.name}" itemValue="#{actual}" />
<p:column>
#{p.name}
</p:column>
<p:ajax event="valueChange" update="playerPanel" />
</p:selectOneMenu>
</p:panelGrid>
</h:form>
</h:body>
</html>
and specially don't forget to implement the EQUALS method in the Object that is "listed":
package org.primefaces.test;
import java.io.Serializable;
import java.util.Objects;
public class Player implements Serializable {
private Integer id;
private String name;
Player(Integer id, String name){
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object obj) {
if(obj == null)
return false;
if(!(obj instanceof Player))
return false;
Player compare = (Player) obj;
return Objects.equals(compare.getId(), this.getId());
}
#Override
public int hashCode() {
int hash = 1;
return hash * 31 + this.getName().hashCode();
}
/**
* #return the id
*/
public Integer getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(Integer id) {
this.id = id;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
I am running into a very odd issue where the selectOneMenu jsf control is showing the wrong value as selected one (even though the model gets updated with the correct value) after there was a validation error on a completely different field on a previous (not current) submit.
How to replicate (minimum code below):
load the form and leave the "Required text field" text box blank.
Set some values to Active and Inactive in a couple of dropdowns
below.
Submit the form. There will be an error message about the
"Required text field" being blank and the drop downs you set will
still have the same value you gave them (expected). The printout of
what the model has will still show the old value and not the value
you selected (expected).
Put some text into the "Required text
field" and submit the form.
The error message goes away and the
form gets "saved". The printouts of what the model has next to the
dropdowns have the value you set (expected), however, the dropdowns
themselves no longer have the value you selected as the selected
option (NOT EXPECTED).
Sample 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://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form id="myForm">
<div>
<h:outputLabel value="Required text field" for="reqField" />
<h:inputText value="#{myBeanController.requiredField}" id="reqField" label="Required Text Field" required="true" />
</div>
<div>
<h:outputLabel value="some objects" />
<ui:repeat value="#{myBeanController.bunchOfObjects}" var="obj">
<div>
value on obj model: #{obj}
<h:selectOneMenu value="#{obj.type}">
<f:selectItems value="#{myBeanController.availableRequiredTypeOptions}" />
</h:selectOneMenu>
</div>
</ui:repeat>
</div>
<div>
<h:commandButton value="Submit" action="#{myBeanController.save}" />
</div>
</h:form>
</h:body>
</html>
Controller bean:
package com.mycompany.reproducefieldnotsavingaftervalidation;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.apache.commons.lang3.text.WordUtils;
#ManagedBean
#SessionScoped
public class MyBeanController {
private String requiredField;
private Collection<MyDomainClass> bunchOfObjects;
#PostConstruct
public void init(){
// create some sample objects
bunchOfObjects = new LinkedList<>();
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.ACTIVE));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.INACTIVE));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
}
public void save() {
System.out.println("SAVING FORM");
System.out.println("requiredField: " + getRequiredField());
System.out.println("Bunch of objects: ");
for(MyDomainClass obj : getBunchOfObjects()) {
System.out.println("/tObj: " + obj);
}
}
public Map<String, String> getAvailableRequiredTypeOptions() {
Map<String, String> options = new TreeMap<>();
for(MyDomainClass.PossibleTypes type : MyDomainClass.PossibleTypes.values()) {
// make the text of the option pretty by removing all caps and replacing underscores with space
options.put(WordUtils.capitalizeFully(type.name(), new char[]{'_'}).replaceAll("_", " "), type.name());
}
return options;
}
public String getRequiredField() {
return requiredField;
}
public void setRequiredField(String requiredField) {
this.requiredField = requiredField;
}
public Collection<MyDomainClass> getBunchOfObjects() {
return bunchOfObjects;
}
public void setBunchOfObjects(Collection<MyDomainClass> bunchOfObjects) {
this.bunchOfObjects = bunchOfObjects;
}
}
Model:
package com.mycompany.reproducefieldnotsavingaftervalidation;
import java.util.Objects;
public class MyDomainClass {
private String type;
public MyDomainClass() { }
public MyDomainClass(PossibleTypes type) {
this.type = type.name();
}
public MyDomainClass(String type) {
this.type = type;
}
public static enum PossibleTypes {
UNKNOWN, ACTIVE, INACTIVE
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public String toString() {
return "MyDomainClass{" + "type=" + type + '}';
}
#Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.getType());
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof MyDomainClass)) {
return false;
}
final MyDomainClass other = (MyDomainClass) obj;
if (!Objects.equals(this.getType(), other.getType())) {
return false;
}
return true;
}
}
Switching the
<ui:repeat>
to
<c:forEach>
Seems to have fixed the issue. I don't know why the ui:repeat was causing issues only on a save after previously failed validation and not on a first valid save (if anyone know the answer to this, please post it, I would be interested to know). Based on some research, ui:repeat happens at a different phase than f:selectItem or f:selectItems, but then I would expect the issue to happen on every save and it's not.
Regardless, if you're seeing something similar and your selects are inside of a ui:repeat, try switching that out with a c:forEach to see if that fixes it.
I try to learn JSF and encountered on a problem connected with ManagedProperty. However I have tried to use it, it always failed - null exception pointer. What am I doing wrongly?
I have read some "similar posts" on stackoverflow, but they did not helped to me. (I use GlassFish 4.0, JSF 2.2, JDK 1.7, Netbeans 7.3.1 (Java EE pack) and Java EE 6.0)
<?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">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
Hello from Facelets
<br/>
User: #{books.user.name}<br/>
1: #{param.pageId}<br/>
2: #{books.pageId}<br/>
<h:form>
<h:inputText value="#{user.name}" /><br/>
<h:inputText value="#{books.v1}" /><br/>
<h:inputText value="#{books.v2}" /><br/>
<h:inputText value="#{books.result}" /><br/>
<h:commandButton value="dodaj" action="#{books.add}" />
</h:form>
</h:body>
</html>
Book
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package tpsa.books.managed;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedProperty;
/**
*
* #author tomasz
*/
#Named(value = "books")
#RequestScoped
public class Books {
private int v1;
private int v2;
private int result;
#ManagedProperty(value = "#{user}")
private User user;
#ManagedProperty(value="#{param.pageId}")
private int pageId;
/**
* Creates a new instance of Books
*/
public Books() {
}
public void add() {
result = v1 + v2;
}
public int getV1() {
return v1;
}
public void setV1(int v1) {
this.v1 = v1;
}
public int getV2() {
return v2;
}
public void setV2(int v2) {
this.v2 = v2;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public User getUser() {
if (user == null) {
System.err.println("WTF");
}
return user;
}
public void setUser(User user) {
this.user = user;
}
public int getPageId() {
return pageId;
}
public void setPageId(int pageId) {
this.pageId = pageId;
}
}
User
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package tpsa.books.managed;
import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
import java.io.Serializable;
/**
*
* #author tomasz
*/
#Named(value = "user")
#SessionScoped
public class User implements Serializable {
private String name;
/**
* Creates a new instance of User
*/
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#ManagedProperty is managed bean annotaion, that can't be used with CDI. In above code, you used CDI bean i.e. #Named that is default in JSF 2.2. In this case you can't use ManagedProperty. Please read following line copied from Java EE docs of ManagedBean.
If this annotation is present on a class that does not have the
ManagedBean annotation, the implementation must take no action on this
annotation.
For details see the link:
http://docs.oracle.com/javaee/6/api/javax/faces/bean/ManagedProperty.html
So, use #Inject instead of #ManagedProperty for CDI bean.
#Inject
private User user;
Note that a getter/setter is unnecessary here.
I am running the duke guess number example , I don't see the life cycle executing as expected. I have an inputText which expects a number. We also have a converter and a validator. Once I submit the <h:form> the validator is called. It should check the input if it is an integer. The value is not getting updated on the managed bean property. Please explain, below is the code snippet.
<h:inputText id="userNo" label="User Number" value="#{UserNumberBean.userNumber}"
converterMessage="#{ErrMsg.userNoConvert}" validator="#{UserNumberBean.validate}">
<f:valueChangeListener type="#{UserNumberBean.valueChange()}"></f:valueChangeListener>
<f:validateLongRange minimum="#{UserNumberBean.minimum}" maximum="#{UserNumberBean.maximum}" />
</h:inputText>
here the validator method is set in inputText component.
Once I submit the <h:form>, the validate method is called, below is the method
public String validate(javax.faces.context.FacesContext fc, javax.faces.component.UIComponent ui, java.lang.Object o){
System.out.println("in my own validation method");
if(userNumber ==8){
return "validation";
}
return "validation";
}
here userNumber is the backing bean property of the class
public class UserNumberBean {
public Integer userNumber = null;
public void setUserNumber(Integer user_number) {
System.out.println("setting userName" + user_number);
userNumber = user_number;
}
public Integer getUserNumber() {
return userNumber;
}
}
it has getters and setter but still userNumber value is not set. I get NullPointerException in validator method when accessing userNumber. Please let me know what is wrong. Below is the code snippet
<HTML xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<HEAD> <title>Hello</title> </HEAD>
<%# page contentType="application/xhtml+xml" %>
<%# taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%# taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<body bgcolor="white">
<f:view>
<h:form id="helloForm" >
<h2>Hi. My name is Duke. I'm thinking of a number Man from
<h:outputText lang="en_US" value="#{UserNumberBean.minimum}"/> to
<h:outputText value="#{UserNumberBean.maximum}"/>. Can you guess
it?</h2>
<h:graphicImage id="waveImg" url="/wave.med.gif" alt="Duke waving" />
<h:inputText id="userNo" label="User Number" value="#{UserNumberBean.userNumber}"
converterMessage="#{ErrMsg.userNoConvert}" validator="#{UserNumberBean.validate}">
<f:valueChangeListener type="#{UserNumberBean.valueChange()}"></f:valueChangeListener>
<f:validateLongRange minimum="#{UserNumberBean.minimum}" maximum="#{UserNumberBean.maximum}" />
</h:inputText>
<h:commandButton id="submit" action="success" value="Submit" />
<p>
<h:message style="color: red; font-family: 'New Century Schoolbook', serif; font-style: oblique; text-decoration: overline" id="errors1" for="userNo"/>
</p>
</h:form>
</f:view>
</body>
</HTML>
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.LongRangeValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import java.util.Random;
public class UserNumberBean {
Integer randomInt = null;
public Integer userNumber = null;
String response = null;
private boolean maximumSet = false;
private boolean minimumSet = false;
private long maximum = 0;
private long minimum = 0;
public UserNumberBean() {
System.out.println(" in constructor");
Random randomGR = new Random();
randomInt = new Integer(randomGR.nextInt(10));
System.out.println("Duke's number: " + randomInt);
}
public void setUserNumber(Integer user_number) {
System.out.println("setting userName" + user_number);
userNumber = user_number;
}
public Integer getUserNumber() {
return userNumber;
}
public String getResponse() {
System.out.println(" in getResponse");
if ((userNumber != null) && (userNumber.compareTo(randomInt) == 0)) {
return "Yay! You got it!";
} else {
return "Sorry, " + userNumber + " is incorrect.";
}
}
public long getMaximum() {
return (this.maximum);
}
public void setMaximum(long maximum) {
this.maximum = maximum;
this.maximumSet = true;
}
public long getMinimum() {
return (this.minimum);
}
public void setMinimum(long minimum) {
this.minimum = minimum;
this.minimumSet = true;
}
public String validate(javax.faces.context.FacesContext fc, javax.faces.component.UIComponent ui, java.lang.Object o){
System.out.println("in my own validation method");
if(userNumber ==8){
return "validation";
}
return "validation";
}
public void valueChange(){
System.out.println(" in value change");
}
}
What actually is the Apply Request phase? What happens in Apply Request phase? How different is it from Update Model Values phase
Your validator is broken in 2 ways:
Wrong method signature. It should return void. On validation faliures, you should be throwing a ValidatorException. On success you should just be returning and doing nothing additional.
You should be validating the value provided as 3rd argument, not the model value (which isn't been set at that point at all).
So, this should do:
public void validate(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return; // Ignore it. Let required="true" handle.
}
if (value != 8) {
// Assuming that 8 is the guess number?
throw new ValidatorException(new FacesMessage("Wrong guess, try again."));
}
}
Your NullPointerException is caused because you're attempting to compare the model value userNumber to a primitive integer 8. This causes autoboxing to try to unbox userNumber to a primitive, however that fails if the userNumber itself is null which cannot be represented in any primitive form.
As to the phases, the apply request values phase is definitely called, the validations phase is otherwise never called. You seem to expect that the apply request values phase updates the model values. This is not true, it applies the request parameters on JSF input component's submittedValue.
See also:
Difference between Apply Request Values and Update Model Values
Debug JSF lifecycle
try to write a composite component that allows mutltiple text inputs. I read that it is possible to define a backing component for a composite component, so I don't have to write a renderer nor a handler. What I couldn't figure out is how to delegate actions declared in composite's xhtml to the backing component. I guess i did not yet quite understand the concept of this. Does anybody has an Idea?
I am using Tomcat 7, EL 2.2, Spring 3, Mojarra 2.1.7
This is the way i'd like to use the component:
<custom:multiInput value="#{backingBean.inputList}"/>
Where the BackingBean.java holds a list of objects:
#Component
#Scope(value="view")
public class BackingBean {
...
private List<Foo> inputList;
....
}
The composite component multiInput.xhtml looks like this:
<cc:interface componentType="MultiInput">
<cc:attribute name="value" required="true" type="java.util.List" />
</cc:interface>
<cc:implementation>
<div id="#{cc.clientId}">
<h:dataTable value="#{cc.attrs.rows}" var="row">
<h:column>
<!-- here will be a selector component in order to select a foo object -->
</h:column>
<h:column>
<h:commandButton value="Remove Row">
<f:ajax execute=":#{cc.clientId}" render=":#{cc.clientId}" listener="#{cc.removeRow(row)}" />
</h:commandButton>
</h:column>
<h:column>
<h:commandButton value="Add Row" rendered="#{cc.lastRow}">
<f:ajax execute=":#{cc.clientId}" render=":#{cc.clientId}" listener="#{cc.addEmptyRow()}" />
</h:commandButton>
</h:column>
</h:dataTable>
</div>
</cc:implementation>
And here the backing component MultiInput.java:
#FacesComponent(value="MultiInput")
public class MultiInput extends UIInput implements NamingContainer, Serializable{
...
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
initRowsFromValueAttribute();
super.encodeBegin(context);
}
public void removeRow(MultiInputRow row) {
// why is this method is never reached when clicking remove button?
}
public void addEmptyRow() {
// why is this method is never reached when clicking add button?
}
public ListDataModel<MultiSelectRow> getRows() {
return (ListDataModel<MultiSelectRow>) getStateHelper().eval(PropertyKeys.rows, null);
}
private void setRows(ListDataModel<MultiSelectRow> rows) {
getStateHelper().put(PropertyKeys.rows, rows);
}
...
}
Now - removeRow and addEmptyRow is never called on MultiInput. An ajax request is triggered but it gets lost somewhere. Why?
I think the method signature for ajax listener methods should include the AjaxBehaviorEvent (unverified):
public void addEmptyRow(AjaxBehaviorEvent event) { ... }
and the f:ajax tag should just look like (without parentheses):
<f:ajax execute=":#{cc.clientId}" render=":#{cc.clientId}" listener="#{cc.addEmptyRow}" />
I'm struggling with the same problem here: using <f:ajax>, action listener methods in the composite component backing component are not executed.
It works partially when using Primefaces <p:commandButton>: the action listener method is correctly called in this case. However, the value of the 'process' attribute seems to be ignored in this case: All form fields are submitted, which causes validation failure in my case. If this is not a problem for you, you could try this.
I have created some test classes that reproduce the problem:
The composite component file testComponent.xhtml:
<html xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface componentType="testComponent">
</composite:interface>
<composite:implementation>
<div id="#{cc.clientId}">
<h:panelGroup id="addPanel">
<h:inputText id="operand1" value="#{cc.operand1}"/>
<h:outputText value=" + " />
<h:inputText id="operand2" value="#{cc.operand2}"/>
<h:outputText value=" = " />
<h:outputText id="result" value="#{cc.result}" />
<br />
<p:commandButton id="testButton1" value="Primefaces CommandButton"
actionListener="#{cc.add()}" process="addPanel" update="addPanel"/>
<h:commandButton id="testButton2" value="f:ajax CommandButton">
<f:ajax execute="addPanel" render="addPanel" listener="#{cc.add()}" />
</h:commandButton>
</h:panelGroup>
</div>
</composite:implementation>
</html>
The backing component class:
package be.solidfrog.pngwin;
import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.event.ActionEvent;
#FacesComponent("testComponent")
public class TestComponent extends UINamingContainer {
private Integer operand1, operand2, result;
public void add() {
System.err.println("Adding " + operand1 + " and " + operand2);
result = operand1 + operand2;
}
public Integer getOperand1() { return operand1; }
public void setOperand1(Integer operand1) { this.operand1 = operand1; }
public Integer getOperand2() { return operand2; }
public void setOperand2(Integer operand2) { this.operand2 = operand2; }
public Integer getResult() { return result; }
public void setResult(Integer result) { this.result = result; }
}
And the using page test.xhtml:
<!DOCTYPE html>
<html xmlns="http://www.w3c.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:sf="http://java.sun.com/jsf/composite/solidfrog">
<h:body>
<h:messages />
<h:form id="testForm">
<h:outputLabel for="field1" value="Integer field: "/>
<h:inputText id="field1" value="#{testBean.field1}" />
<hr/>
<sf:testComponent id="testComponent" />
</h:form>
</h:body>
</html>
When clicking the first button and filling in the two operand fields, the result is correctly calculated. However, when a non-numeric value is entered in field1, there is a failed verification.
When using the second button, the action listener method is never calculated. However, the complete form is always submitted, so entering a non-numeric value in field1 triggers the error too.
I also tried p:ajax, which behaved the same as f:ajax.
I really have no idea what is happening here. Hopefully someone with more JSF wisdom can help out.
Although I don't understand everything in detail, I found a way to make it work. Since on each request a new instance of the backing component MultiInput is created, I had to save the state by overwriting saveState and restoreState. This way I could keep the property rows as a simple property. I also removed the encodeBegin method and overwrote getSubmittedValue.
At least this way it is working in Mojarra. When using MyFaces with default settings, I got some serialization exceptions, but I did not get deepter into that since we will stick on Mojarra. Also MyFaces seemed to be more stricked with ajax event listeners. It required "AjaxBehaviorEvent" parameters in listener methods.
Here the complete backing component MultInput:
#FacesComponent(value = "MultiInput")
public class MultiInput extends UIInput implements NamingContainer, Serializable {
ListDataModel<MultiInputRow> rows;
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
#Override
public Object getSubmittedValue() {
List<Object> values = new ArrayList<Object>();
List<MultiInputRow> wrappedData = (List<MultiInputRow>) getRows().getWrappedData();
for (MultiInputRow row : wrappedData) {
if (row.getValue() != null) { // only if a valid value was selected
values.add(row.getValue());
}
}
return values;
}
public boolean isLastRow() {
int row = getRows().getRowIndex();
int count = getRows().getRowCount();
return (row + 1) == count;
}
public boolean isFirstRow() {
int row = getRows().getRowIndex();
return 0 == row;
}
public void removeRow(AjaxBehaviorEvent e) {
List<MultiInputRow> wrappedData = (List<MultiInputRow>) getRows().getWrappedData();
wrappedData.remove(rows.getRowIndex());
addRowIfEmptyList();
}
public void addEmptyRow(AjaxBehaviorEvent e) {
List<MultiInputRow> wrappedData = (List<MultiInputRow>) getRows().getWrappedData();
wrappedData.add(new MultiInputRow(null));
}
public ListDataModel<MultiInputRow> getRows() {
if (rows == null) {
rows = createRows();
addRowIfEmptyList();
}
return rows;
}
public List<Object> getValues() {
return (List<Object>) super.getValue();
}
private ListDataModel<MultiInputRow> createRows() {
List<MultiInputRow> wrappedData = new ArrayList<MultiInputRow>();
List<Object> values = getValues();
if (values != null) {
for (Object value : values) {
wrappedData.add(new MultiInputRow(value));
}
}
return new ListDataModel<MultiInputRow>(wrappedData);
}
private void addRowIfEmptyList() {
List<MultiInputRow> wrappedData = (List<MultiInputRow>) rows.getWrappedData();
if (wrappedData.size() == 0) {
wrappedData.add(new MultiInputRow(null));
}
}
#Override
public Object saveState(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
Object[] values = new Object[2];
values[0] = super.saveState(context);
values[1] = rows != null ? rows.getWrappedData() : null;
return (values);
}
#Override
public void restoreState(FacesContext context, Object state) {
if (context == null) {
throw new NullPointerException();
}
if (state == null) {
return;
}
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
rows = values[1] != null ? new ListDataModel<MultiInputRow>((List<MultiInputRow>) values[1]) : null;
}
/**
* Represents an editable row that holds a value that can be edited.
*/
public class MultiInputRow {
private Object value;
MultiInputRow(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
}