Fix memory leak in MHistorConfig & MBeanExpHelper (#2531)
* Fix memory leak in MHistorConfig & MBeanExpHelper The `MessageHistoryConfigurer` is a `BeanPostProcessor` which keeps a store of the `TrackableComponent`. Something similar we have with the `MBeanExporterHelper` and its local stores populated by the `postProcessBeforeInitialization()` * Implement `DestructionAwareBeanPostProcessor` in the `MessageHistoryConfigurer` and `MBeanExporterHelper` to remove destroyed beans from their caches **Cherry-pick to 5.0.x** * * Fix concurrency and checkstyle * Apply Java 8 style for collections iterations
This commit is contained in:
committed by
Gary Russell
parent
adbbca13fb
commit
55a43d37c8
@@ -29,7 +29,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.support.management.IntegrationManagedResource;
|
||||
@@ -50,13 +50,13 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
@ManagedResource
|
||||
@IntegrationManagedResource
|
||||
public class MessageHistoryConfigurer implements SmartLifecycle, BeanFactoryAware, BeanPostProcessor {
|
||||
public class MessageHistoryConfigurer implements SmartLifecycle, BeanFactoryAware, DestructionAwareBeanPostProcessor {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final Set<TrackableComponent> currentlyTrackedComponents = ConcurrentHashMap.newKeySet();
|
||||
|
||||
private String[] componentNamePatterns = new String[] { "*" };
|
||||
private String[] componentNamePatterns = new String[]{ "*" };
|
||||
|
||||
private boolean componentNamePatternsExplicitlySet;
|
||||
|
||||
@@ -98,7 +98,7 @@ public class MessageHistoryConfigurer implements SmartLifecycle, BeanFactoryAwar
|
||||
*/
|
||||
@ManagedAttribute(description = "comma-delimited list of patterns; must invoke stop() before changing.")
|
||||
public void setComponentNamePatternsString(String componentNamePatterns) {
|
||||
this.setComponentNamePatterns(StringUtils.delimitedListToStringArray(componentNamePatterns, ",", " "));
|
||||
setComponentNamePatterns(StringUtils.delimitedListToStringArray(componentNamePatterns, ",", " "));
|
||||
}
|
||||
|
||||
@ManagedAttribute
|
||||
@@ -160,6 +160,16 @@ public class MessageHistoryConfigurer implements SmartLifecycle, BeanFactoryAwar
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresDestruction(Object bean) {
|
||||
return bean instanceof TrackableComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
|
||||
this.currentlyTrackedComponents.remove(bean);
|
||||
}
|
||||
|
||||
/*
|
||||
* SmartLifecycle implementation
|
||||
*/
|
||||
|
||||
@@ -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,16 +16,17 @@
|
||||
|
||||
package org.springframework.integration.jmx.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.integration.monitor.IntegrationMBeanExporter;
|
||||
@@ -40,28 +41,29 @@ import org.springframework.jmx.export.MBeanExporter;
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
*/
|
||||
class MBeanExporterHelper implements BeanPostProcessor, Ordered {
|
||||
class MBeanExporterHelper implements DestructionAwareBeanPostProcessor, Ordered {
|
||||
|
||||
private final List<MBeanExporter> mBeanExportersForExcludes = new ArrayList<MBeanExporter>();
|
||||
private final Queue<MBeanExporter> mBeanExportersForExcludes = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private final Set<String> siBeanNames = new HashSet<String>();
|
||||
private final Set<String> siBeanNames = ConcurrentHashMap.newKeySet();
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if ("$autoCreateChannelCandidates".equals(beanName)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<String> autoCreateChannelCandidatesNames = (Collection<String>) new DirectFieldAccessor(bean)
|
||||
.getPropertyValue("channelNames");
|
||||
Collection<String> autoCreateChannelCandidatesNames =
|
||||
(Collection<String>) new DirectFieldAccessor(bean).getPropertyValue("channelNames");
|
||||
this.siBeanNames.addAll(autoCreateChannelCandidatesNames);
|
||||
if (!this.mBeanExportersForExcludes.isEmpty()) {
|
||||
for (String autoCreateChannelCandidatesName : autoCreateChannelCandidatesNames) {
|
||||
for (MBeanExporter mBeanExporter : this.mBeanExportersForExcludes) {
|
||||
mBeanExporter.addExcludedBean(autoCreateChannelCandidatesName);
|
||||
}
|
||||
}
|
||||
autoCreateChannelCandidatesNames
|
||||
.stream().
|
||||
<Consumer<? super MBeanExporter>>map(candidateName ->
|
||||
mBeanExporter -> mBeanExporter.addExcludedBean(candidateName))
|
||||
.forEach(this.mBeanExportersForExcludes::forEach);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,25 +74,37 @@ class MBeanExporterHelper implements BeanPostProcessor, Ordered {
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (AnnotatedElementUtils.isAnnotated(AopUtils.getTargetClass(bean),
|
||||
IntegrationManagedResource.class.getName())) {
|
||||
|
||||
this.siBeanNames.add(beanName);
|
||||
if (!this.mBeanExportersForExcludes.isEmpty()) {
|
||||
for (MBeanExporter mBeanExporter : this.mBeanExportersForExcludes) {
|
||||
mBeanExporter.addExcludedBean(beanName);
|
||||
}
|
||||
}
|
||||
this.mBeanExportersForExcludes.forEach(mBeanExporter -> mBeanExporter.addExcludedBean(beanName));
|
||||
}
|
||||
|
||||
if (bean instanceof MBeanExporter && !(bean instanceof IntegrationMBeanExporter)) {
|
||||
MBeanExporter mBeanExporter = (MBeanExporter) bean;
|
||||
this.mBeanExportersForExcludes.add(mBeanExporter);
|
||||
for (String siBeanName : this.siBeanNames) {
|
||||
mBeanExporter.addExcludedBean(siBeanName);
|
||||
}
|
||||
this.siBeanNames.forEach(mBeanExporter::addExcludedBean);
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresDestruction(Object bean) {
|
||||
return (bean instanceof MBeanExporter && !(bean instanceof IntegrationMBeanExporter)) ||
|
||||
AnnotatedElementUtils.isAnnotated(AopUtils.getTargetClass(bean),
|
||||
IntegrationManagedResource.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof MBeanExporter) {
|
||||
this.mBeanExportersForExcludes.remove(bean);
|
||||
}
|
||||
else {
|
||||
this.siBeanNames.remove(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
|
||||
Reference in New Issue
Block a user