Rick

Rick
Rick

Tuesday, April 12, 2011

Combining Spring With CDI Part II

#summary Strategy for combining Spring and CDI

I added support for discovering all the beans in CDI and registering them all in Spring as bean definitions.

This is a continuations of the last post part 1.

The last step we added a FactoryBean that was tied to the TaskRepository.
The next step is to make this generic.

CdiFactoryBean

package org.cdisource.springintegration;

import javax.enterprise.inject.spi.BeanManager;
import javax.naming.InitialContext;

import org.springframework.beans.factory.FactoryBean;
import org.cdisource.beancontainer.BeanContainer;
import org.cdisource.beancontainer.BeanContainerImpl;
import org.cdisource.beancontainer.BeanContainerInitializationException;

public class CdiFactoryBean implements FactoryBean<Object> {

 private BeanContainer beanContainer = null;
 private final String BEAN_MANAGER_LOCATION = "java:comp/BeanManager";
 
 private Class<?> beanClass;
 
 private boolean singleton = true;

 
 public void setBeanClass(Class<?> beanClass) {
  this.beanClass = beanClass;
 }

 @Override
 public Object getObject() throws Exception {

  if (beanContainer==null) {
   InitialContext ic = new InitialContext();
   Object bean = null;
   try {
    bean = ic.lookup(BEAN_MANAGER_LOCATION);
   } catch (Exception e) {
    throw new BeanContainerInitializationException("Unable to lookup BeanManager instance in JNDI", e);
   }
   if (bean == null) {
    throw new BeanContainerInitializationException(
      "Null value returned when looking up the BeanManager from JNDI");
   }
   if (bean instanceof BeanManager) {
    beanContainer = new BeanContainerImpl((BeanManager)bean);
   } else {
    String msg = "Looked up JNDI Bean is not a BeanManager instance, bean type is " + bean.getClass().getName();
    throw new BeanContainerInitializationException(msg);
   }
  }
  return beanContainer.getBeanByType(beanClass);
 }

 @Override
 public Class<?> getObjectType() {
  return beanClass;
 }

 @Override
 public boolean isSingleton() {
  return singleton;
 }

 public void setSingleton(boolean singleton) {
  this.singleton = singleton;
 }

}


Then you can register this in the applicationContext as follows:
applicationContext.xml

     <bean class="org.cdisource.springintegration.CdiFactoryBean" name="taskRespository" >
      <property name="beanClass" value="org.cdisource.springapp.TaskRepository"/>
     </bean>


Of course, this is a bit of a pain. Do you really want to register every CDI bean in your system so it is available for Spring? Imagine a system with 25 CDI beans or 250 CDI beans. Imagine what the Spring XML file will look like. Yuck.

We need a way to map CDI beans into the spring applicationContext.

The next step is bulk import a bunch of beans from CDI into Spring as follows:

package org.cdisource.springintegration;

import java.lang.annotation.Annotation;
import java.util.Set;

import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Named;
import javax.naming.InitialContext;

import org.cdisource.beancontainer.BeanContainerInitializationException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

public class CdiBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

 @Override
 public void postProcessBeanFactory(
   ConfigurableListableBeanFactory beanFactory) throws BeansException {
  
  DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
  
  Set<Bean<?>> beans = beanManager().getBeans(Object.class);
  for (Bean bean : beans) {
   BeanDefinitionBuilder definition = BeanDefinitionBuilder.rootBeanDefinition(CdiFactoryBean.class).addPropertyValue("beanClass", bean.getBeanClass()).setLazyInit(true);
   Named named = (Named) bean.getBeanClass().getAnnotation(Named.class);
   String name = named != null ? named.value() : bean.getBeanClass().getSimpleName() + "FactoryBean";
   factory.registerBeanDefinition(name, definition.getBeanDefinition());
  }
 }

 
 private final String BEAN_MANAGER_LOCATION = "java:comp/BeanManager";
 private BeanManager beanManager;

 private BeanManager beanManager() {
  if (beanManager == null) {
   Object bean = null;

   try {
    InitialContext ic = new InitialContext();
    bean = ic.lookup(BEAN_MANAGER_LOCATION);
   } catch (Exception e) {
    throw new BeanContainerInitializationException(
      "Unable to lookup BeanManager instance in JNDI", e);
   }
   if (bean == null) {
    throw new BeanContainerInitializationException(
      "Null value returned when looking up the BeanManager from JNDI");
   }
   if (bean instanceof BeanManager) {
    this.beanManager = (BeanManager) bean;
   } else {
    String msg = "Looked up JNDI Bean is not a BeanManager instance, bean type is "
      + bean.getClass().getName();
    throw new BeanContainerInitializationException(msg);
   }
  }
  return this.beanManager;

 }

}


The heart of the class functionality is here:

  DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
  
  Set<Bean<?>> beans = beanManager().getBeans(Object.class);
  for (Bean bean : beans) {
   BeanDefinitionBuilder definition = BeanDefinitionBuilder
                                .rootBeanDefinition(CdiFactoryBean.class)
                                .addPropertyValue("beanClass", 
                                bean.getBeanClass()).setLazyInit(true);
   Named named = (Named) bean.getBeanClass().getAnnotation(Named.class);
   String name = named != null ? named.value() : bean.getBeanClass().getSimpleName() + "FactoryBean";
   factory.registerBeanDefinition(name, definition.getBeanDefinition());
  }


We iterate through the beans from CDI, create a Spring definition that uses CdiFactoryBean from the last step.

We register the CdiFactoryBean. We use the name from the @Named attribute if possible. If not we generate a name.

There are 4 code listings in this article

1 comment:

  1. I additional help for finding all of the beans in CDI and registering them all in Spring as bean definitions
    What can you do when you have the free time? How about to get access to the online games? When we talk about the game, we need to talk about the WOW Gold? As the WOW is the world's most famous online game,all of us knows that we have to get the World Of Warcraft Gold to save money !

    ReplyDelete

Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training