9 Replies Latest reply on Nov 24, 2009 4:27 PM by william.drai

    Extensions and produces

    william.drai

      Hi all,


      I am trying to build an simple extension to collect classes annotated with a particular annotation during startup and provide this list to another bean later by injection.


      public class ModelExtension implements Extension {
           
           private ModuleEntityClasses moduleEntityClasses = new ModuleEntityClasses();
           
      
           public void beforeBeanDiscovery(@Observes ProcessAnnotatedType<?> event) {
                AnnotatedType<?> annotatedType = event.getAnnotatedType();
                for (Annotation annotation : annotatedType.getAnnotations()) {
                     if (annotation.annotationType().isAnnotationPresent(Module.class)) {
                          // This is a model class
                          moduleEntityClasses.registerEntityClass(annotatedType.getJavaClass(), annotation.annotationType());
                     }
                }
           }
           
           
           @Produces
           public ModuleEntityClasses produceModuleEntityClasses() {
                return moduleEntityClasses;
           }
      }
      



      public class ModuleEntityClasses {
           
           private Map<Class<? extends Annotation>, Set<Class<?>>> moduleEntityClasses = new HashMap<Class<? extends Annotation>, Set<Class<?>>>();
           
           
           public void registerEntityClass(Class<?> entityClass, Class<? extends Annotation> annotationType) {
                Set<Class<?>> entityClasses = moduleEntityClasses.get(annotationType);
                if (entityClasses == null) {
                     entityClasses = new HashSet<Class<?>>();
                     moduleEntityClasses.put(annotationType, entityClasses);
                }
                entityClasses.add(entityClass);
           }
           
           public Set<Class<?>> getEntityClasses(Class<? extends Annotation> moduleMetadataClass) {
                return moduleEntityClasses.get(moduleMetadataClass);
           }
      }
      



      I would like to inject this ModuleEntityClasses in another bean with :


      @Inject
      private ModuleEntityClasses moduleEntityClasses;
      



      but I get this error :


      Injection point has ambiguous dependencies.
      Injection point: field com.adequate.core.metadata.EntityMetadataStore.moduleEntityClasses; 
      Qualifiers: [@javax.enterprise.inject.Default()];
      Possible dependencies: [
          org.jboss.weld.bean-flat-ManagedBean-com.adequate.core.jcdi.ModuleEntityClasses, 
          org.jboss.weld.bean-flat-ProducerMethod-com.adequate.core.jcdi.ModelExtension.produceModuleEntityClasses()
      ], **ERROR**
      



      I don't understand why ModuleEntityClasses is seen as a CDI bean by itself as it has absolutely no CDI annotation, I just would like to define it as a produced object.
      Note: all classes are in the same jar inside an ear in JBoss 5.2.


        • 1. Re: Extensions and produces
          nickarls

          A @Produces field/method is an implicit bean definition. You have a (dependent scoped) explicit bean and the produced one conflicting with the same type (ModuleEntityClasses, Object) and qualifiers (@Default, @Any)

          • 2. Re: Extensions and produces
            william.drai

            Yes but why is ModuleEntityClasses detected as an explicit dependent scoped bean.
            It has no CDI annotation, no scope, it's just a plain class in the classpath. Does that mean that all classes in a CDI module are registered as explicit beans ?


            And if it is because it is the return type of the producer method, that means that it is impossible to produce an instance of a class that is present is the classpath because that would always generate this ambiguous dependency error.


            I've worked around this by using a qualifier for the producer method but I don't think that should be needed.

            • 3. Re: Extensions and produces
              nickarls

              Is the ModuleEntityClasses inside a normal archive that has a beans.xml? In this case it is picked up.

              • 4. Re: Extensions and produces
                nickarls

                Umm, good question regarding @Produces without qualifiers, have to check the spec if this is a case for a specializing producer or something...

                • 5. Re: Extensions and produces
                  nickarls

                  Tip from Pete, try @Produces @Alternative (remember to activate it in beans.xml, too)

                  • 6. Re: Extensions and produces
                    william.drai

                    Thanks for the tip.


                    I've found another workaround by defining a dummy non empty constructor for the produced class, so it is not picked up as explicit bean.


                    I should have read this section of the spec: '3.1.1. Which Java classes are managed beans?' :-).


                    Almost all classes in a jar containing beans.xml are explicit beans except very few cases (non static inner classes, classes without empty or injection constructor and classes implementing Extension).


                    If it was not an extension, I guess I could just have done :


                    public class ModelExtension {
                    
                        @Inject
                        private ModuleEntityClasses moduleEntityClasses;
                        
                        ...
                    }
                    



                    So no need for producer in mosts cases.

                    • 7. Re: Extensions and produces
                      gavin.king

                      Yes but why is ModuleEntityClasses detected as an explicit dependent scoped bean. It has no CDI annotation, no scope, it's just a plain class in the classpath. Does that mean that all classes in a CDI module are registered as explicit beans ?

                      If it has an appropriate constructor, yes.



                      And if it is because it is the return type of the producer method, that means that it is impossible to produce an instance of a class that is present is the classpath because that would always generate this ambiguous dependency error.

                      There are actually several ways to work around this.


                      You could:



                      • annotate the bean class @Alternative

                      • annotate the bean class @Any, thereby suppressing the @Default qualifier

                      • annotate the bean class with some other qualifier

                      • annotate the bean class with an empty @Typed annotation


                      • 8. Re: Extensions and produces
                        gavin.king

                        You could:


                        • annotate the bean class @Alternative

                        • annotate the bean class @Any, thereby suppressing the @Default qualifier

                        • annotate the bean class with some other qualifier

                        • annotate the bean class with an empty @Typed annotation





                        Or:



                        • deploy it in a location that does not have a beans.xml file

                        • veto() the AnnotatedType in a portable extension


                        • 9. Re: Extensions and produces
                          william.drai

                          OK, thanks, all this makes sense after a second thought.


                          I guess it's just a question of becoming used to the behaviour of CDI.