CDI injection of subclasses of session scoped beans as a managed property

136 Views Asked by At

I am using JSF 2.3 and I want to inject different sublaccess of a session scoped bean as a managed property using CDI.

From the link below

How to inject different subclasses as ManagedProperty JSF 2?

I understood that this was not possible for RequestScoped beans using the "old" JSF and JEE-annotations, but my beans have session scope and I am using CDI injection, and therefore different annotations than the ones used in the above link.

In particular I do have:

public abstract class BaseContainer {
     String prop1;
}

@Named
@SessionScoped
public class MaklerContainer extends BaseContainer {
     String prop 2;
}

@Named
@SessionScoped
public class  AppManagerContainer extends MaklerContainer {
     String prop 3;
}

public abstract class BaseBean {

   @Inject
   @javax.faces.annotation.ManagedProperty(value = "#{maklerSessionContainer}")
   private MaklerSessionContainer maklerSessionContainer;

}

Is it possible to inject interchangeably instances of both MaklerContainer and AppManagerContainer as a managed property maklerSessionContainer of the class BaseBean above?

1

There are 1 best solutions below

2
Nikos Paraskevopoulos On

Let me describe one option, there may be others.

First of all, if you want to inject different sublaccess, you have to find a way to disambiguate them for CDI, or it will complain about "Ambiguous dependencies". E.g. given the class hierarchy of the question, the line below results in ambiguous dependency exception, because CDI cannot decide whether to inject the MaklerContainer or the AppManagerContainer that extends it:

@Inject MaklerContainer maklerContainer; // ambiguous!

You can use qualifiers, named beans, or @Typed (perhaps there are even more ways).

Let's use @Named, since it is already present.

The idea is to create a producer that introduces a third bean of type MaklerContainer, with a different name, to the appropriate scope. The producer code will decide which of the 2 implementations to choose at runtime. Something like this:

@ApplicationScoped
public class TheProducer {
  @Inject @Named("maklerContainer")
  private MaklerContainer maklerContainer;
  @Inject @Named("appManagerContainer")
  private AppManagerContainer appManagerContainer;
  @Inject
  private User currentUser;

  @Produces
  @SessionScoped
  @Named("theOne") // choose appropriate name of course
  public MaklerContainer makeMaklerContainer() {
    if (currentUser.hasRole("Role1")) {
      return appManagerContainer;
    } else {
      return maklerContainer;
    }
  }
}

Now all you have to do is inject the appropriate named MaklerContainer, like:

@Inject @Named("theOne") MaklerContainer maklerContainer;