/* * Copyright 2002-2017 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.integration.config; import java.util.List; import org.aopalliance.aop.Advice; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.NameMatchMethodPointcutAdvisor; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.SmartLifecycle; import org.springframework.integration.channel.FixedSubscriberChannel; import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.endpoint.PollingConsumer; import org.springframework.integration.endpoint.ReactiveStreamsConsumer; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.handler.advice.HandleMessageAdvice; import org.springframework.integration.scheduling.PollerMetadata; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.SubscribableChannel; import org.springframework.messaging.core.BeanFactoryMessageChannelDestinationResolver; import org.springframework.messaging.core.DestinationResolver; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** * @author Mark Fisher * @author Oleg Zhurakousky * @author Josh Long * @author Gary Russell * @author Artem Bilan */ public class ConsumerEndpointFactoryBean implements FactoryBean<AbstractEndpoint>, BeanFactoryAware, BeanNameAware, BeanClassLoaderAware, InitializingBean, SmartLifecycle { private volatile MessageHandler handler; private volatile String beanName; private volatile String inputChannelName; private volatile PollerMetadata pollerMetadata; private volatile boolean autoStartup = true; private volatile int phase = 0; private volatile boolean isPhaseSet; private volatile MessageChannel inputChannel; private volatile ConfigurableBeanFactory beanFactory; private volatile ClassLoader beanClassLoader; private volatile AbstractEndpoint endpoint; private volatile boolean initialized; private final Object initializationMonitor = new Object(); private final Object handlerMonitor = new Object(); private final Log logger = LogFactory.getLog(this.getClass()); private volatile List<Advice> adviceChain; private volatile DestinationResolver<MessageChannel> channelResolver; public void setHandler(MessageHandler handler) { Assert.notNull(handler, "handler must not be null"); synchronized (this.handlerMonitor) { Assert.isNull(this.handler, "handler cannot be overridden"); this.handler = handler; } } public void setInputChannel(MessageChannel inputChannel) { this.inputChannel = inputChannel; } public void setInputChannelName(String inputChannelName) { this.inputChannelName = inputChannelName; } public void setPollerMetadata(PollerMetadata pollerMetadata) { this.pollerMetadata = pollerMetadata; } /** * Specify the {@link DestinationResolver} strategy to use. * The default is a BeanFactoryChannelResolver. * @param channelResolver The channel resolver. * @since 4.1.3 */ public void setChannelResolver(DestinationResolver<MessageChannel> channelResolver) { Assert.notNull(channelResolver, "'channelResolver' must not be null"); this.channelResolver = channelResolver; } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } public void setAutoStartup(boolean autoStartup) { this.autoStartup = autoStartup; } public void setPhase(int phase) { this.phase = phase; this.isPhaseSet = true; } @Override public void setBeanName(String beanName) { this.beanName = beanName; } @Override public void setBeanFactory(BeanFactory beanFactory) { Assert.isInstanceOf(ConfigurableBeanFactory.class, beanFactory, "a ConfigurableBeanFactory is required"); this.beanFactory = (ConfigurableBeanFactory) beanFactory; } public void setAdviceChain(List<Advice> adviceChain) { Assert.notNull(adviceChain, "adviceChain must not be null"); this.adviceChain = adviceChain; } @Override public void afterPropertiesSet() throws Exception { if (this.beanName == null) { this.logger.error("The MessageHandler [" + this.handler + "] will be created without a 'componentName'. " + "Consider specifying the 'beanName' property on this ConsumerEndpointFactoryBean."); } else { try { if (!this.beanName.startsWith("org.springframework")) { MessageHandler targetHandler = this.handler; if (AopUtils.isAopProxy(targetHandler)) { Object target = ((Advised) targetHandler).getTargetSource().getTarget(); if (target instanceof MessageHandler) { targetHandler = (MessageHandler) target; } } if (targetHandler instanceof IntegrationObjectSupport) { ((IntegrationObjectSupport) targetHandler).setComponentName(this.beanName); } } } catch (Exception e) { if (this.logger.isDebugEnabled()) { this.logger.debug("Could not set component name for handler " + this.handler + " for " + this.beanName + " :" + e.getMessage()); } } } if (!CollectionUtils.isEmpty(this.adviceChain)) { /* * ARPMHs advise the handleRequestMessage method internally and already have the advice chain injected. * So we only advise handlers that are not reply-producing. * Or if one (or more) of advices is IdempotentReceiverInterceptor. * If the handler is already advised, * add the configured advices to its chain, otherwise create a proxy. */ Class<?> targetClass = AopUtils.getTargetClass(this.handler); boolean replyMessageHandler = AbstractReplyProducingMessageHandler.class.isAssignableFrom(targetClass); for (Advice advice : this.adviceChain) { if (!replyMessageHandler || advice instanceof HandleMessageAdvice) { NameMatchMethodPointcutAdvisor handlerAdvice = new NameMatchMethodPointcutAdvisor(advice); handlerAdvice.addMethodName("handleMessage"); if (this.handler instanceof Advised) { ((Advised) this.handler).addAdvisor(handlerAdvice); } else { ProxyFactory proxyFactory = new ProxyFactory(this.handler); proxyFactory.addAdvisor(handlerAdvice); this.handler = (MessageHandler) proxyFactory.getProxy(this.beanClassLoader); } } } } if (this.channelResolver == null) { this.channelResolver = new BeanFactoryMessageChannelDestinationResolver(this.beanFactory); } initializeEndpoint(); } @Override public boolean isSingleton() { return true; } @Override public AbstractEndpoint getObject() throws Exception { if (!this.initialized) { this.initializeEndpoint(); } return this.endpoint; } @Override public Class<?> getObjectType() { if (this.endpoint == null) { return AbstractEndpoint.class; } return this.endpoint.getClass(); } @SuppressWarnings("unchecked") private void initializeEndpoint() throws Exception { synchronized (this.initializationMonitor) { if (this.initialized) { return; } MessageChannel channel = null; if (StringUtils.hasText(this.inputChannelName)) { channel = this.channelResolver.resolveDestination(this.inputChannelName); } if (this.inputChannel != null) { channel = this.inputChannel; } Assert.state(channel != null, "one of inputChannelName or inputChannel is required"); if (channel instanceof SubscribableChannel) { Assert.isNull(this.pollerMetadata, "A poller should not be specified for endpoint '" + this.beanName + "', since '" + channel + "' is a SubscribableChannel (not pollable)."); this.endpoint = new EventDrivenConsumer((SubscribableChannel) channel, this.handler); if (this.logger.isWarnEnabled() && !this.autoStartup && channel instanceof FixedSubscriberChannel) { this.logger.warn("'autoStartup=\"false\"' has no effect when using a FixedSubscriberChannel"); } } else if (channel instanceof PollableChannel) { PollingConsumer pollingConsumer = new PollingConsumer((PollableChannel) channel, this.handler); if (this.pollerMetadata == null) { this.pollerMetadata = PollerMetadata.getDefaultPollerMetadata(this.beanFactory); Assert.notNull(this.pollerMetadata, "No poller has been defined for endpoint '" + this.beanName + "', and no default poller is available within the context."); } pollingConsumer.setTaskExecutor(this.pollerMetadata.getTaskExecutor()); pollingConsumer.setTrigger(this.pollerMetadata.getTrigger()); pollingConsumer.setAdviceChain(this.pollerMetadata.getAdviceChain()); pollingConsumer.setMaxMessagesPerPoll(this.pollerMetadata.getMaxMessagesPerPoll()); pollingConsumer.setErrorHandler(this.pollerMetadata.getErrorHandler()); pollingConsumer.setReceiveTimeout(this.pollerMetadata.getReceiveTimeout()); pollingConsumer.setTransactionSynchronizationFactory( this.pollerMetadata.getTransactionSynchronizationFactory()); pollingConsumer.setBeanClassLoader(this.beanClassLoader); pollingConsumer.setBeanFactory(this.beanFactory); this.endpoint = pollingConsumer; } else { this.endpoint = new ReactiveStreamsConsumer(channel, this.handler); } this.endpoint.setBeanName(this.beanName); this.endpoint.setBeanFactory(this.beanFactory); this.endpoint.setAutoStartup(this.autoStartup); int phase = this.phase; if (!this.isPhaseSet && this.endpoint instanceof PollingConsumer) { phase = Integer.MAX_VALUE / 2; } this.endpoint.setPhase(phase); this.endpoint.afterPropertiesSet(); this.initialized = true; } } /* * SmartLifecycle implementation (delegates to the created endpoint) */ @Override public boolean isAutoStartup() { return (this.endpoint == null) || this.endpoint.isAutoStartup(); } @Override public int getPhase() { return (this.endpoint != null) ? this.endpoint.getPhase() : 0; } @Override public boolean isRunning() { return (this.endpoint != null) && this.endpoint.isRunning(); } @Override public void start() { if (this.endpoint != null) { this.endpoint.start(); } } @Override public void stop() { if (this.endpoint != null) { this.endpoint.stop(); } } @Override public void stop(Runnable callback) { if (this.endpoint != null) { this.endpoint.stop(callback); } } }