2

I tried to setup a FacesConverter to display some entity through my JSF page as referenced in JSF specification.

I'm running the following: - Open Liberty 19.0.0.11 (tested on 19.0.0.6 as well, don't ask me why this version, I picked randomly another one) - Java Open JDK 13 - JSF 2.3 - CDI 2.0

I've added to my project the following web.xml:

<web-app id="WebApp_ID" version="4.0" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">
    <display-name>TestConverterInjection</display-name>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <enabled>true</enabled>
        <async-supported>false</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>
        *.xhtml</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
</web-app>

The following faces-config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"
    version="2.3">

</faces-config>

The following beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
    bean-discovery-mode="all" 
    version="2.0">
</beans>

I do have an AppConfig class, annotated with @FacesConfig(Version.JSF_2_3). All fine so far.

I've simple TestConverter annotated as such:

@FacesConverter(value = "testConverter", managed = true)

When looking through the BeanManager, my TestConverter seems to be available as it appears in the list.

My test.xhtml file looks like this:

    <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
       xmlns:f="http://java.sun.com/jsf/core" 
       xmlns:h="http://java.sun.com/jsf/html"
       xmlns:ui="http://java.sun.com/jsf/facelets"> 


    <h:outputText value="#{testBean.selectedEntity.id}">    
    </h:outputText>
    <br/>
    <h:outputText value="#{testBean.selectedEntity}" converter="testConverter">    
    </h:outputText>
</html>

And my backing bean:

package com.test.beans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.util.AnnotationLiteral;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;

import com.test.entities.MyEntity;

@Named("testBean")
@ViewScoped
public class TestBean implements Serializable {

    private List<MyEntity> listEntities;

    private MyEntity selectedEntity;

    @Inject private BeanManager beanManager;

    @PostConstruct
    public void init() {

        this.listEntities = new ArrayList<MyEntity>();

        for(int i = 0; i < 5; i++) {
            this.listEntities.add(new MyEntity(i, "test_"+i, i+"_test"));
        }

        this.selectedEntity = this.listEntities.get(0);

        Set<Bean<?>> beans = beanManager.getBeans(Object.class,new AnnotationLiteral<Any>() {});
        for (Bean<?> bean : beans) {       
            System.out.println("bean: "+bean.getBeanClass().getName());
        }       
    }

    public List<MyEntity> getListEntities() {
        return listEntities;
    }

    public void setListEntities(List<MyEntity> listEntities) {
        this.listEntities = listEntities;
    }

    public MyEntity getSelectedEntity() {
        return selectedEntity;
    }

    public void setSelectedEntity(MyEntity selectedEntity) {
        this.selectedEntity = selectedEntity;
    }       

}

Should be all good right ? Well, at least it worked with Apache TomEE 8.0.0 PluME. But here on Open Liberty, I'm getting:

SRVE0777E: Exception émise par la classe d'application 'javax.faces.webapp.FacesServlet.service:236' javax.servlet.ServletException: at javax.faces.webapp.FacesServlet.service(FacesServlet.java:236) at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1255) at [internal classes] Caused by: java.lang.NullPointerException: at org.apache.myfaces.cdi.converter.FacesConverterCDIWrapper.getAsString(FacesConverterCDIWrapper.java:62) ... 1 more

So, it looks like my bean is instantiated but is null... Features of Open Liberty are javaee8-0 (to be sure there wasn't anything missing). In the example above, if I'm removing the "managed = true" in my converter, it works. Some bug within CDI ?

Kukeltje
  • 12,223
  • 4
  • 24
  • 47
Cedric
  • 23
  • 2
  • _"So, it looks like my bean is instantiated but is null..."_ that is a contradictio in terminis. If it is instantiated it cannot be null. ;-) I've seen this `managed="true"` in a previous question. I'll try to find it, but you may do too. – Kukeltje Nov 12 '19 at 19:41
  • Did not find a duplicate (yet) but please read http://showcase.omnifaces.org/cdi/FacesConverter – Kukeltje Nov 12 '19 at 19:45
  • Did you check the log to see if jsf actually runs in 2.3 mode? And you can debug the code jsf code to see how it actually resolves. Also change the facelets namespaces to jsf 2.2+ ones (off topic most likely but never the less) – Kukeltje Nov 12 '19 at 19:55
  • Using MyFaces 2.3.5 and OpenwebBeans 2.0.12 I get the same NPE when `managed` is `true`. Using Mojarra 2.3.9 I get my converter instance (instead of a wrapper) and no NPE, but `@Injected` fields remain `null` in the converter. – Selaron Nov 13 '19 at 07:57
  • That is indeed contradictory. According to the recent jsf2.3 jsr, injection within converters and validators should now be working: https://arjan-tijms.omnifaces.org/p/jsf-23.html?m=1. However, that's not the point here, the point is that the converter itself is null when injected within the page. I managed to get this working on a test project with a dumb converter by removing the managed=true. However, in this specific case, it didn't. – Cedric Nov 13 '19 at 08:31
  • Another solution that I found was to remove the faces converter annotation and used named and requestscoped on the converter and then use converter="#{converterName}" instead of converter="converterName" but I do think this is really a terrible workaround for something defined in the specs. – Cedric Nov 13 '19 at 08:36
  • Kukeltje, I've looked for the jsf implementation of open liberty and this was well myfaces 2.3.0.1 if I'm not mistaken. – Cedric Nov 13 '19 at 09:54

1 Answers1

1

I investigated the issue and I found that it's caused by beanManager.getBeans call. The bean is registered in the beanManger (as you demonstrated), but it's just not retrieved properly. Since the Converter uses generics, you need to search by type instead of class. I opened an issue in the MyFaces community and provided a patch! :)

https://issues.apache.org/jira/browse/MYFACES-4311

The fix will be included in MyFaces 2.3.7.

Thanks for spotting this one!

volosied
  • 151
  • 1
  • 6