September 19th, 2012 by Lincoln Baxter III

How to @Inject Enum values into CDI beans

I was recently asked how to Inject a Java Enum into a bean constructor using CDI. This turns out to be a fairly interesting situation, because enums are not usually available for Injection.

The first attempt

As much as we would hope for this to work, it does not. Enums are not beans because they have no default constructor (hence CDI does not know how to construct them,) and there is no standard way to resolve which enumerated value should be injected by default (Unless there is only one value, but this is still not supported due to the lack of default constructor.)

Injecting an enum with a single value does not work.
public class InjectedObject {
   private MyEnum e;

   @Inject
   public InjectedObject(MyEnum e) {
      this.e = e;
   }
   
   public MyEnum getValue() {
      return e;
   }

   /**
    * Our enum
    */
   public enum MyEnum {
      ONE
   }
}

This will result in a deployment failure

Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [MyEnum] with qualifiers [@Default] at injection point [[BackedAnnotatedParameter] Parameter 1 of [BackedAnnotatedConstructor] @Inject public org.example.InjectedObject(MyEnum)]
	at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:365)
	at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:297)
	at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:157)
	at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:184)
	at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:470)
	at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:74)
	at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:72)
	at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:60)
	at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:53)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	... 1 more

The solution

We must turn to producer methods in order to make this possible. It takes just a little bit of code, but it is possible, and very easy! The reason this works is because any value may be injected if it resolves to a unique set of qualifiers and types. You could even inject a java.lang.String using this technique.

Using @Produces to to inject an enum in CDI.
/**
 * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
 */
public class InjectedObject
{
   private MyEnum e;

   @Inject
   public InjectedObject(MyEnum e)
   {
      this.e = e;
   }

   public MyEnum getValue()
   {
      return e;
   }

   /**
    * A producer is required in order to {@link Inject} an Enum
    */
   @Produces
   public static MyEnum getEnum()
   {
      return MyEnum.THREE;
   }

   /**
    * Our enum
    */
   public enum MyEnum
   {
      ONE, TWO, THREE
   }

}

And there you have it! A perfectly functional method of injecting enum values in CDI. You will, however, need to make sure you know which type of enum you want to inject, and if you need multiple types, then you’ll need to create qualifiers for each value.

You could also use the built-in @Named qualifier

The @Named qualifier is a built in qualifying type that allows unique qualifiers to be created using String values.

Using the @Named qualifier to inject arbitrary values of the same type
/**
 * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
 */
public class InjectedObject
{
   private MyEnum e1;
   private MyEnum e2;

   @Inject
   public InjectedObject(@Named("ONE") MyEnum e1, @Named("TWO") MyEnum e2)
   {
      this.e1 = e1;
      this.e2 = e2;
   }

   public MyEnum getValue1()
   {
      return e1;
   }

   public MyEnum getValue2()
   {
      return e2;
   }

   /**
    * A producer is required in order to {@link Inject} an Enum
    */
   @Produces
   @Named("ONE")
   public static MyEnum getEnumOne()
   {
      return MyEnum.ONE;
   }

   @Produces
   @Named("TWO")
   public static MyEnum getEnumTwo()
   {
      return MyEnum.TWO;
   }

   /**
    * Our enum
    */
   public enum MyEnum
   {
      ONE, TWO, THREE
   }

}

We would not, however, be able to @Inject @Named("THREE") MyEnum e because we have not declared a producer with such a qualifier!

Posted in Java, JBoss

7 Comments

  1. w0mbat says:

    Cool article about CDI producer methods!
    I modified it a bit to support a "dynamic producer" based on a Config qualifier. This makes it possible to add a value to the enum and directly inject it without adding a new producer:

    public class InjectedObject
    {
       private MyEnum e1;
       private MyEnum e2;
       private MyEnum e3;
    
       @Inject
       public InjectedObject(@Config("ONE") MyEnum e1, @Config("TWO") MyEnum e2, @Config("THREE") MyEnum e3)
       {
          this.e1 = e1;
          this.e2 = e2;
          this.e3 = e3;
       }
    
       public MyEnum getValue1()
       {
          return e1;
       }
    
       public MyEnum getValue2()
       {
          return e2;
       }
       
       public MyEnum getValue3()
       {
          return e3;
       }
    
       /**
        * A producer is required in order to {@link Inject} an Enum
        */
       @Produces
       @Config
       public static MyEnum getEnum(InjectionPoint ip)
       {
    	   String name = null;
    	   
    	   /**
    	    * Iterate over all Qualifiers of the Injection Point to find our configuration and save the config value
    	    */
    	   for(Annotation a : ip.getQualifiers()) {
    		   if(a instanceof Config) {
    			   name = ((Config) a).value();
    		   }
    	   }
    
    	   /**
    	    * Iterate over all enum values to match them against our configuration value
    	    */
    	   for(MyEnum me : MyEnum.values()) {
    		   if(me.toString().equals(name)) {
    			   return me;
    		   }
    	   }
    	   
          return null;
       }
    
    /**
        * Our enum
        */
       public enum MyEnum
       {
          ONE, TWO, FOO
       }
       
       @Qualifier
       @Target({ TYPE, METHOD, PARAMETER, FIELD })
       @Retention(RUNTIME)
       @Documented
       public @interface Config {
    	   @Nonbinding String value() default "";
       }
    }
    1. w0mbat says:

      As some might have guessed, the e3 variable is injected as null because no such enum value exists(but ‘@Config("FOO") MyEnum e3’ would have been possible)

  2. Hi Lincon and wOmbat, please help me.

    I try use PasswordConfirmValidator from soicalpm in my project.

    @Inject
    private InputElement<String> password;

    Compilation was sucessfully, but when I try run at Jboss AS7, I get:

    org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [InputElement<String>] with qualifiers [@Default] at injection point [[field] @Inject private org.eqaula.glue.security.validator.SignupValidator.password

    I read this article, I have a question: Where I can create a Producer for InputElement<String>?

  3. w0mbat says:

    Hey José Lus Granda,
    you would have to create a producer in any class you want. I should look somehow similar to the code below:

    @Produces
    public InputElement<String> getInputElement() {
    // your business code here.
    // return your entity
    }

    Hope this helped!

    Cheers,

    w0mbat

  4. Sebastian says:

    Using the @Named Qualifier, is there a way to define a default implementation that is used when no explicit name is given at the injection point?

    1. @Named is actually not a qualifier, it just specifies the EL bean name.

      1. Sebastian says:

        I see, never used that before. Thanks for the hint.

Reply to Lincoln Baxter III




Please note: In order to submit code or special characters, wrap it in

[code lang="xml"][/code]
(for your language) - or your tags will be eaten.

Please note: Comment moderation is enabled and may delay your comment from appearing. There is no need to resubmit your comment.