We now started to use JSF 2.3 on our existing JSF 2.2 project. On our custom converters we got warning Converter is a raw type. References to generic type Converter<T> should be parameterized.
Problem that we experiencing is when we tried to fix that warning using generics:
#FacesConverter(value = "myConverter", managed = true)
public class MyConverter implements Converter<MyCustomObject>{
#Override
public MyCustomObject getAsObject(FacesContext context, UIComponent component, String submittedValue){}
#Override
public String getAsString(FacesContext context, UIComponent component, MyCustomObject modelValue) {}
}
and when converter is used for example in
<!DOCTYPE html>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:selectOneMenu id="#{componentId}" value="#{componentValue}">
<f:converter converterId="myConverter" />
<f:selectItem itemLabel="label"
itemValue="" />
<f:selectItems value="listOfValues"
var="singleValue"
itemValue="singleValue.value"
itemLabel="singleValue.label" />
</h:selectOneMenu>
then ClassCastException with message java.lang.String cannot be cast to MyCustomObjectis thrown. There is also one line in stacktrace that maybe can help com.sun.faces.cdi.CdiConverter.getAsString(CdiConverter.java:109).
But when converter generics definition changed from MyCustomObject to Object :
#FacesConverter(value = "myConverter", managed = true)
public class MyConverter implements Converter<Object>{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue){}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {}
}
then everything works as expected, but that obviously beats the purpose of Converter<T> interface.
I had same issue here and came up with following solution that actually not just compiles but also runs well:
some_page.xhtml (relevant excerpt):
<h:selectOneMenu styleClass="select" id="companyUserOwner" value="#{adminCompanyDataController.companyUserOwner}">
<f:converter converterId="UserConverter" />
<f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
<f:selectItems value="#{userController.allUsers()}" var="companyUserOwner" itemValue="#{companyUserOwner}" itemLabel="#{companyUserOwner.userContact.contactFirstName} #{companyUserOwner.userContact.contactFamilyName} (#{companyUserOwner.userName})" />
</h:selectOneMenu>
Please note that the above JSF code is full of custom stuff and may not work on your end. And that my converter compiles without rawtype warning (JSF 2.3+, not 2.2!):
SomeUserConverter.java:
#FacesConverter (value = "UserConverter")
public class SomeUserConverter implements Converter<User> {
/**
* User EJB
*/
private static UserSessionBeanRemote USER_BEAN;
/**
* Default constructor
*/
public SomeUserConverter () {
}
#Override
public User getAsObject (final FacesContext context, final UIComponent component, final String submittedValue) {
// Is the value null or empty?
if ((null == submittedValue) || (submittedValue.trim().isEmpty())) {
// Return null
return null;
}
// Init instance
User user = null;
try {
// Try to parse the value as long
final Long userId = Long.valueOf(submittedValue);
// Try to get user instance from it
user = USER_BEAN.findUserById(userId);
} catch (final NumberFormatException ex) {
// Throw again
throw new ConverterException(ex);
} catch (final UserNotFoundException ex) {
// User was not found, return null
}
// Return it
return user;
}
#Override
public String getAsString (final FacesContext context, final UIComponent component, final User value) {
// Is the object null?
if ((null == value) || (String.valueOf(value).isEmpty())) {
// Is null
return ""; //NOI18N
}
// Return id number
return String.valueOf(value.getUserId());
}
}
This converter class has the JNDI lookup removed (I will rewrite that part later anyway) but it should be enough for demonstrating the important parts:
Converter<User> (by User is a custom POJI) preventing raw-type warning
value="UserConverter" that is the actual name you use in your JSF page
And most important, which actually fixed the ClassCastException:
<f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
By omitting #{null} an empty string instead of null is being handled over to your getAsString method!
This last one actually fixed the said exception and my application is working again with much lesser warnings and much better type-hinting.
Need to remove forClass as value is there: FacesConverter is using both value andforClass, only value will be applied.
The raw-type warning will come (since Java SE 5) because you use an interface which has a generic (mostly stated as <T> after the interface name) which you need to satisfy so the compiler stop throwing that warning at you.
I had two issues with the FacesConverter.
First one, when I didn't use forClass = MyCustomObject.class I got the exact same error message as you had. Putting the forClass attribute in the #FacesConverter annotation solved the issue. It should solve it also for you...
Secondly, I suppose this is happening in the getAsString method? I had another issue with String casting and I had to do: return myCustomObject.getId().toString() where id is of type Long. After that I adapted my getAsObject to fetch the MyCustomObject based on the id. Maybe it is not directly related to your problem, but I thought it was important to mention as it is something I often bump into when writing converters.
I have a checkbox component with a <f:attribute> and a <p:ajax listener>.
<h:selectManyCheckbox ...>
<p:ajax listener="#{locationHandler.setChangedSOI}" />
<f:attribute name="Dummy" value="test" />
...
</h:selectManyCheckbox>
I tried to get the <f:attribute> value of test inside the listener method as below:
public void setChangedSOI() throws Exception {
FacesContext context = FacesContext.getCurrentInstance();
Map<String, String> map = context.getExternalContext().getRequestParameterMap();
String r1 = map.get("Dummy");
System.out.println(r1);
}
However, it printed null. How can I get it?
Component attributes are not passed as HTTP request parameters. Component attributes are set as .. uh, component attributes. I.e. they are stored in UIComponent#getAttributes(). You can grab them through that map.
Now the right question is obviously how to get the desired UIComponent inside the ajax listener method. There are 2 ways for this:
Specify the AjaxBehaviorEvent argument. It offers a getComponent() method for the very purpose.
public void setChangedSOI(AjaxBehaviorEvent event) {
UIComponent component = event.getComponent();
String dummy = component.getAttributes().get("Dummy");
// ...
}
Use the UIComponent#getCurrentComponent() helper method.
public void setChangedSOI() {
UIComponent component = UIComponent.getCurrentComponent(FacesContext.getCurrentInstance());
String dummy = component.getAttributes().get("Dummy");
// ...
}
I'm populating a <p:selectOneMenu/> from database as follows.
<p:selectOneMenu id="cmbCountry"
value="#{bean.country}"
required="true"
converter="#{countryConverter}">
<f:selectItem itemLabel="Select" itemValue="#{null}"/>
<f:selectItems var="country"
value="#{bean.countries}"
itemLabel="#{country.countryName}"
itemValue="#{country}"/>
<p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>
<p:message for="cmbCountry"/>
The default selected option, when this page is loaded is,
<f:selectItem itemLabel="Select" itemValue="#{null}"/>
The converter:
#ManagedBean
#ApplicationScoped
public final class CountryConverter implements Converter {
#EJB
private final Service service = null;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
try {
//Returns the item label of <f:selectItem>
System.out.println("value = " + value);
if (!StringUtils.isNotBlank(value)) {
return null;
} // Makes no difference, if removed.
long parsedValue = Long.parseLong(value);
if (parsedValue <= 0) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Message"));
}
Country entity = service.findCountryById(parsedValue);
if (entity == null) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_WARN, "", "Message"));
}
return entity;
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Message"), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value instanceof Country ? ((Country) value).getCountryId().toString() : null;
}
}
When the first item from the menu represented by <f:selectItem> is selected and the form is submitted then, the value obtained in the getAsObject() method is Select which is the label of <f:selectItem> - the first item in the list which is intuitively not expected at all.
When the itemValue attribute of <f:selectItem> is set to an empty string then, it throws java.lang.NumberFormatException: For input string: "" in the getAsObject() method even though the exception is precisely caught and registered for ConverterException.
This somehow seems to work, when the return statement of the getAsString() is changed from
return value instanceof Country?((Country)value).getCountryId().toString():null;
to
return value instanceof Country?((Country)value).getCountryId().toString():"";
null is replaced by an empty string but returning an empty string when the object in question is null, in turn incurs another problem as demonstrated here.
How to make such converters work properly?
Also tried with org.omnifaces.converter.SelectItemsConverter but it made no difference.
When the select item value is null, then JSF won't render <option value>, but only <option>. As consequence, browsers will submit the option's label instead. This is clearly specified in HTML specification (emphasis mine):
value = cdata [CS]
This attribute specifies the initial value of the control. If this attribute is not set, the initial value is set to the contents of the OPTION element.
You can also confirm this by looking at HTTP traffic monitor. You should see the option label being submitted.
You need to set the select item value to an empty string instead. JSF will then render a <option value="">. If you're using a converter, then you should actually be returning an empty string "" from the converter when the value is null. This is also clearly specified in Converter#getAsString() javadoc (emphasis mine):
getAsString
...
Returns: a zero-length String if value is null, otherwise the result of the conversion
So if you use <f:selectItem itemValue="#{null}"> in combination with such a converter, then a <option value=""> will be rendered and the browser will submit just an empty string instead of the option label.
As to dealing with the empty string submitted value (or null), you should actually let your converter delegate this responsibility to the required="true" attribute. So, when the incoming value is null or an empty string, then you should return null immediately. Basically your entity converter should be implemented like follows:
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return ""; // Required by spec.
}
if (!(value instanceof SomeEntity)) {
throw new ConverterException("Value is not a valid instance of SomeEntity.");
}
Long id = ((SomeEntity) value).getId();
return (id != null) ? id.toString() : "";
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null; // Let required="true" do its job on this.
}
if (!Utils.isNumber(value)) {
throw new ConverterException("Value is not a valid ID of SomeEntity.");
}
Long id = Long.valueOf(value);
return someService.find(id);
}
As to your particular problem with this,
but returning an empty string when the object in question is null, in turn incurs another problem as demonstrated here.
As answered over there, this is a bug in Mojarra and bypassed in <o:viewParam> since OmniFaces 1.8. So if you upgrade to at least OmniFaces 1.8.3 and use its <o:viewParam> instead of <f:viewParam>, then you shouldn't be affected anymore by this bug.
The OmniFaces SelectItemsConverter should also work as good in this circumstance. It returns an empty string for null.
If you want to avoid null values for your select component, the most elegant way is to use the noSelectionOption.
When noSelectionOption="true", the converter will not even try to process the value.
Plus, when you combine that with <p:selectOneMenu required="true"> you will get a validation error, when user tries to select that option.
One final touch, you can use the itemDisabled attribute to make it clear to the user that he can't use this option.
<p:selectOneMenu id="cmbCountry"
value="#{bean.country}"
required="true"
converter="#{countryConverter}">
<f:selectItem itemLabel="Select"
noSelectionOption="true"
itemDisabled="true"/>
<f:selectItems var="country"
value="#{bean.countries}"
itemLabel="#{country.countryName}"
itemValue="#{country}"/>
<p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>
<p:message for="cmbCountry"/>
Now if you do want to be able to set a null value, you can 'cheat' the converter to return a null value, by using
<f:selectItem itemLabel="Select" itemValue="" />
More reading here, here, or here
You're mixing a few things, and it's not fully clear to me what you want to achieve, but let's try
This obviously causes the java.lang.NumberFormatException to be thrown
in its converter.
It's nothing obvious in it. You don't check in converter if value is empty or null String, and you should. In that case the converter should return null.
Why does it render Select (itemLabel) as its value and not an empty
string (itemValue)?
The select must have something selected. If you don't provide empty value, the first element from list would be selected, which is not something that you would expect.
Just fix the converter to work with empty/null strings and let the JSF react to returned null as not allowed value. The conversion is called first, then comes the validation.
I hope that answers your questions.
In addition to incompleteness, this answer was deprecated, since I was using Spring at the time of this post :
I have modified the converter's getAsString() method to return an empty string instead of returning null, when no Country object is found like (in addition to some other changes),
#Controller
#Scope("request")
public final class CountryConverter implements Converter {
#Autowired
private final transient Service service = null;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
try {
long parsedValue = Long.parseLong(value);
if (parsedValue <= 0) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "The id cannot be zero or negative."));
}
Country country = service.findCountryById(parsedValue);
if (country == null) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_WARN, "", "The supplied id doesn't exist."));
}
return country;
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "", "Conversion error : Incorrect id."), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value instanceof Country ? ((Country) value).getCountryId().toString() : ""; //<--- Returns an empty string, when no Country is found.
}
}
And <f:selectItem>'s itemValue to accept a null value as follows.
<p:selectOneMenu id="cmbCountry"
value="#{stateManagedBean.selectedItem}"
required="true">
<f:selectItem itemLabel="Select" itemValue="#{null}"/>
<f:selectItems var="country"
converter="#{countryConverter}"
value="#{stateManagedBean.selectedItems}"
itemLabel="#{country.countryName}"
itemValue="${country}"/>
</p:selectOneMenu>
<p:message for="cmbCountry"/>
This generates the following HTML.
<select id="form:cmbCountry_input" name="form:cmbCountry_input">
<option value="" selected="selected">Select</option>
<option value="56">Country1</option>
<option value="55">Country2</option>
</select>
Earlier, the generated HTML looked like,
<select id="form:cmbCountry_input" name="form:cmbCountry_input">
<option selected="selected">Select</option>
<option value="56">Country1</option>
<option value="55">Country2</option>
</select>
Notice the first <option> with no value attribute.
This works as expected bypassing the converter when the first option is selected (even though require is set to false). When itemValue is changed to other than null, then it behaves unpredictably (I don't understand this).
No other items in the list can be selected, if it is set to a non-null value and the item received in the converter is always an empty string (even though another option is selected).
Additionally, when this empty string is parsed to Long in the converter, the ConverterException which is caused after the NumberFormatException is thrown doesn't report the error in the UIViewRoot (at least this should happen). The full exception stacktrace can be seen on the server console instead.
If someone could expose some light on this, I would accept the answer, if it is given.
This is fully working to me :
<p:selectOneMenu id="cmbCountry"
value="#{bean.country}"
required="true"
converter="#{countryConverter}">
<f:selectItem itemLabel="Select"/>
<f:selectItems var="country"
value="#{bean.countries}"
itemLabel="#{country.countryName}"
itemValue="#{country}"/>
<p:ajax update="anotherMenu" listener=/>
</p:selectOneMenu>
The Converter
#Controller
#Scope("request")
public final class CountryConverter implements Converter {
#Autowired
private final transient Service service = null;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.trim().equals("")) {
return null;
}
//....
// No change
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return value == null ? null : value instanceof Country ? ((Country) value).getCountryId().toString() : null;
//**** Returns an empty string, when no Country is found ---> wrong should return null, don't care about the rendering.
}
}
public void limparSelecao(AjaxBehaviorEvent evt) {
Object submittedValue = ((UIInput)evt.getSource()).getSubmittedValue();
if (submittedValue != null) {
getPojo().setTipoCaixa(null);
}
}
<p:selectOneMenu id="tipo"
value="#{cadastroCaixaMonitoramento.pojo.tipoCaixa}"
immediate="true"
required="true"
valueChangeListener="#{cadastroCaixaMonitoramento.selecionarTipoCaixa}">
<f:selectItem itemLabel="Selecione" itemValue="SELECIONE" noSelectionOption="false"/>
<f:selectItems value="#{cadastroCaixaMonitoramento.tiposCaixa}"
var="tipo" itemValue="#{tipo}"
itemLabel="#{tipo.descricao}" />
<p:ajax process="tipo"
update="iten_monitorado"
event="change" listener="#{cadastroCaixaMonitoramento.limparSelecao}" />
</p:selectOneMenu>
The following code:
<h:selectOneMenu id="discountCode" value="#{customerMBean.details.discountcode}"
title="DiscountCode" required="true" requiredMessage="The DiscountCode field is required."
converter="#{customerMBean.discountCodeConverter}">
<f:selectItems value="#{customerMBean.allDiscountCodes}"/>
</h:selectOneMenu>
<h:commandButton id="back" value="Back" action="#{customerMBean.list}"/>
<h:commandButton id="update" value="Update" action="#{customerMBean.update}"/>
Shows the following error, regardless of the clicked button:
j_idt6:discountCode : validation error .
public Converter getDiscountCodeConverter() {
return discountCodeConverter;
}
private Converter discountCodeConverter = new Converter() {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return new ConverterException("On verra la conversion String->Objet plus tard...");
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
DiscountCode dc = (DiscountCode) value;
return dc.getDiscountcode()+" : "+dc.getRate()+"%";
}
};
How is this caused and how can I solve it?
When using a custom converter in h:selectOneMenu, you should always override the equals() method of the object involved.
See also
JSF Validation Error While Using Custom Converter
Use <h:message/> tag it will show you validation error and update it on clicking of button or you can use auto update.
According to me :-
why your button is not working ?? because every time when you click on submit button it will show you validation error but you are not using that's why it is not visible on your screen.
And on back button use immediate=true it will skip the validation phase.
I am using seam 2.2.2.Final on JBoss AS 5. I am working on a multi-page wizard.On my first page, user will be able to enter several business names separated by a new line on a textarea.
<s:decorate id="businessNameTextAreaField" template="layout/edit.xhtml">
<ui:define name="label">Business Names</ui:define>
<h:inputTextarea id="businessNameTextArea"
cols="80"
rows="3"
required="true"
value="#{businessNameHome.instance.businessNameTextArea}"/>
</s:decorate>
Upon submission of the page, the system parses the inputed value and splits it into a list of strings
public String checkBusinessNames(){
String businessNameTextArea = this.getInstance().getbusinessNameTextArea();
String[] businessNameTextAreaArray = businessNameTextArea.split("\\n");
List<SelectItem> businessNameChoices = new ArrayList<SelectItem>();
for(String businessNameText: businessNameTextAreaArray){
businessNameChoices.add(new SelectItem(businessNameText));
}
this.getInstance().setBusinessNameChoices(businessNameChoices);
return "valid";
}
The user is then asked to select from the list of valid business names to register
<s:decorate id="businessNameRegisterListField" template="layout/edit.xhtml">
<ui:define name="label">Business Name</ui:define>
<h:selectManyCheckbox value="#{businessNameHome.instance.selectedbusinessName}" layout="pageDirection" immediate="true" >
<s:selectItems value="#{businessNameHome.instance.businessNameChoices}" var="bn" label="#{bn.label}" /> </h:selectManyCheckbox>
</s:decorate>
selectedbusinessName is of type String while businessNameChoices is of List
Upon submission of the page, what is submitted as business names is something like this:
javax.faces.model.SelectItem#135aa7c
I have tried putting an itemValue on the s:selectItems but I get another error which is "Value is not valid"
Tried to use <s:convertEntity> but gets a NumberFormatException
I have also tried to create my own converter
public class BusinessNameBeanConverter implements javax.faces.convert.Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
// TODO Auto-generated method stub
System.out.println("getAsObject "+value);
return value;
}
#Override
public String getAsString(FacesContext context, UIComponent cmp, Object value) {
// TODO Auto-generated method stub
System.out.println("getAsString "+((SelectItem)value).getValue());
return ((SelectItem)value).getValue();
}
}
but I still get the same "Value is not valid" error.
I don't know what to do anymore. Please help.
Thanks,
Nicholas
Change
<s:selectItems value="#{businessNameHome.instance.businessNameChoices}" var="bn" label="#{bn.label}" />
to
<f:selectItems value="#{businessNameHome.instance.businessNameChoices}" />
You've namely already a List<SelectItem>, not a List<SomeObject> for which <s:selectItems> is useful.
Don't forget to remove the converter, it makes no sense.