JSF 2.3 Custom Converter with Generics

Now we have started using JSF 2.3 for our existing JSF 2.2 project. In our custom converters we got a warning. Converter is a raw type. References to generic type Converter<T> should be parameterized. The problem we are facing is that we tried to fix this warning with 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 the 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 ClassCastExceptionwith a message java.lang.String cannot be cast to MyCustomObject. There is also one line in stacktrace that might help com.sun.faces.cdi.CdiConverter.getAsString(CdiConverter.java:109).

But when the definition of universal converters changed from MyCustomObjectto 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 it clearly exceeds the purpose of the interface Converter<T>.

+4
2

, , , :

some_page.xhtml ( ):

<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>

, JSF . rawtype (JSF 2.3+, 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())) {
            // Warning message
            // @TODO Not working with JNDI (no remote interface) this.loggerBeanLocal.logWarning(MessageFormat.format("{0}.getAsObject(): submittedValue is null or empty - EXIT!", this.getClass().getSimpleName())); //NOI18N

            // Return null
            return null;
        }

        // Init instance
        User user = null;

        try {
            // Try to parse the value as long
            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) {
            // Debug message
            // @TODO Not working with JNDI (no remote interface) this.loggerBeanLocal.logDebug(MessageFormat.format("getAsObject: Exception: {0} - Returning null ...", ex)); //NOI18N
        }

        // 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());
    }

}

JNDI ( ), :

  • Converter<User> ( User POJI), raw
  • value="UserConverter" , JSF
  • , ClassCastException:
  • <f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
  • #{null}, null getAsString!

, .

forClass value: FacesConverter is using both value and forClass, only value will be applied.

+2

FacesConverter.

, forClass = MyCustomObject.class, , . forClass @FacesConverter . ...

-, , getAsString? String casting, : return myCustomObject.getId().toString(), id Long. getAsObject MyCustomObject . , , , , , .

+1

Source: https://habr.com/ru/post/1674134/


All Articles