users@glassfish.java.net

Inconsistent behaviour between JSF2 and CDI injection in _at_FacesConverter . Possible Glassfish bug?

From: Craig Ringer <craig_at_postnewspapers.com.au>
Date: Fri, 20 Aug 2010 17:16:49 +0800

[I've CC'd Sivakumar because of the similarity between this and the
earlier issue I reported, as both involve inconsistencies between Weld
and JSF2 injection in Glassfish. Hope you don't mind.]


Hi

I'm using the Mojarra JSF2 RI embedded in Glassfish 3.0.1 with CDI(weld)
in a new project, and I seem to have hit another odd inconsistency
between CDI and JSF2 injection. The previous one was a glassfish bug,
but I'm not sure if this one is a bug or me doing something silly.

I need to access a DAO facade (a @Stateless EJB) from a JSF2
@FacesConverter. As apparently JSF2 doesn't support DI in converters
(sigh) I have to look the EJB up via JNDI or obtain it indirectly via an
EL lookup on an object that contains a reference to it.

If I do an EL lookup via FacesContext in the converter, I can obtain the
@SessionScoped model bean that has the @Stateless DAO EJB injected into
it. This works fine whether using JSF2 or CDI.

If the model bean is managed using CDI (ie @Named
@javax.enterprise.context.SessionScoped and inject with @Inject), the
field into which the DAO EJB is injected is null when the model bean is
looked up via EL from the FacesConverter, even though it was non-null at
@PostConstruct time in the same object.

If I use JSF2 (ie use @ManagedBean @javax.faces.bean.SessionScoped and
inject with @EJB), the injected dao ejb field is always non-null and can
be accessed and used as expected.

I've produced a self-contained test case in source-code and deployable
war form to demonstrate this inconsistency. It can be found here:

http://www.postnewspapers.com.au/~craig/public_files_keep/ErrorDemo2.war
http://www.postnewspapers.com.au/~craig/public_files_keep/ErrorDemo2.zip

Is this another CDI integration bug? Or is this not meant to work?


A boiled-down outline of the code is that this works:


@ManagedBean
@javax.faces.bean.SessionScoped
public class ModelBean implements Serializable {
    private static final long serialVersionUID = 99L;

    @EJB DemoEJB demoEJB;

    @PostConstruct
    protected void postConstruct() {
        if (demoEJB == null) throw new IllegalStateException("NoEJB");
    }

    // blah blah

    @FacesConverter(forClass=EntityObject.class)
    public static class EntityObjectConverter implements Converter {

        @Override
        public Object getAsObject(FacesContext context, UIComponent
                      component, String value) {
            ModelBean modelBean =
                (ModelBean)context.getApplication()
                .getELResolver().getValue(
                     context.getELContext(), null, "modelBean"
                );
            System.err.println("(EL) modelBean is " + modelBean +
                 ", modelBean.demoEJB is " + modelBean.demoEJB);
            return modelBean.demoEJB.getEntityForKey(value);
        }
    }
}



but this throws a NullPointerException when accessing members of
ModelBean.demoEJB:



@Named
@javax.enterprise.context.SessionScoped
public class ModelBean implements Serializable {
    private static final long serialVersionUID = 98L;

    @Inject DemoEJB demoEJB;

    @PostConstruct
    protected void postConstruct() {
        if (demoEJB == null) throw new IllegalStateException("NoEJB");
    }

    // blah blah

    @FacesConverter(forClass=EntityObject.class)
    public static class EntityObjectConverter implements Converter {

        @Override
        public Object getAsObject(FacesContext context, UIComponent
                              component, String value) {
            ModelBean modelBean =
                (ModelBean)context.getApplication().getELResolver()
                    .getValue(
                        context.getELContext(), null, "modelBean"
                    );
            // Note that "modelBean.demoEJB" is null here. WTF?
            // It was a suitable EJB proxy object in @PostConstruct...
            System.err.println("(EL) ModelBean is " + modelBean + ",
                 modelBean.demoEJB is " + modelBean.demoEJB);
            return modelBean.demoEJB.getEntityForKey(value);
        }

    }

}



-- 
Craig Ringer
Tech-related writing: http://soapyfrogs.blogspot.com/