package org.constretto.spring.internal; import org.constretto.ConstrettoConfiguration; import org.constretto.exception.ConstrettoException; import org.constretto.spring.ConfigurationAnnotationConfigurer; import org.constretto.spring.ConstrettoPropertyPlaceholderConfigurer; import org.constretto.spring.annotation.Constretto; import org.constretto.spring.internal.resolver.DefaultAssemblyContextResolver; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.LinkedList; import java.util.List; /** * Helper class for registering Constretto Spring's BeanPostProcessors when using Spring JavaConfig. * * @author zapodot at gmail dot com */ public class ConstrettoImportRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(final AnnotationMetadata importingClassMetadata, final BeanDefinitionRegistry registry) { final ConstrettoConfiguration constrettoConfiguration = getConstrettoConfigurationForConfigurationClass(importingClassMetadata.getClassName()); AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(Constretto.class.getName())); if (annotationAttributes.getBoolean("enablePropertyPlaceholder")) { registry.registerBeanDefinition(ConstrettoPropertyPlaceholderConfigurer.class.getName(), createPropertyPlaceholderBeanDefinition(constrettoConfiguration)); } if (annotationAttributes.getBoolean("enableAnnotationSupport")) { registry.registerBeanDefinition(ConfigurationAnnotationConfigurer.class.getName(), createConfigurationAnnotationConfigurerBeanDefinition(constrettoConfiguration)); } } private BeanDefinition createConfigurationAnnotationConfigurerBeanDefinition(final ConstrettoConfiguration constrettoConfiguration) { final GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(ConfigurationAnnotationConfigurer.class); final ConstructorArgumentValues argumentValues = new ConstructorArgumentValues(); argumentValues.addIndexedArgumentValue(0, constrettoConfiguration); argumentValues.addIndexedArgumentValue(1, new DefaultAssemblyContextResolver()); beanDefinition.setConstructorArgumentValues(argumentValues); return beanDefinition; } private BeanDefinition createPropertyPlaceholderBeanDefinition(final ConstrettoConfiguration constrettoConfiguration) { final GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(ConstrettoPropertyPlaceholderConfigurer.class); final ConstructorArgumentValues argumentValues = new ConstructorArgumentValues(); argumentValues.addIndexedArgumentValue(0, constrettoConfiguration); beanDefinition.setConstructorArgumentValues(argumentValues); return beanDefinition; } private ConstrettoConfiguration getConstrettoConfigurationForConfigurationClass(String className) { Class<?> configurationClass; try { configurationClass = Class.forName(className); } catch (ClassNotFoundException e) { throw new ConstrettoException(String.format("Could not load configuration class \"%s\"", className), e); } final List<Method> methods = findStaticNonArgsMethodsReturningConstrettoConfiguration(configurationClass); if (methods.isEmpty()) { throw new ConstrettoException("Could not find a static factory non-arg method that creates a " + "org.constretto.ConstrettoConfiguration instance in your configuration class (or superclass). " + "In order to use automatic setup of Constretto-Spring BeanPostProcessors " + "you will have to define one"); } else if (methods.size() > 1) { throw new ConstrettoException("Found more than static non-arg method returning a " + "org.constretto.ConstrettoConfiguration instance in your configuration class (or superclass). " + "To use automatic setup of Constretto Spring BeanPostProcessors you should have only one"); } else { Method factoryMethod = methods.get(0); try { return (ConstrettoConfiguration) factoryMethod.invoke(null); } catch (IllegalAccessException e) { throw new ConstrettoException(String.format("Could not invoke factory method \"%1$s\" in configuration class \"%2$s\"", factoryMethod.getName(), configurationClass.getName()), e); } catch (InvocationTargetException e) { throw new ConstrettoException(String.format("Could not invoke factory method \"%1$s\" in configuration class \"%2$s\"", factoryMethod.getName(), configurationClass.getName()), e); } } } private List<Method> findStaticNonArgsMethodsReturningConstrettoConfiguration(Class<?> configurationClass) { return filterMethodsHavingArgs( filterMethodsReturningByReturnTypeNotBeingConstretto( findPublicStaticMethods(configurationClass))); } private List<Method> filterMethodsHavingArgs(Iterable<Method> methods) { List<Method> nonArgsMethods = new LinkedList<Method>(); for (Method method : methods) { if (method.getParameterTypes().length == 0) { nonArgsMethods.add(method); } } return nonArgsMethods; } private List<Method> filterMethodsReturningByReturnTypeNotBeingConstretto(Iterable<Method> methods) { List<Method> constrettoMethods = new LinkedList<Method>(); for (Method method : methods) { if (ConstrettoConfiguration.class.isAssignableFrom(method.getReturnType())) { constrettoMethods.add(method); } } return constrettoMethods; } private List<Method> findPublicStaticMethods(Class<?> configurationClass) { List<Method> staticMethods = new LinkedList<Method>(); for (Method method : configurationClass.getMethods()) { if (Modifier.isStatic(method.getModifiers()) && Modifier.isPublic(method.getModifiers())) { staticMethods.add(method); } } return staticMethods; } }