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 MyCustomObject
is 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 warningvalue="UserConverter"
that is the actual name you use in your JSF pageClassCastException
:<f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
#{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 and
forClass, 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.