"Unable to convert ejbRef for ejb" on CDI (Weld) injection of @Stateless EJB into @SessionScoped JSF2 bean in Glassfish
Asked Answered
S

1

6

[UPDATE: After discussion on the Glassfish forums/ML at http://forums.java.net/jive/thread.jspa?messageID=480532 a bug was filed against Glassfish https://glassfish.dev.java.net/issues/show_bug.cgi?id=13040 for this issue.]

I'm trying to inject a local no-interface view of a @Stateless EJB into a JSF2 @Named @javax.enterprise.context.SessionScoped backing bean. The EJB is one of several that extend an abstract generic base class. Injection of "@Inject TheEJBClass varName" fails with "Unable to convert ejbRef for ejb TheEJBClass to a business object of type class my.package.name.TheAbstractBase". [edit: Actually, it turns out that injection succeeds, but method resolution in the injected proxy for methods inherited from superclasses fails.] If I use "@EJB TheEJBClass varName" then varName remains null, ie nothing is injected.

Details:

I'm running Glassfish 3.0.1 on Linux (Ubuntu 10.04 in case it matters) and having real problems handling injection of my data model EJBs into my JSF2 session scoped models using CDI (Weld). And yes, before you ask, I have beans.xml in place and CDI is activating to perform injection.

If I inject it with an @EJB annotation, eg:

@EJB TheEJBClass memberName;

... the EJB isn't actually injected, leaving memberName null.

If I inject it with a CDI @Inject annotation:

@Inject TheEJBClass memberName;

... then CDI complains when I call a method of "memberName" that's implemented in a superclass of TheEJBClass and not overridden in TheEJBClass its self, reporting:

java.lang.IllegalStateException: Unable to convert ejbRef for ejb TheEJBClass to a business object of type class my.package.name.TheAbstractBase
    at
com.sun.ejb.containers.EjbContainerServicesImpl.getBusinessObject(EjbContainerServicesImpl.java:104)
at
org.glassfish.weld.ejb.SessionObjectReferenceImpl.getBusinessObject(SessionObjectReferenceImpl.java:60)
....

I've tried converting the base to concrete class and de-generifying it, but encounter the same problem, so I don't think I'm hitting the Weld bugs with generic bases (https://jira.jboss.org/browse/WELD-305, https://jira.jboss.org/browse/WELD-381, https://jira.jboss.org/browse/WELD-518).

An outline of the code, with full package qualification on annotations added for clarity, is:

// JSF2 managed backing bean.
//
// Called via #{someJSF2Model.value} in a JSF2 page
//
@javax.inject.Named
@javax.enterprise.context.SessionScoped
public class SomeJSF2Model implements Serializable {
   @javax.inject.Inject TheEJBClass member;

   public Integer getValue() {
       return member.getValue();
   }
   // blah blah
}

// One of several EJB classes that extend TheAbstractBase
@javax.ejb.Stateless
public class TheEJBClass extends TheAbstractBase {
  // blah blah
  // does **NOT** override "getValue()"
}

public abstract class TheAbstractBase {
    // blah blah
    public Integer getValue() {
        return 1;
    }
}

Note that injection does work if I override TheAbstractBase.getValue() in TheEJBClass, or if I call a method defined in TheEJBClass and not any superclass. It seems like the issue is something to do with inheritance.

Very similar code that used JSF2's built-in lifecycle and injection features worked, but given that this is a new project and CDI is where things are heading in the future, I thought it best to try to go for CDI. Here's what I started out with using JSF2/EJB injection, which worked:

// JSF2 managed backing bean. Using @ManagedBean and JSF2's @SessionScoped
// instead of CDI @Named and CDI @SessionScoped this time.
//
@javax.faces.bean.ManagedBean
@javax.faces.bean.SessionScoped
public class SomeJSF2Model implements Serializable {
   @javax.ejb.EJB TheEJBClass member;
   public Integer getValue() {
       return member.getValue();
   }
   // blah blah
}

// One of several EJB classes that extend TheAbstractBase
// Unchanged from CDI version
@javax.ejb.Stateless
public class TheEJBClass extends TheAbstractBase {
  // blah blah
  // does **NOT** override "getValue()"
}

// Unchanged from CDI version
public abstract class TheAbstractBase {
    // blah blah
    public Integer getValue() {
        return 1;
    }
}

I'm currently working on putting together a self-contained test case, but thought I'd fire off the question now in case this is something where I'm just doing something silly or there's a well known solution my Google-fu isn't up to finding. Why did it work with JSF2/EJB injection, but fail with CDI injection?

( Since re-posted on the Glassfish forums as http://forums.java.net/jive/thread.jspa?threadID=152567 )

Stefaniastefanie answered 18/8, 2010 at 5:41 Comment(2)
I've built a test case that demonstrates this issue. It seems to come down to how CDI's generated JavaAssist wrapper resolves references to methods defined in superclasses. The problem isn't actually at injection time, but at the time a method inherited from a superclass is called via the injected wrapper. The use of JSF2 or CDI naming and scope has nothing to do with it, it's just @EJB vs @Inject See comments and example in: postnewspapers.com.au/~craig/public_files_keep/ErrorDemo.zip (src) and postnewspapers.com.au/~craig/public_files_keep/ErrorDemo.war (webapp) .Stefaniastefanie
... and for bonus pain, the issue is pretty much reversed in JBoss AS 6. The CDI injection works fine, but the JSF2 injection fails.Stefaniastefanie
S
3

As noted above, it's a Weld/glassfish bug.

Fix: Give up on Glassfish and move to JBoss AS 7, which actually works most of the time.

Stefaniastefanie answered 29/11, 2011 at 3:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.