INT-4530: Remove Meters

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

- implement `DisposableBean` in all meter-aware components
- remove meters in `destroy()`
- manually destroy annotation components created by `registerSingleton()`
  - changing these beans to register as bean definitions caused some subtle
    initialization side-effects; given we are post-RC, I felt it was too late
    to make such a large change
  - also CPXAC is not a `GenericApplicationContext`
  - hence a bean is registered to destroy them manually

* Rebase; Polishing - PR Comments
This commit is contained in:
Gary Russell
2018-10-19 15:00:04 -04:00
committed by Artem Bilan
parent e3ce37ca36
commit 23ebe56838
25 changed files with 325 additions and 43 deletions

View File

@@ -121,7 +121,7 @@ subprojects { subproject ->
kryoShadedVersion = '3.0.3'
lettuceVersion = '5.1.0.RELEASE'
log4jVersion = '2.11.1'
micrometerVersion = '1.0.6'
micrometerVersion = '1.1.0-SNAPSHOT'
mockitoVersion = '2.22.0'
mysqlVersion = '8.0.11'
pahoMqttClientVersion = '1.2.0'

View File

@@ -352,4 +352,12 @@ public class PollableAmqpChannel extends AbstractAmqpChannel
return this.executorInterceptorsSize > 0;
}
@Override
public void destroy() throws Exception {
super.destroy();
if (this.receiveCounter != null) {
this.receiveCounter.remove();
}
}
}

View File

@@ -177,7 +177,7 @@ public class AmqpOutboundEndpointTests {
.setHeader(AmqpHeaders.CONTENT_TYPE, "application/json")
.build();
this.ctRequestChannel.send(message);
org.springframework.amqp.core.Message m = template.receive();
org.springframework.amqp.core.Message m = receive(template);
assertNotNull(m);
assertEquals("\"hello\"", new String(m.getBody(), "UTF-8"));
assertEquals("application/json", m.getMessageProperties().getContentType());
@@ -186,7 +186,7 @@ public class AmqpOutboundEndpointTests {
message = MessageBuilder.withPayload("hello")
.build();
this.ctRequestChannel.send(message);
m = template.receive();
m = receive(template);
assertNotNull(m);
assertEquals("hello", new String(m.getBody(), "UTF-8"));
assertEquals("text/plain", m.getMessageProperties().getContentType());
@@ -195,4 +195,15 @@ public class AmqpOutboundEndpointTests {
}
}
private org.springframework.amqp.core.Message receive(RabbitTemplate template) throws Exception {
int n = 0;
org.springframework.amqp.core.Message message = template.receive();
while (message == null && n++ < 100) {
Thread.sleep(100);
message = template.receive();
}
assertNotNull(message);
return message;
}
}

View File

@@ -22,6 +22,8 @@ import java.util.Comparator;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.logging.Log;
@@ -39,6 +41,7 @@ import org.springframework.integration.support.management.MessageChannelMetrics;
import org.springframework.integration.support.management.MetricsContext;
import org.springframework.integration.support.management.Statistics;
import org.springframework.integration.support.management.TrackableComponent;
import org.springframework.integration.support.management.metrics.MeterFacade;
import org.springframework.integration.support.management.metrics.MetricsCaptor;
import org.springframework.integration.support.management.metrics.SampleFacade;
import org.springframework.integration.support.management.metrics.TimerFacade;
@@ -73,6 +76,8 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
private final ManagementOverrides managementOverrides = new ManagementOverrides();
protected final Set<MeterFacade> meters = ConcurrentHashMap.newKeySet(); // NOSONAR
private volatile boolean shouldTrack = false;
private volatile Class<?>[] datatypes = new Class<?>[0];
@@ -493,13 +498,15 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
}
private TimerFacade buildSendTimer(boolean success, String exception) {
return this.metricsCaptor.timerBuilder(SEND_TIMER_NAME)
TimerFacade timer = this.metricsCaptor.timerBuilder(SEND_TIMER_NAME)
.tag("type", "channel")
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("result", success ? "success" : "failure")
.tag("exception", exception)
.description("Send processing time")
.build();
this.meters.add(timer);
return timer;
}
private Message<?> convertPayloadIfNecessary(Message<?> message) {
@@ -544,6 +551,10 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
*/
protected abstract boolean doSend(Message<?> message, long timeout);
@Override
public void destroy() throws Exception {
this.meters.forEach(t -> t.remove());
}
/**
* A convenience wrapper class for the list of ChannelInterceptors.

View File

@@ -139,14 +139,15 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel
catch (RuntimeException e) {
if (countsEnabled && !counted) {
if (getMetricsCaptor() != null) {
getMetricsCaptor().counterBuilder(RECEIVE_COUNTER_NAME)
CounterFacade counter = getMetricsCaptor().counterBuilder(RECEIVE_COUNTER_NAME)
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("type", "channel")
.tag("result", "failure")
.tag("exception", e.getClass().getSimpleName())
.description("Messages received")
.build()
.increment();
.build();
this.meters.add(counter);
counter.increment();
}
getMetrics().afterError();
}
@@ -231,4 +232,12 @@ public abstract class AbstractPollableChannel extends AbstractMessageChannel
@Nullable
protected abstract Message<?> doReceive(long timeout);
@Override
public void destroy() throws Exception {
super.destroy();
if (this.receiveCounter != null) {
this.receiveCounter.remove();
}
}
}

View File

@@ -273,4 +273,11 @@ public class NullChannel implements PollableChannel, MessageChannelMetrics,
return (this.beanName != null) ? this.beanName : super.toString();
}
@Override
public void destroy() throws Exception {
if (this.successTimer != null) {
this.successTimer.remove();
}
}
}

View File

@@ -34,6 +34,7 @@ import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
@@ -108,6 +109,8 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
protected final Class<T> annotationType;
protected final Disposables disposables; // NOSONAR
@SuppressWarnings("unchecked")
public AbstractMethodAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "'beanFactory' must not be null");
@@ -123,6 +126,14 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
this.channelResolver = new BeanFactoryChannelResolver(beanFactory);
this.annotationType = (Class<T>) GenericTypeResolver.resolveTypeArgument(this.getClass(),
MethodAnnotationPostProcessor.class);
Disposables disposables = null;
try {
disposables = beanFactory.getBean(Disposables.class);
}
catch (Exception e) {
// NOSONAR - only for test cases
}
this.disposables = disposables;
}
@@ -177,6 +188,9 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
}
this.beanFactory.registerSingleton(handlerBeanName, handler);
handler = (MessageHandler) this.beanFactory.initializeBean(handler, handlerBeanName);
if (handler instanceof DisposableBean && this.disposables != null) {
this.disposables.add((DisposableBean) handler);
}
}
if (AnnotatedElementUtils.isAnnotated(method, IdempotentReceiver.class.getName())
@@ -297,6 +311,9 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
inputChannel = new DirectChannel();
this.beanFactory.registerSingleton(inputChannelName, inputChannel);
inputChannel = (MessageChannel) this.beanFactory.initializeBean(inputChannel, inputChannelName);
if (this.disposables != null) {
this.disposables.add((DisposableBean) inputChannel);
}
}
else {
throw e;

View File

@@ -0,0 +1,54 @@
/*
* Copyright 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.
* 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.annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.DisposableBean;
/**
* A container for a collection of {@link DisposableBean} it is, itself a
* {@link DisposableBean} and will dispose of its contained beans when it is destroyed.
* Intended for any {@link DisposableBean} that is registered as a singleton in which
* case, the container does not automatically dispose of them.
*
* @author Gary Russell
* @since 5.1
*
*/
class Disposables implements DisposableBean {
private final List<DisposableBean> disposables = new ArrayList<>();
public void add(DisposableBean... disposables) {
this.disposables.addAll(Arrays.asList(disposables));
}
@Override
public void destroy() throws Exception {
this.disposables.forEach(d -> {
try {
d.destroy();
}
catch (Exception e) {
// NOSONAR
}
});
}
}

View File

@@ -131,6 +131,9 @@ public class InboundChannelAdapterAnnotationPostProcessor extends
this.beanFactory.registerSingleton(messageSourceBeanName, methodInvokingMessageSource);
messageSource = (MessageSource<?>) this.beanFactory
.initializeBean(methodInvokingMessageSource, messageSourceBeanName);
if (this.disposables != null) {
this.disposables.add(methodInvokingMessageSource);
}
}
return messageSource;
}

View File

@@ -38,6 +38,8 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.annotation.Aggregator;
@@ -50,6 +52,7 @@ import org.springframework.integration.annotation.Router;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.annotation.Splitter;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.util.MessagingAnnotationUtils;
import org.springframework.util.Assert;
@@ -94,6 +97,10 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
@Override
public void afterPropertiesSet() {
Assert.notNull(this.beanFactory, "BeanFactory must not be null");
((BeanDefinitionRegistry) this.beanFactory).registerBeanDefinition(
IntegrationContextUtils.DISPOSABLES_BEAN_NAME,
BeanDefinitionBuilder.genericBeanDefinition(Disposables.class, () -> new Disposables())
.getRawBeanDefinition());
this.postProcessors.put(Filter.class, new FilterAnnotationPostProcessor(this.beanFactory));
this.postProcessors.put(Router.class, new RouterAnnotationPostProcessor(this.beanFactory));
this.postProcessors.put(Transformer.class, new TransformerAnnotationPostProcessor(this.beanFactory));

View File

@@ -99,6 +99,8 @@ public abstract class IntegrationContextUtils {
public static final String LIST_ARGUMENT_RESOLVERS_BEAN_NAME = "integrationListArgumentResolvers";
public static final String DISPOSABLES_BEAN_NAME = "integrationDisposableAutoCreatedBeans";
/**
* @param beanFactory BeanFactory for lookup, must not be null.
* @return The {@link MetadataStore} bean whose name is "metadataStore".

View File

@@ -233,4 +233,11 @@ public abstract class AbstractMessageSource<T> extends AbstractExpressionEvaluat
*/
protected abstract Object doReceive();
@Override
public void destroy() throws Exception {
if (this.receiveCounter != null) {
this.receiveCounter.remove();
}
}
}

View File

@@ -16,6 +16,9 @@
package org.springframework.integration.handler;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.reactivestreams.Subscription;
import org.springframework.core.Ordered;
@@ -59,6 +62,8 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport
private final ManagementOverrides managementOverrides = new ManagementOverrides();
private final Set<TimerFacade> timers = ConcurrentHashMap.newKeySet();
private volatile boolean shouldTrack = false;
private volatile int order = Ordered.LOWEST_PRECEDENCE;
@@ -90,7 +95,6 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport
this.managementOverrides.loggingConfigured = true;
}
@SuppressWarnings("unchecked")
@Override
public void registerMetricsCaptor(MetricsCaptor metricsCaptor) {
this.metricsCaptor = metricsCaptor;
@@ -185,13 +189,15 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport
}
private TimerFacade buildSendTimer(boolean success, String exception) {
return this.metricsCaptor.timerBuilder(SEND_TIMER_NAME)
TimerFacade timer = this.metricsCaptor.timerBuilder(SEND_TIMER_NAME)
.tag("type", "handler")
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("result", success ? "success" : "failure")
.tag("exception", exception)
.description("Send processing time")
.build();
this.timers.add(timer);
return timer;
}
@Override
@@ -330,4 +336,9 @@ public abstract class AbstractMessageHandler extends IntegrationObjectSupport
return this.managedType;
}
@Override
public void destroy() throws Exception {
this.timers.forEach(t -> t.remove());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 the original author or authors.
* Copyright 2015-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.support.management;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.integration.support.management.metrics.MetricsCaptor;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
@@ -27,7 +28,7 @@ import org.springframework.jmx.export.annotation.ManagedOperation;
* @since 4.2
*
*/
public interface IntegrationManagement {
public interface IntegrationManagement extends DisposableBean {
String METER_PREFIX = "spring.integration.";

View File

@@ -187,4 +187,9 @@ public class LifecycleMessageHandlerMetrics implements MessageHandlerMetrics, Li
return this.delegate.getOverrides();
}
@Override
public void destroy() throws Exception {
this.delegate.destroy();
}
}

View File

@@ -126,4 +126,9 @@ public class LifecycleMessageSourceMetrics implements MessageSourceMetrics, Life
return this.delegate.getOverrides();
}
@Override
public void destroy() throws Exception {
this.delegate.destroy();
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.integration.support.management;
import org.springframework.integration.support.management.metrics.CounterFacade;
import org.springframework.jmx.export.annotation.ManagedMetric;
import org.springframework.jmx.support.MetricType;
@@ -50,16 +49,4 @@ public interface MessageSourceMetrics extends IntegrationManagement {
String getManagedType();
/**
* Set a micrometer counter to count messages produced.
* @param counter the counter.
* @since 5.0.2
* @deprecated in favor of built-in counter registration via {@code MeterRegistry} callbacks.
* Will be remove in the next release.
*/
@Deprecated
default void setCounter(CounterFacade counter) {
// no op
}
}

View File

@@ -21,7 +21,7 @@ package org.springframework.integration.support.management.metrics;
* @since 5.0.4
*
*/
public interface CounterFacade {
public interface CounterFacade extends MeterFacade {
void increment();

View File

@@ -21,6 +21,6 @@ package org.springframework.integration.support.management.metrics;
* @since 5.0.4
*
*/
public interface GaugeFacade {
public interface GaugeFacade extends MeterFacade {
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 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.
* 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.support.management.metrics;
import org.springframework.lang.Nullable;
/**
* Facade for Meters.
*
* @author Gary Russell
* @since 5.1
*
*/
public interface MeterFacade {
/**
* Remove this meter facade.
* @param <T> the type of meter removed.
* @return the facade that was removed, or null.
*/
@Nullable
default <T extends MeterFacade> T remove() {
return null;
}
}

View File

@@ -58,6 +58,17 @@ public interface MetricsCaptor {
*/
SampleFacade start();
/**
* Remove a meter facade.
* @param facade the facade to remove.
* @return the removed facade, or null.
* @since 5.1
*/
@Nullable
default MeterFacade removeMeter(MeterFacade facade) {
return null;
}
/**
* A builder for a timer.
*/

View File

@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
* @since 5.0.4
*
*/
public interface TimerFacade {
public interface TimerFacade extends MeterFacade {
void record(long time, TimeUnit unit);

View File

@@ -24,6 +24,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.integration.support.management.metrics.CounterFacade;
import org.springframework.integration.support.management.metrics.GaugeFacade;
import org.springframework.integration.support.management.metrics.MeterFacade;
import org.springframework.integration.support.management.metrics.MetricsCaptor;
import org.springframework.integration.support.management.metrics.SampleFacade;
import org.springframework.integration.support.management.metrics.TimerFacade;
@@ -31,6 +32,7 @@ import org.springframework.util.Assert;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
@@ -73,6 +75,11 @@ public class MicrometerMetricsCaptor implements MetricsCaptor {
return new MicroSample(Timer.start(this.meterRegistry));
}
@Override
public MeterFacade removeMeter(MeterFacade facade) {
return facade.remove();
}
/**
* Add a MicrometerMetricsCaptor to the context if there's a MeterRegistry.
* @param applicationContext the application context.
@@ -135,19 +142,51 @@ public class MicrometerMetricsCaptor implements MetricsCaptor {
@Override
public MicroTimer build() {
return new MicroTimer(this.builder.register(this.meterRegistry));
return new MicroTimer(this.builder.register(this.meterRegistry), this.meterRegistry);
}
}
private static class MicroTimer implements TimerFacade {
private static abstract class AbstractMeter implements MeterFacade {
protected final MeterRegistry meterRegistry; // NOSONAR
protected AbstractMeter(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
/**
* Get the meter.
* @return the meter.
*/
protected abstract Meter getMeter();
@SuppressWarnings("unchecked")
@Override
public <T extends MeterFacade> T remove() {
if (this.meterRegistry.remove(getMeter()) != null) {
return (T) this;
}
else {
return null;
}
}
}
private static class MicroTimer extends AbstractMeter implements TimerFacade {
private final Timer timer;
MicroTimer(Timer timer) {
MicroTimer(Timer timer, MeterRegistry meterRegistry) {
super(meterRegistry);
this.timer = timer;
}
@Override
protected Meter getMeter() {
return this.timer;
}
@Override
public void record(long time, TimeUnit unit) {
this.timer.record(time, unit);
@@ -180,19 +219,25 @@ public class MicrometerMetricsCaptor implements MetricsCaptor {
@Override
public CounterFacade build() {
return new MicroCounter(this.builder.register(this.meterRegistry));
return new MicroCounter(this.builder.register(this.meterRegistry), this.meterRegistry);
}
}
private static class MicroCounter implements CounterFacade {
private static class MicroCounter extends AbstractMeter implements CounterFacade {
private final Counter counter;
MicroCounter(Counter counter) {
MicroCounter(Counter counter, MeterRegistry meterRegistry) {
super(meterRegistry);
this.counter = counter;
}
@Override
protected Meter getMeter() {
return this.counter;
}
@Override
public void increment() {
this.counter.increment();
@@ -225,20 +270,25 @@ public class MicrometerMetricsCaptor implements MetricsCaptor {
@Override
public GaugeFacade build() {
return new MicroGauge(this.builder.register(this.meterRegistry));
return new MicroGauge(this.builder.register(this.meterRegistry), this.meterRegistry);
}
}
private static class MicroGauge implements GaugeFacade {
private static class MicroGauge extends AbstractMeter implements GaugeFacade {
@SuppressWarnings("unused")
private final Gauge gauge;
MicroGauge(Gauge gauge) {
MicroGauge(Gauge gauge, MeterRegistry meterRegistry) {
super(meterRegistry);
this.gauge = gauge;
}
@Override
protected Meter getMeter() {
return this.gauge;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-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.
@@ -25,12 +25,14 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.withSettings;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.messaging.MessageChannel;
@@ -46,7 +48,8 @@ public class MessagingAnnotationPostProcessorChannelCreationTests {
@Test
public void testAutoCreateChannel() {
ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class,
withSettings().extraInterfaces(BeanDefinitionRegistry.class));
given(beanFactory.getBean("channel", MessageChannel.class)).willThrow(NoSuchBeanDefinitionException.class);
willAnswer(invocation -> invocation.getArgument(0))
.given(beanFactory).initializeBean(any(DirectChannel.class), eq("channel"));
@@ -61,7 +64,8 @@ public class MessagingAnnotationPostProcessorChannelCreationTests {
@Test
public void testDontCreateChannelWhenChannelHasBadDefinition() {
ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class,
withSettings().extraInterfaces(BeanDefinitionRegistry.class));
given(beanFactory.getBean("channel", MessageChannel.class)).willThrow(BeanCreationException.class);
willAnswer(invocation -> invocation.getArgument(0))
.given(beanFactory).initializeBean(any(DirectChannel.class), eq("channel"));

View File

@@ -45,10 +45,10 @@ import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.search.MeterNotFoundException;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
/**
@@ -58,7 +58,6 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
*
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class MicrometerMetricsTests {
@Autowired
@@ -86,7 +85,7 @@ public class MicrometerMetricsTests {
private NullChannel nullChannel;
@Test
public void testSend() {
public void testSend() throws Exception {
GenericMessage<String> message = new GenericMessage<>("foo");
this.channel.send(message);
try {
@@ -170,6 +169,39 @@ public class MicrometerMetricsTests {
.tag("name", "newChannel")
.tag("result", "success")
.timer().count()).isEqualTo(1);
// Test meter removal
registry.get("spring.integration.send")
.tag("name", "newChannel")
.tag("result", "success")
.timer();
newChannel.destroy();
try {
registry.get("spring.integration.send")
.tag("name", "newChannel")
.tag("result", "success")
.timer();
fail("Expected MeterNotFoundException");
}
catch (MeterNotFoundException e) {
assertThat(e).hasMessageContaining("A meter with name 'spring.integration.send' was found");
assertThat(e).hasMessageContaining("No meters have a tag 'name' with value 'newChannel'");
}
this.context.close();
try {
registry.get("spring.integration.send").timers();
fail("Expected MeterNotFoundException");
}
catch (MeterNotFoundException e) {
assertThat(e).hasMessageContaining("No meter with name 'spring.integration.send' was found");
}
try {
registry.get("spring.integration.receive").counters();
fail("Expected MeterNotFoundException");
}
catch (MeterNotFoundException e) {
assertThat(e).hasMessageContaining("No meter with name 'spring.integration.receive' was found");
}
}
@Configuration