INT-4509: Fix memory leak for static beans stores
JIRA: https://jira.spring.io/browse/INT-4509 To avoid a re-usage of the `AbstractReplyProducingMessageHandler`, the `IntegrationFlowDefinition` and `AbstractStandardMessageHandlerFactoryBean` have a `static Set<>` to store already used producers and check it for newly provided. These stores are not cleaned when beans are destroyed leading to memory leaks * Implements a `DisposableBean` for the `AbstractStandardMessageHandlerFactoryBean` to remove its `replyHandler` from the `referencedReplyProducers` on `destroy()` * Introduce a `IntegrationFlowDefinition.ReplyProducerCleaner` - an `DestructionAwareBeanPostProcessor` to clean up removing `MessageProducer` from the `IntegrationFlowDefinition.REFERENCED_REPLY_PRODUCERS` **Cherry-pick to 5.0.x**
This commit is contained in:
committed by
Gary Russell
parent
20789ac118
commit
da977941f8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -21,6 +21,7 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
@@ -41,21 +42,23 @@ import org.springframework.util.StringUtils;
|
||||
* @author David Liu
|
||||
*/
|
||||
public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
extends AbstractSimpleMessageHandlerFactoryBean<MessageHandler> {
|
||||
extends AbstractSimpleMessageHandlerFactoryBean<MessageHandler> implements DisposableBean {
|
||||
|
||||
private static final ExpressionParser expressionParser = new SpelExpressionParser();
|
||||
|
||||
private static final Set<MessageHandler> referencedReplyProducers = new HashSet<>();
|
||||
|
||||
private volatile Boolean requiresReply;
|
||||
private Boolean requiresReply;
|
||||
|
||||
private volatile Object targetObject;
|
||||
private Object targetObject;
|
||||
|
||||
private volatile String targetMethodName;
|
||||
private String targetMethodName;
|
||||
|
||||
private volatile Expression expression;
|
||||
private Expression expression;
|
||||
|
||||
private volatile Long sendTimeout;
|
||||
private Long sendTimeout;
|
||||
|
||||
private MessageHandler replyHandler;
|
||||
|
||||
/**
|
||||
* Set the target POJO for the message handler.
|
||||
@@ -101,6 +104,13 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
return this.sendTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (this.replyHandler != null) {
|
||||
referencedReplyProducers.remove(this.replyHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler() {
|
||||
MessageHandler handler;
|
||||
@@ -158,6 +168,7 @@ public abstract class AbstractStandardMessageHandlerFactoryBean
|
||||
"An AbstractMessageProducingMessageHandler may only be referenced once (" +
|
||||
replyHandler.getComponentName() + ") - use scope=\"prototype\"");
|
||||
referencedReplyProducers.add(replyHandler);
|
||||
this.replyHandler = replyHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,9 @@ import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.aggregator.AggregatingMessageHandler;
|
||||
@@ -120,9 +122,9 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
|
||||
|
||||
protected final Map<Object, String> integrationComponents = new LinkedHashMap<>();
|
||||
|
||||
protected MessageChannel currentMessageChannel;
|
||||
private MessageChannel currentMessageChannel;
|
||||
|
||||
protected Object currentComponent;
|
||||
private Object currentComponent;
|
||||
|
||||
private StandardIntegrationFlow integrationFlow;
|
||||
|
||||
@@ -479,7 +481,9 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
|
||||
* @return the current {@link IntegrationFlowDefinition}.
|
||||
* @see ExpressionEvaluatingTransformer
|
||||
*/
|
||||
public B transform(String expression, Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
|
||||
public B transform(String expression,
|
||||
Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
|
||||
|
||||
Assert.hasText(expression, "'expression' must not be empty");
|
||||
return transform(new ExpressionEvaluatingTransformer(PARSER.parseExpression(expression)),
|
||||
endpointConfigurer);
|
||||
@@ -2771,4 +2775,21 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
|
||||
REFERENCED_REPLY_PRODUCERS.add(replyHandler);
|
||||
}
|
||||
|
||||
public static final class ReplyProducerCleaner implements DestructionAwareBeanPostProcessor {
|
||||
|
||||
private ReplyProducerCleaner() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresDestruction(Object bean) {
|
||||
return IntegrationFlowDefinition.REFERENCED_REPLY_PRODUCERS.contains(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
|
||||
IntegrationFlowDefinition.REFERENCED_REPLY_PRODUCERS.remove(bean);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.integration.config.IntegrationConfigurationInitializer;
|
||||
import org.springframework.integration.dsl.IntegrationComponentSpec;
|
||||
import org.springframework.integration.dsl.IntegrationFlowDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -46,6 +47,9 @@ public class DslIntegrationConfigurationInitializer implements IntegrationConfig
|
||||
private static final String INTEGRATION_FLOW_CONTEXT_BEAN_NAME =
|
||||
Introspector.decapitalize(IntegrationFlowContext.class.getName());
|
||||
|
||||
private static final String INTEGRATION_FLOW_REPLY_PRODUCER_CLEANER_BEAN_NAME =
|
||||
Introspector.decapitalize(IntegrationFlowDefinition.ReplyProducerCleaner.class.getName());
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(BeanDefinitionRegistry.class, configurableListableBeanFactory,
|
||||
@@ -59,6 +63,8 @@ public class DslIntegrationConfigurationInitializer implements IntegrationConfig
|
||||
new RootBeanDefinition(IntegrationFlowBeanPostProcessor.class));
|
||||
registry.registerBeanDefinition(INTEGRATION_FLOW_CONTEXT_BEAN_NAME,
|
||||
new RootBeanDefinition(StandardIntegrationFlowContext.class));
|
||||
registry.registerBeanDefinition(INTEGRATION_FLOW_REPLY_PRODUCER_CLEANER_BEAN_NAME,
|
||||
new RootBeanDefinition(IntegrationFlowDefinition.ReplyProducerCleaner.class));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user