vamsi
vamsi

Reputation: 323

Equivalent for @Conditional in CDI

I have two classes with post construct initialization, and i need one of them to be injected based on a vm argument. I have done this kind of conditional injection in spring using @Conditional annotation, however i could not find any equivalent in CDI. Can some one please help me with this.

The code goes something like this,

public void impl1{
    @PostConstruct
    public void init(){
       ....
    }
    ....
}


public void impl2{
    @PostConstruct
    public void init(){
       ...
    }
    ....
}

If vmargument type=1, impl1 has to be injected and if type=2, impl2 has to be injected

Upvotes: 3

Views: 3017

Answers (3)

Siliarus
Siliarus

Reputation: 6753

Probably the best way is to use an extension. You will create two classes both of which will have the same type so they are eligible for injection into the same injection point. Then, using the extension, you will disable one of them, leaving only one valid (the other will not become a bean).

Extensions can 'hook into' container lifecycle and affect it. You will want to leverage ProcessAnnotatedType<T> lifecycle phase (one of the first phases) to tell CDI that certain class should be @Vetoed. That means CDI will ignore it and not turn in into a bean.

Note the type parameter T in ProcessAnnotatedType<T> - replace it with a type of your implementation. Then the observer will only be notified once, when that class is picked up by CDI. Alternatively, you can replace T with some type both impls have in common (typically an interface) and the observer will be notified for both (you then need to add a login to determine which class was it notified for).

Here is a snippet using two observers. Each of them will be notified only once - when CDI picks up that given impl - and if it differes from the vm arg, it is vetoed:

public class MyExtension implements Extension {

  public void observePAT(@Observes ProcessAnnotatedType<Impl1.class> pat){
    // resolve your configuration option, you can alternatively place this login into no-args constructor and re-use
    String vmArgumentType = loadVmArg();
    // if the arg does not equal this impl, we do not want it
    if (! vmArgumentType.equals(Impl1.class.getSimpleName())) {
      pat.veto();
    }
  }

  public void observePAT(@Observes ProcessAnnotatedType<Impl2.class> pat){
    // resolve your configuration option, you can alternatively place this login into no-args constructor and re-use
    String vmArgumentType = loadVmArg();
    // if the arg does not equal this impl, we do not want it
    if (! vmArgumentType.equals(Impl2.class.getSimpleName())) {
      pat.veto();
    }
  }
}

Upvotes: 3

mtj
mtj

Reputation: 3554

For runtime decision (without changing your beans.xml), you basically have two options:

Option 1: use a producer method

@Produces
public MyInterface getImplementation() {
   if(runtimeTestPointsTo1)  return new Impl1();
   else                      return new Impl2();
}

Drawback: you leave the world of bean creation by using new, therefore your Impl1 and Impl2 cannot @Inject dependencies. (Or you inject both variants in the producer bean and return one of them - but this means both types will be initialized.)

Option 2: use a CDI-extension

Basically listen to processAnotated() and veto everything you don't want. Excellent blog-entry here: http://nightspawn.com/rants/cdi-alternatives-at-runtime/

Upvotes: 4

Jay Smith
Jay Smith

Reputation: 2480

Create your own @Qualifier and use it to inject cdi bean:

public class YourBean {

    @Inject 
    @MyOwnQualifier
    private BeanInterface myEJB;
}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface MyOwnQualifier {
    YourCondition condition();
}

Upvotes: 1

Related Questions