/* * Copyright 2002-2011 the original author or authors. * * 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 org.springframework.flex.config; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.flex.remoting.RemotingDestination; import org.springframework.flex.remoting.RemotingDestinationExporter; import org.springframework.flex.remoting.RemotingExclude; import org.springframework.flex.remoting.RemotingInclude; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * {@link BeanFactoryPostProcessor} implementation that searches the {@link BeanFactory} for beans annotated with * {@link RemotingDestination} and adds a corresponding {@link RemotingDestinationExporter} bean definition according to * the attributes of the {@link RemotingDestination} annotation and any methods found to be marked with either the * {@link RemotingInclude} or {@link RemotingExclude} annotation. * * <p> * This processor will be enabled automatically when using the message-broker tag of the xml config namespace. * * @author Jeremy Grelle */ public class RemotingAnnotationPostProcessor implements BeanFactoryPostProcessor { private static final Log log = LogFactory.getLog(RemotingAnnotationPostProcessor.class); // --------------------------- Bean Configuration Properties -------------// private static final String MESSAGE_BROKER_PROPERTY = "messageBroker"; private static final String SERVICE_PROPERTY = "service"; private static final String DESTINATION_ID_PROPERTY = "destinationId"; private static final String CHANNELS_PROPERTY = "channels"; private static final String INCLUDE_METHODS_PROPERTY = "includeMethods"; private static final String EXCLUDE_METHODS_PROPERTY = "excludeMethods"; private static final String SERVICE_ADAPTER_PROPERTY = "serviceAdapter"; /** * * {@inheritDoc} */ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Set<RemotingDestinationMetadata> remoteBeans = findRemotingDestinations(beanFactory); if (remoteBeans.size() > 0) { Assert.isInstanceOf(BeanDefinitionRegistry.class, beanFactory, "In order for services to be exported via the @RemotingDestination annotation, the current BeanFactory must be a BeanDefinitionRegistry."); } BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; for (RemotingDestinationMetadata remotingDestinationConfig : remoteBeans) { BeanDefinitionBuilder exporterBuilder = BeanDefinitionBuilder.rootBeanDefinition(RemotingDestinationExporter.class); exporterBuilder.getRawBeanDefinition().setRole(BeanDefinition.ROLE_INFRASTRUCTURE); RemotingDestination remotingDestination = remotingDestinationConfig.getRemotingDestination(); String messageBrokerId = StringUtils.hasText(remotingDestination.messageBroker()) ? remotingDestination.messageBroker() : BeanIds.MESSAGE_BROKER; String destinationId = StringUtils.hasText(remotingDestination.value()) ? remotingDestination.value() : remotingDestinationConfig.getBeanName(); String[] channels = null; for (String channelValue : remotingDestination.channels()) { channelValue = beanFactory.resolveEmbeddedValue(channelValue); String[] parsedChannels = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(channelValue)); channels = StringUtils.mergeStringArrays(channels, parsedChannels); } exporterBuilder.addPropertyReference(MESSAGE_BROKER_PROPERTY, messageBrokerId); exporterBuilder.addPropertyValue(SERVICE_PROPERTY, remotingDestinationConfig.getBeanName()); exporterBuilder.addDependsOn(remotingDestinationConfig.getBeanName()); exporterBuilder.addPropertyValue(DESTINATION_ID_PROPERTY, destinationId); exporterBuilder.addPropertyValue(CHANNELS_PROPERTY, channels); exporterBuilder.addPropertyValue(INCLUDE_METHODS_PROPERTY, remotingDestinationConfig.getIncludeMethods()); exporterBuilder.addPropertyValue(EXCLUDE_METHODS_PROPERTY, remotingDestinationConfig.getExcludeMethods()); exporterBuilder.addPropertyValue(SERVICE_ADAPTER_PROPERTY, remotingDestination.serviceAdapter()); BeanDefinitionReaderUtils.registerWithGeneratedName(exporterBuilder.getBeanDefinition(), registry); } } /** * Helper that searches the BeanFactory for beans annotated with @RemotingDestination, being careful not to force * eager creation of the beans if it can be avoided. * * @param beanFactory the BeanFactory to search * @return a set of collected RemotingDestinationMetadata */ private Set<RemotingDestinationMetadata> findRemotingDestinations(ConfigurableListableBeanFactory beanFactory) { Set<RemotingDestinationMetadata> remotingDestinations = new HashSet<RemotingDestinationMetadata>(); Set<String> beanNames = new HashSet<String>(); beanNames.addAll(Arrays.asList(beanFactory.getBeanDefinitionNames())); if (beanFactory.getParentBeanFactory() instanceof ListableBeanFactory) { beanNames.addAll(Arrays.asList(((ListableBeanFactory)beanFactory.getParentBeanFactory()).getBeanDefinitionNames())); } for (String beanName : beanNames) { if (beanName.startsWith("scopedTarget.")) { continue; } RemotingDestination remotingDestination = null; BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); if (bd.isAbstract() || bd.isLazyInit()) { continue; } if (bd instanceof AbstractBeanDefinition) { AbstractBeanDefinition abd = (AbstractBeanDefinition) bd; if (abd.hasBeanClass()) { Class<?> beanClass = abd.getBeanClass(); remotingDestination = AnnotationUtils.findAnnotation(beanClass, RemotingDestination.class); if (remotingDestination != null) { remotingDestinations.add(new RemotingDestinationMetadata(remotingDestination, beanName, beanClass)); continue; } } } Class<?> handlerType = beanFactory.getType(beanName); if (handlerType != null) { remotingDestination = AnnotationUtils.findAnnotation(handlerType, RemotingDestination.class); } else { if (log.isDebugEnabled()) { log.debug("Could not get type of bean '" + beanName + "' from bean factory."); } } if (remotingDestination != null) { remotingDestinations.add(new RemotingDestinationMetadata(remotingDestination, beanName, handlerType)); } } return remotingDestinations; } }