/** * Copyright (C) 2008 Mathieu Carbou <mathieu.carbou@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 com.mycila.testing.plugin.atunit.container; import atunit.core.Container; import atunit.spring.Bean; import atunit.spring.Context; import com.mycila.testing.core.Mycila; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ApplicationContextException; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.io.UrlResource; import java.lang.reflect.Field; import java.net.URL; import java.util.Map; public class SpringContainer implements Container { public Object createTest(Class<?> testClass, Map<Field, Object> fieldValues) throws Exception { GenericApplicationContext ctx = new GenericApplicationContext(); for (Field field : fieldValues.keySet()) { Bean beanAnno = field.getAnnotation(Bean.class); AbstractBeanDefinition beandef = defineInstanceHolderFactoryBean(field.getType(), fieldValues.get(field)); if ((beanAnno != null) && !beanAnno.value().equals("")) { ctx.registerBeanDefinition(beanAnno.value(), beandef); } else { BeanDefinitionReaderUtils.registerWithGeneratedName(beandef, ctx); } } loadBeanDefinitions(testClass, ctx); fillInMissingFieldBeans(testClass, ctx); ctx.refresh(); for (Field field : testClass.getDeclaredFields()) { field.setAccessible(true); Bean beanAnno = field.getAnnotation(Bean.class); if (beanAnno == null) { if (fieldValues.containsKey(field)) { field.set(Mycila.currentExecution().context().introspector().instance(), fieldValues.get(field)); } } else { if (!beanAnno.value().equals("")) { field.set(Mycila.currentExecution().context().introspector().instance(), ctx.getBean(beanAnno.value())); } else { String[] beanNames = ctx.getBeanNamesForType(field.getType()); if (beanNames.length < 1) { throw new BeanCreationException("There are no beans defined with type " + field.getType()); } if (beanNames.length > 1) { throw new BeanCreationException("There are " + beanNames.length + " beans defined with type " + field.getType() + "; consider wiring by name instead"); } field.set(Mycila.currentExecution().context().introspector().instance(), ctx.getBean(beanNames[0])); } } } return null; } protected void loadBeanDefinitions(Class<?> testClass, BeanDefinitionRegistry registry) { XmlBeanDefinitionReader xml = new XmlBeanDefinitionReader(registry); String resourceName = testClass.getSimpleName() + ".xml"; Context ctxAnno = testClass.getAnnotation(Context.class); if (ctxAnno != null) { resourceName = ctxAnno.value(); } URL xmlUrl = testClass.getResource(resourceName); if (xmlUrl != null) { xml.loadBeanDefinitions(new UrlResource(xmlUrl)); } else if (ctxAnno != null) { // is this the appropriate exception here? throw new ApplicationContextException("Could not find context file named " + resourceName); } } protected void fillInMissingFieldBeans(Class<?> testClass, GenericApplicationContext ctx) throws Exception { for (Field field : testClass.getDeclaredFields()) { Bean beanAnno = field.getAnnotation(Bean.class); if (beanAnno == null) continue; String name = beanAnno.value(); if (!name.equals("") && !ctx.containsBean(name)) { ctx.registerBeanDefinition(name, defineAutowireBean(field.getType())); } else if (ctx.getBeansOfType(field.getType()).isEmpty()) { BeanDefinitionReaderUtils.registerWithGeneratedName(defineAutowireBean(field.getType()), ctx); } } } protected AbstractBeanDefinition defineAutowireBean(Class<?> type) throws Exception { AbstractBeanDefinition beandef = BeanDefinitionReaderUtils.createBeanDefinition(null, type.getName(), type.getClassLoader()); beandef.setAutowireCandidate(true); beandef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_AUTODETECT); return beandef; } protected AbstractBeanDefinition defineInstanceHolderFactoryBean(Class<?> type, Object instance) throws Exception { ConstructorArgumentValues args = new ConstructorArgumentValues(); args.addIndexedArgumentValue(0, type); args.addIndexedArgumentValue(1, instance); AbstractBeanDefinition beandef = BeanDefinitionReaderUtils.createBeanDefinition(null, InstanceHolderFactoryBean.class.getName(), getClass().getClassLoader()); beandef.setConstructorArgumentValues(args); beandef.setAutowireCandidate(true); return beandef; } protected static class InstanceHolderFactoryBean implements FactoryBean { final Class<?> type; final Object instance; public InstanceHolderFactoryBean(Class<?> type, Object instance) { this.type = type; this.instance = instance; } public Object getObject() throws Exception { return instance; } public Class<?> getObjectType() { return type; } public boolean isSingleton() { return true; } } }