/** * Copyright (C) 2013 Kametic <epo.jemba@kametic.com> * * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3, 29 June 2007; * or any later version * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.gnu.org/licenses/lgpl-3.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.nuunframework.spring; import com.google.inject.AbstractModule; import com.google.inject.name.Names; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import java.util.HashMap; import java.util.Map; class SpringModule extends AbstractModule { class SpringBeanDefinition { private String name; private ConfigurableListableBeanFactory beanFactory; SpringBeanDefinition(String name, ConfigurableListableBeanFactory beanFactory) { this.name = name; this.beanFactory = beanFactory; } public String getName() { return name; } public ConfigurableListableBeanFactory getBeanFactory() { return beanFactory; } } private Logger logger = LoggerFactory.getLogger(InternalDependencyInjectionProvider.class); private final ConfigurableListableBeanFactory beanFactory; private Map<Class<?>, Map<String, SpringBeanDefinition>> beanDefinitions; public SpringModule(ConfigurableListableBeanFactory beanFactory) { this.beanFactory = beanFactory; } @Override protected void configure() { this.beanDefinitions = new HashMap<Class<?>, Map<String, SpringBeanDefinition>>(); bindFromApplicationContext(); } @SuppressWarnings("unchecked") private void bindFromApplicationContext() { boolean debugEnabled = logger.isDebugEnabled(); ConfigurableListableBeanFactory currentBeanFactory = this.beanFactory; do { for (String beanName : currentBeanFactory.getBeanDefinitionNames()) { BeanDefinition beanDefinition = currentBeanFactory.getMergedBeanDefinition(beanName); if (!beanDefinition.isAbstract()) { Class<?> beanClass = classFromString(beanDefinition.getBeanClassName()); if (beanClass == null) { logger.warn("Cannot bind spring bean " + beanName + " because its class " + beanDefinition.getBeanClassName() + " failed to load"); return; } SpringBeanDefinition springBeanDefinition = new SpringBeanDefinition(beanName, currentBeanFactory); // Adding bean with its base type addBeanDefinition(beanClass, springBeanDefinition); // Adding bean with its parent type if enabled Class<?> parentClass = beanClass.getSuperclass(); if (parentClass != null && parentClass != Object.class) addBeanDefinition(parentClass, springBeanDefinition); // Adding bean with its immediate interfaces if enabled for (Class<?> i : beanClass.getInterfaces()) addBeanDefinition(i, springBeanDefinition); } } BeanFactory factory = currentBeanFactory.getParentBeanFactory(); if (factory != null) { if (factory instanceof ConfigurableListableBeanFactory) currentBeanFactory = (ConfigurableListableBeanFactory)factory; else { logger.info("Cannot go up further in the bean factory hierarchy, parent bean factory doesn't implement ConfigurableListableBeanFactory"); currentBeanFactory = null; } } else currentBeanFactory = null; } while (currentBeanFactory != null); for (Map.Entry<Class<?>, Map<String, SpringBeanDefinition>> entry : this.beanDefinitions.entrySet()) { Class<?> type = entry.getKey(); Map<String, SpringBeanDefinition> definitions = entry.getValue(); // Bind by name for each bean of this type and by type if there is no ambiguity for (SpringBeanDefinition candidate : definitions.values()) { if (debugEnabled) logger.info("Binding spring bean " + candidate.getName() + " by name and type " + type.getCanonicalName()); bind(type).annotatedWith(Names.named(candidate.getName())).toProvider( new ByNameSpringContextProvider(type, candidate.getName(), candidate.getBeanFactory())); } } } private void addBeanDefinition(Class<?> beanClass, SpringBeanDefinition springBeanDefinition) { Map<String, SpringBeanDefinition> beansOfType = this.beanDefinitions.get(beanClass); if (beansOfType == null) this.beanDefinitions.put(beanClass, beansOfType = new HashMap<String, SpringBeanDefinition>()); if (!beansOfType.containsKey(springBeanDefinition.getName())) // TODO determine which beans override which in spring semantics ? Here first discovered wins... beansOfType.put(springBeanDefinition.getName(), springBeanDefinition); } private Class<?> classFromString(String className) { try { return Class.forName(className); } catch (ClassNotFoundException e) { return null; } } }