5 Replies Latest reply on Feb 18, 2010 6:52 PM by William Drai

    Spring context portable extension

    Tilman Rossmy Newbie

      I have created a portable extension that injects all the beans of the spring context into the cdi context like this:




      package weld.example.portable.extension.spring;
      
      import java.lang.annotation.Annotation;
      import java.lang.reflect.Type;
      import java.util.Collections;
      import java.util.HashSet;
      import java.util.List;
      import java.util.Set;
      
      import javax.enterprise.context.ApplicationScoped;
      import javax.enterprise.context.RequestScoped;
      import javax.enterprise.context.SessionScoped;
      import javax.enterprise.context.spi.CreationalContext;
      import javax.enterprise.event.Observes;
      import javax.enterprise.inject.spi.AfterBeanDiscovery;
      import javax.enterprise.inject.spi.Bean;
      import javax.enterprise.inject.spi.BeanManager;
      import javax.enterprise.inject.spi.Extension;
      import javax.enterprise.inject.spi.InjectionPoint;
      import javax.enterprise.inject.spi.ProcessAnnotatedType;
      import javax.enterprise.inject.spi.ProcessProducer;
      import javax.enterprise.inject.spi.Producer;
      
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.annotation.Scope;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      /**
       * User: rossmyt
       * Date: 18.02.2010
       * Time: 09:22:28
       */
      public class SpringContextExtension implements Extension
      {
          private  ApplicationContext applicationContext;
          private List<Bean> beans= new java.util.ArrayList<Bean>();
      
          void processAnnotatedType(@Observes ProcessAnnotatedType pat)
          {
              boolean isSpring = pat.getAnnotatedType().isAnnotationPresent(SpringContext.class);
              if (isSpring)
              {
                  SpringContext springContext = pat.getAnnotatedType().getAnnotation(SpringContext.class);
                  String configFile = springContext.configFile();
                  applicationContext = new ClassPathXmlApplicationContext(configFile);
                  String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
                  for (final String beanDefinitionName : beanDefinitionNames)
                  {
                      final Object bean = applicationContext.getBean(beanDefinitionName);
                      final Class type = applicationContext.getType(beanDefinitionName);
                      Scope scope = bean.getClass().getAnnotation(Scope.class);
                      Class cdiScope= ApplicationScoped.class;
                      if (scope!=null)
                      {
                          String scopeName = scope.value();
                          if (scopeName.equals("request"))
                          {
                              cdiScope= RequestScoped.class;
                          }
                          else if (scopeName.equals("session"))
                          {
                              cdiScope= SessionScoped.class;
                          }
                      }
                      final Class scp=cdiScope;
      
                      final Set<Type> types = new HashSet<Type>()
                      {
                          {
                              add(type);
                              add(Object.class);
                          } };
      
                      Bean<?> cdiBean = new Bean<Object>()
                      {
                          @Override
                          public Set<Type> getTypes()
                          {
                              return types;
                          }
      
                          @Override
                          public Set<Annotation> getQualifiers()
                          {
                              return Collections.emptySet();
                          }
      
                          @SuppressWarnings({"unchecked"})
                          @Override
                          public Class<? extends Annotation> getScope()
                          {
                              return scp;
                          }
      
                          @Override
                          public String getName()
                          {
                              return beanDefinitionName;
                          }
      
                          @Override
                          public Set<Class<? extends Annotation>> getStereotypes()
                          {
                              return Collections.emptySet();
                          }
      
                          @Override
                          public Class<?> getBeanClass()
                          {
                              return type;
                          }
      
                          @Override
                          public boolean isAlternative()
                          {
                              return false;
                          }
      
                          @Override
                          public boolean isNullable()
                          {
                              return false;
                          }
      
                          @Override
                          public Set<InjectionPoint> getInjectionPoints()
                          {
                              return Collections.emptySet();
                          }
      
                          @Override
                          public Object create(CreationalContext<Object> objectCreationalContext)
                          {
                              return bean;
                          }
      
                          @Override
                          public void destroy(Object instance, CreationalContext<Object> objectCreationalContext)
                          {
                             objectCreationalContext.release();
                          }
                      };
                      beans.add(cdiBean);
                  }
      
              }
      
          }
      
          void afterBeanDiscovery(@Observes AfterBeanDiscovery abd)
          {
              for (Bean bean:beans)
              {
                  abd.addBean(bean);
              }
              System.out.println("Added spring context!");
          }
      }
      



      with the SpringContext @interface




      package weld.example.portable.extension.spring;
      
      import java.lang.annotation.Retention;
      import java.lang.annotation.Target;
      
      import static java.lang.annotation.ElementType.PARAMETER;
      import static java.lang.annotation.RetentionPolicy.RUNTIME;
      
      /**
       * User: rossmyt
       * Date: 18.02.2010
       * Time: 09:27:36
       */
      @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD})
      @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
      public @interface SpringContext
      {
          public String configFile () default "application.xml";
      }
      



      The Spring context is injected in this bean




      package weld.example;
      
      import java.io.Serializable;
      
      import javax.enterprise.context.ApplicationScoped;
      import javax.enterprise.context.SessionScoped;
      import javax.enterprise.inject.Produces;
      import javax.inject.Named;
      import javax.persistence.EntityManager;
      import javax.persistence.PersistenceContext;
      
      import weld.example.portable.extension.spring.SpringContext;
      
      /**
       * User: rossmyt
       * Date: 17.02.2010
       * Time: 16:41:35
       */
      @Named(value = "init")
      @ApplicationScoped
      @SpringContext
      public class Init implements Serializable
      {
          @Produces
          @ApplicationScoped
          @PersistenceContext(unitName = "foo")
          EntityManager em;
      
      }
      



      I test it with the following spring config file application.xml




      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
           <bean id="springTest" class="weld.example.SpringTest">
               <property name="doesItWork" value="yes"/>
           </bean>
      </beans>



      and




      package weld.example;
      
      import java.io.Serializable;
      
      /**
       * User: rossmyt
       * Date: 18.02.2010
       * Time: 11:53:57
       */
      public class SpringTest  implements Serializable
      {
          private String doesItWork;
      
          public String getDoesItWork()
          {
              return doesItWork;
          }
      
          public void setDoesItWork(String doesItWork)
          {
              this.doesItWork = doesItWork;
          }
      }
      



      So far so good. If I test with a jsf page


      <h1>#{springTest.doesItWork}</h1>



      everything works as expected.


      But if I inject the springTest bean into another bean like this




      @Inject
       SpringTest springTest;



      a new Instance of SpringTest is created, with springTest.getDoesItWork returning null.


      On the other hand, if I inject the beanManager like this




       @Inject
        BeanManager beanManager;



      then beanManager.getBeans(springTest) returns a Set containing the expected springTest bean with springTest.getDoesItWork() returning 'yes'


      So, what is it that I don't understand correctly?

















        • 1. Re: Spring context portable extension
          he youlin Novice

          You have two SpringTest bean. One is Extension define spring sington scope bean. One is cdi define depend scope bean.

          • 2. Re: Spring context portable extension
            Tilman Rossmy Newbie

            I don't really understand why I have two SpringTest beans...anyway if I add a qualifier it works:


            1) The qualifier


            package weld.example.portable.extension.spring;
            
            import javax.inject.Qualifier;
            
            /**
             * User: rossmyt
             * Date: 18.02.2010
             * Time: 17:14:06
             */
            @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD})
            @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
            @Qualifier
            public @interface SpringBean
            {
            }
            



            2.) adding the qualifier to the bean definition (in SpringContextExtension):




                               @Override
                                public Set<Annotation> getQualifiers()
                                {
                                    HashSet<Annotation> ret = new HashSet<Annotation>();
                                    Annotation annotation= new Annotation()
                                    {
                                        @Override
                                        public Class<? extends Annotation> annotationType()
                                        {
                                            return SpringBean.class;
                                        }
                                    };
                                    ret.add(annotation);
                                    return ret;
                                }



            3.) adding the @SpringBean annotation to the field declaration:




             @Inject
              @SpringBean        
              SpringTest springTest;







            • 3. Re: Spring context portable extension
              he youlin Novice

              Oh. I just tell you to do that :)

              • 4. Re: Spring context portable extension
                he youlin Novice

                @Override
                public String getName()
                {
                    return beanDefinitionName;
                }


                this similar as @Name(springTest)


                So jsf use Extension define spring bean.


                
                @Inject
                 SpringTest springTest;
                
                Use cdi define depend scope bean.
                
                Your Extension define spring bean have no cdi define scope. 
                
                 @SuppressWarnings({"unchecked"})
                 @Override
                 public Class<? extends Annotation> getScope()
                 {
                    return scp;
                  }
                
                
                
                SpringTest Only return sington(? or orthers?) scope that don't recognised by weld.
                
                
                BWT, I don't think it's a better way to mix spring scope to cdi scope. You can use spring proxy object so spring can auto manage it. So you just try doing how to inject spring proxied bean to cdi bean.
                


                • 5. Re: Spring context portable extension
                  William Drai Newbie

                  You can also use @Inject @Named(springTest) SpringTest test


                  Maybe you can remove the need for the SpringBean or Named qualifier by vetoing in your extension any bean class that is already in the Spring context.