/******************************************************************************* * Copyright (c) 2007, 2014 Spring IDE Developers * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.core.internal.model.validation.rules; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.ide.eclipse.beans.core.internal.model.Bean; import org.springframework.ide.eclipse.beans.core.internal.model.BeansModelUtils; import org.springframework.ide.eclipse.beans.core.model.IBean; import org.springframework.ide.eclipse.beans.core.model.validation.IBeansValidationContext; import org.springframework.ide.eclipse.core.java.Introspector.Static; import org.springframework.ide.eclipse.core.java.JdtUtils; import org.springframework.ide.eclipse.core.java.typehierarchy.TypeHierarchyEngine; import org.springframework.ide.eclipse.core.model.validation.ValidationProblemAttribute; import org.springframework.scripting.ScriptFactory; import org.springsource.ide.eclipse.commons.core.SpringCoreUtils; /** * Validates a given {@link IBean}'s factory bean and factory method. * @author Torsten Juergeleit * @author Christian Dupuis * @since 2.0 */ public class BeanFactoryRule extends AbstractBeanMethodValidationRule { @Override public void validate(IBean bean, IBeansValidationContext context, IProgressMonitor monitor) { TypeHierarchyEngine typeEngine = getTypeHierarchyEngine(context); AbstractBeanDefinition bd = (AbstractBeanDefinition) ((Bean) bean).getBeanDefinition(); BeanDefinition mergedBd = BeansModelUtils.getMergedBeanDefinition(bean, context.getContextElement()); String mergedClassName = mergedBd.getBeanClassName(); // Validate factory bean and it's non-static factory method if (bd.getFactoryBeanName() != null) { if (bd.getFactoryMethodName() == null) { context.error(bean, "NO_FACTORY_METHOD", "A factory bean requires a factory method"); } else { validateFactoryBean(bean, bd.getFactoryBeanName(), bd.getFactoryMethodName(), context, typeEngine); } } // Validate bean's static factory method with bean class from merged bean definition - skip // factory methods with placeholders else { String methodName = bd.getFactoryMethodName(); try { if (methodName != null && mergedClassName != null && !SpringCoreUtils.hasPlaceHolder(mergedClassName) && !SpringCoreUtils.hasPlaceHolder(methodName)) { IType type = JdtUtils.getJavaType(context.getRootElementProject(), mergedClassName); if (type == null) { return; } // Use constructor argument values of root bean as arguments for static factory method int argCount = (!bd.isAbstract() && bd.getAutowireMode() != AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR ? bd .getConstructorArgumentValues().getArgumentCount() : -1); if (type.isEnum()) { if (!"valueOf".equals(methodName) && (argCount == 0 || argCount > 2)) { validateFactoryMethod(bean, mergedClassName, methodName, argCount, Static.DONT_CARE, context, typeEngine); } } else { validateFactoryMethod(bean, mergedClassName, methodName, argCount, Static.YES, context, typeEngine); } } } catch (JavaModelException e) { } } // Check if factory class can be found, but only if bean definition is not abstract if (!bd.isAbstract() && bd.getFactoryMethodName() != null && mergedClassName == null && bd.getParentName() == null && bd.getFactoryBeanName() == null) { context.error(bean, "BEAN_WITHOUT_CLASS_OR_PARENT", "Factory method needs class from root " + "or parent bean"); } } protected void validateFactoryBean(IBean bean, String beanName, String methodName, IBeansValidationContext context, TypeHierarchyEngine typeEngine) { if (beanName != null && !SpringCoreUtils.hasPlaceHolder(beanName)) { try { AbstractBeanDefinition factoryBd = (AbstractBeanDefinition) context.getCompleteRegistry() .getBeanDefinition(beanName); // Skip validating factory beans which are created by another // factory bean if (factoryBd.getFactoryBeanName() == null) { if (factoryBd.isAbstract() || factoryBd.getBeanClassName() == null) { context.error(bean, "INVALID_FACTORY_BEAN", "Referenced factory bean '" + beanName + "' is invalid (abstract or no bean class)", new ValidationProblemAttribute("BEAN", beanName)); } else { // Validate non-static factory method in factory bean // Factory beans with factory methods can only be // validated during runtime - so skip them if (factoryBd.getFactoryMethodName() == null) { validateFactoryMethod(bean, factoryBd.getBeanClassName(), methodName, (bean.getConstructorArguments().size() > 0 ? bean.getConstructorArguments().size() : -1), Static.NO, context, typeEngine); } } } } catch (NoSuchBeanDefinitionException e) { // Skip error "parent name is equal to bean name" if (!e.getBeanName().equals(bean.getElementName())) { context.error(bean, "UNDEFINED_FACTORY_BEAN", "Factory bean '" + beanName + "' not found", new ValidationProblemAttribute("BEAN", beanName)); } } } } protected void validateFactoryMethod(IBean bean, String className, String methodName, int argCount, Static statics, IBeansValidationContext context, TypeHierarchyEngine typeEngine) { if (className != null && !SpringCoreUtils.hasPlaceHolder(className)) { IType type = JdtUtils.getJavaType(BeansModelUtils.getProject(bean).getProject(), className); // Skip factory-method validation for factory beans which are // Spring factory beans as well and for those aspectOf methods if (type != null && !ValidationRuleUtils.ASPECT_OF_METHOD_NAME.equals(methodName) && !JdtUtils.doesImplement(context.getRootElementResource(), type, FactoryBean.class.getName(), typeEngine) && !JdtUtils.doesImplement(context.getRootElementResource(), type, ScriptFactory.class.getName(), typeEngine)) { validateMethod(bean, type, MethodType.FACTORY, methodName, argCount, statics, context); } } } }