/******************************************************************************* * Copyright (c) 2012, 2014 Pivotal Software, Inc. * 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.metadata.process; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IMarker; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; import org.springframework.beans.BeanMetadataAttributeAccessor; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.SourceExtractor; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.ide.eclipse.beans.core.BeansCorePlugin; import org.springframework.ide.eclipse.beans.core.internal.model.DelegatingSourceExtractor; import org.springframework.ide.eclipse.beans.core.internal.model.ProfileAwareCompositeComponentDefinition; import org.springframework.ide.eclipse.beans.core.internal.model.ToolingAwareEnvironment; import org.springframework.ide.eclipse.beans.core.model.process.IBeansConfigPostProcessingContext; import org.springframework.ide.eclipse.beans.core.model.process.IBeansConfigPostProcessor; import org.springframework.ide.eclipse.beans.core.model.process.IBeansConfigRegistrationSupport; import org.springframework.ide.eclipse.core.java.JdtUtils; import org.springframework.ide.eclipse.core.java.classreading.CachingJdtMetadataReaderFactory; import org.springframework.ide.eclipse.core.java.classreading.JdtConnectedMetadata; import org.springframework.ide.eclipse.core.model.java.JavaModelMethodSourceLocation; import org.springframework.ide.eclipse.core.model.java.JavaModelSourceLocation; import org.springframework.ide.eclipse.core.model.validation.ValidationProblem; /** * {@link IBeansConfigPostProcessor} that mirrors the behavior of Spring 3.0's {@link ConfigurationClassPostProcessor}. * @author Christian Dupuis * @author Martin Lippert * @since 2.1.0 */ @SuppressWarnings("restriction") public class JdtConfigurationClassPostProcessor implements IBeansConfigPostProcessor { /** * {@inheritDoc} */ public void postProcess(final IBeansConfigPostProcessingContext postProcessingContext) { IJavaProject project = JdtUtils.getJavaProject(postProcessingContext.getBeansConfig().getElementResource()); if (project == null) { return; } ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ConfigurationClassPostProcessor processor = new ConfigurationClassPostProcessor(); DelegatingSourceExtractor sourceExtractor = new DelegatingSourceExtractor(project.getProject()); processor.setEnvironment(new ToolingAwareEnvironment()); processor.setSourceExtractor(sourceExtractor); processor.setMetadataReaderFactory(new CachingJdtMetadataReaderFactory(project, classLoader)); processor.setProblemReporter(new JdtAnnotationMetadataProblemReporter(postProcessingContext)); processor.setResourceLoader(new DefaultResourceLoader(classLoader)); ReaderEventListenerForwardingBeanDefinitionRegistry registry = new ReaderEventListenerForwardingBeanDefinitionRegistry( postProcessingContext.getBeanDefinitionRegistry(), postProcessingContext .getBeansConfigRegistrySupport(), sourceExtractor); registry.setBeanClassLoader(classLoader); processor.processConfigBeanDefinitions(registry); } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { return obj instanceof JdtConfigurationClassPostProcessor; } /** * {@inheritDoc} */ @Override public int hashCode() { return getClass().getName().hashCode(); } /** * {@link BeanDefinitionRegistry} implementation that funnels * {@link #registerBeanDefinition(String, BeanDefinition)} calls to * {@link IBeansConfigRegistrationSupport#registerComponent()}. */ class ReaderEventListenerForwardingBeanDefinitionRegistry extends DefaultListableBeanFactory implements BeanDefinitionRegistry { private final BeanDefinitionRegistry registry; private final IBeansConfigRegistrationSupport beansConfigRegistrationSupport; private final Set<String> profileDefinedBeans; private final SourceExtractor sourceExtractor; public ReaderEventListenerForwardingBeanDefinitionRegistry(BeanDefinitionRegistry registry, IBeansConfigRegistrationSupport beansConfigRegistrationSupport, SourceExtractor sourceExtractor) { this.registry = registry; this.beansConfigRegistrationSupport = beansConfigRegistrationSupport; this.sourceExtractor = sourceExtractor; this.profileDefinedBeans = new HashSet<String>(); } /** * {@inheritDoc} */ public boolean containsBeanDefinition(String beanName) { return (!profileDefinedBeans.contains(beanName) && registry.containsBeanDefinition(beanName)); } /** * {@inheritDoc} */ public String[] getAliases(String name) { return registry.getAliases(name); } /** * {@inheritDoc} */ public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { return registry.getBeanDefinition(beanName); } /** * {@inheritDoc} */ public int getBeanDefinitionCount() { return registry.getBeanDefinitionCount(); } /** * {@inheritDoc} */ public String[] getBeanDefinitionNames() { return registry.getBeanDefinitionNames(); } /** * {@inheritDoc} */ public boolean isAlias(String beanName) { return registry.isAlias(beanName); } /** * {@inheritDoc} */ public boolean isBeanNameInUse(String beanName) { return registry.isBeanNameInUse(beanName); } /** * {@inheritDoc} */ public void registerAlias(String name, String alias) { registry.registerAlias(name, alias); } /** * {@inheritDoc} */ public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Object source = beanDefinition.getSource(); if (!(source instanceof JavaModelSourceLocation)) { source = sourceExtractor.extractSource(beanDefinition.getSource(), null); } // Convert the Bean definition to a bean definition with concrete class and id if (source instanceof JavaModelSourceLocation) { IJavaElement javaElement = JavaCore.create(((JavaModelSourceLocation) source).getHandleIdentifier(), DefaultWorkingCopyOwner.PRIMARY); if (javaElement instanceof IMethod && beanDefinition instanceof AnnotatedBeanDefinition && source instanceof JavaModelMethodSourceLocation) { JdtAnnotationBeanDefinition newBeanDefinition = new JdtAnnotationBeanDefinition(((AnnotatedBeanDefinition) beanDefinition).getMetadata()); newBeanDefinition.setSource(source); String className = ((JavaModelMethodSourceLocation) source).getReturnType(); newBeanDefinition.setBeanClassName(className); registry.registerBeanDefinition(beanName, newBeanDefinition); super.registerBeanDefinition(beanName, newBeanDefinition); Map<String, Object> profileAnnotationMetadata = newBeanDefinition.getMetadata(). getAnnotationAttributes("org.springframework.context.annotation.Profile"); if (profileAnnotationMetadata != null && profileAnnotationMetadata.get("value") != null && profileAnnotationMetadata.get("value") instanceof String[]) { String[] profiles = (String[]) profileAnnotationMetadata.get("value"); ProfileAwareCompositeComponentDefinition profileAware = new ProfileAwareCompositeComponentDefinition(beanName, newBeanDefinition.getSource(), profiles); profileAware.addNestedComponent(new BeanComponentDefinition(newBeanDefinition, beanName)); beansConfigRegistrationSupport.registerComponent(profileAware); profileDefinedBeans.add(beanName); } else { beansConfigRegistrationSupport.registerComponent(new BeanComponentDefinition(newBeanDefinition, beanName)); } } else { if (beanDefinition instanceof BeanMetadataAttributeAccessor && beanDefinition.getSource() != source) { ((BeanMetadataAttributeAccessor) beanDefinition).setSource(source); } registry.registerBeanDefinition(beanName, beanDefinition); super.registerBeanDefinition(beanName, beanDefinition); beansConfigRegistrationSupport.registerComponent(new BeanComponentDefinition(beanDefinition, beanName)); } } else { if (beanDefinition instanceof BeanMetadataAttributeAccessor && beanDefinition.getSource() != source) { ((BeanMetadataAttributeAccessor) beanDefinition).setSource(source); } registry.registerBeanDefinition(beanName, beanDefinition); super.registerBeanDefinition(beanName, beanDefinition); beansConfigRegistrationSupport.registerComponent(new BeanComponentDefinition(beanDefinition, beanName)); } } /** * {@inheritDoc} */ public void removeAlias(String alias) { registry.removeAlias(alias); } /** * {@inheritDoc} */ public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { registry.removeBeanDefinition(beanName); super.removeBeanDefinition(beanName); } } @SuppressWarnings("serial") class JdtAnnotationBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition { private final AnnotationMetadata annotationMetadata; public JdtAnnotationBeanDefinition(AnnotationMetadata annotationMetadata) { this.annotationMetadata = annotationMetadata; } public AnnotationMetadata getMetadata() { return annotationMetadata; } public MethodMetadata getFactoryMethodMetadata() { return null; } } class JdtAnnotationMetadataProblemReporter implements ProblemReporter { private final IBeansConfigPostProcessingContext postProcessingContext; public JdtAnnotationMetadataProblemReporter(IBeansConfigPostProcessingContext postProcessingContext) { this.postProcessingContext = postProcessingContext; } /** * {@inheritDoc} */ public void error(Problem problem) { createProblem(IMarker.SEVERITY_ERROR, problem.getMessage(), problem.getLocation().getSource()); } /** * {@inheritDoc} */ public void fatal(Problem problem) { error(problem); } /** * {@inheritDoc} */ public void warning(Problem problem) { createProblem(IMarker.SEVERITY_WARNING, problem.getMessage(), problem.getLocation().getSource()); } private void createProblem(int severity, String message, Object source) { if (source instanceof JdtConnectedMetadata) { IJavaElement je = ((JdtConnectedMetadata) source).getJavaElement(); createProblem(severity, postProcessingContext, message, je); } } private void createProblem(int severity, IBeansConfigPostProcessingContext postProcessingContext, String message, IJavaElement je) { try { postProcessingContext.reportProblem(new ValidationProblem(severity, message, je.getUnderlyingResource(), JdtUtils.getLineNumber(je))); } catch (JavaModelException e) { BeansCorePlugin.log(e); } } } }