INT-4487: Allow reconfiguring ARPMH.adviceChain

JIRA: https://jira.spring.io/browse/INT-4487

Currently the `AbstractReplyProducingMessageHandler` applies advices
only in the `afterPropertiesSet()` phase.
When we use bean references for `MessageHandler`s in Java DSL, the
`.advice()` provided in the flow definition is not applied to the
`AbstractReplyProducingMessageHandler`.

* For consistency with the endpoint configured by the DSL definition,
override and reconfigure advices in the `AbstractReplyProducingMessageHandler`
bean reference during DSL parsing and processing.

Fix `TwitterSearchOutboundGatewayParserTests` for the proper assert
This commit is contained in:
Artem Bilan
2018-06-21 13:05:15 -04:00
committed by Gary Russell
parent 0735ddb0ed
commit e6a35c4a60
7 changed files with 141 additions and 43 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -75,26 +75,28 @@ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedCo
private final ConversionService defaultConversionService = DefaultConversionService.getSharedInstance();
private volatile DestinationResolver<MessageChannel> channelResolver;
private DestinationResolver<MessageChannel> channelResolver;
private volatile String beanName;
private String beanName;
private volatile String componentName;
private String componentName;
private volatile BeanFactory beanFactory;
private BeanFactory beanFactory;
private volatile TaskScheduler taskScheduler;
private TaskScheduler taskScheduler;
private volatile Properties integrationProperties = IntegrationProperties.defaults();
private Properties integrationProperties = IntegrationProperties.defaults();
private volatile ConversionService conversionService;
private ConversionService conversionService;
private volatile ApplicationContext applicationContext;
private ApplicationContext applicationContext;
private volatile MessageBuilderFactory messageBuilderFactory;
private MessageBuilderFactory messageBuilderFactory;
private Expression expression;
private boolean initialized;
@Override
public final void setBeanName(String beanName) {
this.beanName = beanName;
@@ -181,6 +183,8 @@ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedCo
}
throw new BeanInitializationException("failed to initialize", e);
}
this.initialized = true;
}
/**
@@ -190,6 +194,14 @@ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedCo
protected void onInit() throws Exception {
}
/**
* Return the status of this component if it has been initialized already.
* @return the flag if this component has been initialized already.
*/
protected boolean isInitialized() {
return this.initialized;
}
protected BeanFactory getBeanFactory() {
return this.beanFactory;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 the original author or authors.
* Copyright 2016-2018 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.
@@ -96,7 +96,9 @@ public class BarrierSpec extends ConsumerEndpointSpec<BarrierSpec, BarrierMessag
@Override
public Tuple2<ConsumerEndpointFactoryBean, BarrierMessageHandler> doGet() {
this.handler = new BarrierMessageHandler(this.timeout, this.outputProcessor, this.correlationStrategy);
this.handler.setAdviceChain(this.adviceChain);
if (!this.adviceChain.isEmpty()) {
this.handler.setAdviceChain(this.adviceChain);
}
this.handler.setRequiresReply(this.requiresReply);
this.handler.setSendTimeout(this.sendTimeout);
this.handler.setAsync(this.async);

View File

@@ -267,7 +267,7 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
@Override
protected Tuple2<ConsumerEndpointFactoryBean, H> doGet() {
this.endpointFactoryBean.setAdviceChain(this.adviceChain);
if (this.handler instanceof AbstractReplyProducingMessageHandler) {
if (this.handler instanceof AbstractReplyProducingMessageHandler && !this.adviceChain.isEmpty()) {
((AbstractReplyProducingMessageHandler) this.handler).setAdviceChain(this.adviceChain);
}
this.endpointFactoryBean.setHandler(this.handler);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -16,6 +16,7 @@
package org.springframework.integration.handler;
import java.util.LinkedList;
import java.util.List;
import org.aopalliance.aop.Advice;
@@ -26,7 +27,6 @@ import org.springframework.integration.handler.advice.HandleMessageAdvice;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
/**
* Base class for MessageHandlers that are capable of producing replies.
@@ -41,14 +41,14 @@ import org.springframework.util.CollectionUtils;
public abstract class AbstractReplyProducingMessageHandler extends AbstractMessageProducingHandler
implements BeanClassLoaderAware {
private final List<Advice> adviceChain = new LinkedList<>();
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private boolean requiresReply = false;
private volatile RequestHandler advisedRequestHandler;
private volatile List<Advice> adviceChain;
private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private volatile boolean requiresReply = false;
/**
* Flag whether a reply is required. If true an incoming message MUST result in a reply message being sent.
* If false an incoming message MAY result in a reply message being sent. Default is false.
@@ -62,13 +62,23 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
return this.requiresReply;
}
/**
* Configure a list of {@link Advice}s to proxy a {@link #handleRequestMessage(Message)} method.
* @param adviceChain the list of {@link Advice}s to use.
*/
public void setAdviceChain(List<Advice> adviceChain) {
Assert.notNull(adviceChain, "adviceChain cannot be null");
this.adviceChain = adviceChain;
Assert.notEmpty(adviceChain, "adviceChain cannot be empty");
synchronized (this.adviceChain) {
this.adviceChain.clear();
this.adviceChain.addAll(adviceChain);
if (isInitialized()) {
initAdvisedRequestHandlerIfAny();
}
}
}
protected boolean hasAdviceChain() {
return this.adviceChain != null && this.adviceChain.size() > 0;
return this.adviceChain.size() > 0;
}
@Override
@@ -80,7 +90,12 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
@Override
protected final void onInit() throws Exception {
super.onInit();
if (!CollectionUtils.isEmpty(this.adviceChain)) {
initAdvisedRequestHandlerIfAny();
doInit();
}
private void initAdvisedRequestHandlerIfAny() {
if (!this.adviceChain.isEmpty()) {
ProxyFactory proxyFactory = new ProxyFactory(new AdvisedRequestHandler());
boolean advised = false;
for (Advice advice : this.adviceChain) {
@@ -93,7 +108,6 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
this.advisedRequestHandler = (RequestHandler) proxyFactory.getProxy(this.beanClassLoader);
}
}
doInit();
}
protected void doInit() {
@@ -154,9 +168,6 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
Object handleRequestMessage(Message<?> requestMessage);
@Override
String toString();
/**
* Utility method, intended for use in message handler advice classes to get
* information about the advised object. For example: