diff --git a/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java index 00f771f260..4436936fc8 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java @@ -19,6 +19,7 @@ package org.springframework.integration.gateway; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -32,6 +33,7 @@ import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; @@ -103,7 +105,7 @@ import reactor.core.publisher.Mono; */ public class GatewayProxyFactoryBean extends AbstractEndpoint implements TrackableComponent, FactoryBean, MethodInterceptor, BeanClassLoaderAware, - IntegrationManagement { + IntegrationManagement { private final Object initializationMonitor = new Object(); @@ -864,7 +866,13 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint timeouts(requestTimeout, replyTimeout, messageMapper, gateway); - gateway.setBeanName(getComponentName()); + String gatewayMethodBeanName = + getComponentName() + '#' + method.getName() + + '(' + Arrays.stream(method.getParameterTypes()) + .map(Class::getSimpleName) + .collect(Collectors.joining(", ")) + ')'; + + gateway.setBeanName(gatewayMethodBeanName); gateway.setBeanFactory(getBeanFactory()); gateway.setShouldTrack(this.shouldTrack); gateway.registerMetricsCaptor(this.metricsCaptor); @@ -1059,8 +1067,8 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint public IntegrationPatternType getIntegrationPatternType() { return this.pollable ? IntegrationPatternType.outbound_channel_adapter : this.isVoidReturn - ? IntegrationPatternType.inbound_channel_adapter - : IntegrationPatternType.inbound_gateway; + ? IntegrationPatternType.inbound_channel_adapter + : IntegrationPatternType.inbound_gateway; } @Nullable diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dsl/gateway/GatewayDslTests.java b/spring-integration-core/src/test/java/org/springframework/integration/dsl/gateway/GatewayDslTests.java index 4ae4919732..e6adb7b943 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dsl/gateway/GatewayDslTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/dsl/gateway/GatewayDslTests.java @@ -153,12 +153,19 @@ public class GatewayDslTests { @Autowired private RoutingGateway routingGateway; + @Autowired + @Qualifier("&routingGateway") + private GatewayProxyFactoryBean routingGatewayProxy; + @Test void testRoutingGateway() { String result = this.routingGateway.route1("test1"); assertThat(result).isEqualTo("route1"); result = this.routingGateway.route2("test2"); assertThat(result).isEqualTo("route2"); + MessagingGatewaySupport gatewayMethod = this.routingGatewayProxy.getGateways().values().iterator().next(); + assertThat(gatewayMethod.getComponentName()) + .isEqualTo("routingGateway#route1(Object)"); } @Configuration @@ -211,9 +218,9 @@ public class GatewayDslTests { } @Bean - public IntegrationFlow routingGateway() { + public IntegrationFlow routingGatewayFlow() { return IntegrationFlows.from(RoutingGateway.class, - (gateway) -> gateway.header("gatewayMethod", MethodArgsHolder::getMethod)) + (gateway) -> gateway.beanName("routingGateway").header("gatewayMethod", MethodArgsHolder::getMethod)) .route(Message.class, (message) -> message.getHeaders().get("gatewayMethod", Method.class).getName(), (router) -> router diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java b/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java index ae31a64c20..2692fa2c99 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/ServiceActivatorDefaultFrameworkMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -18,13 +18,11 @@ package org.springframework.integration.handler; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.fail; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; @@ -44,7 +42,7 @@ import org.springframework.messaging.MessageHandlingException; import org.springframework.messaging.MessagingException; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.support.ErrorMessage; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.SettableListenableFuture; @@ -57,7 +55,7 @@ import org.springframework.util.concurrent.SettableListenableFuture; * * @since 2.0.1 */ -@RunWith(SpringRunner.class) +@SpringJUnitConfig public class ServiceActivatorDefaultFrameworkMethodTests { @Autowired @@ -106,7 +104,8 @@ public class ServiceActivatorDefaultFrameworkMethodTests { this.gatewayTestInputChannel.send(message); Message reply = replyChannel.receive(0); assertThat(reply.getHeaders().get("history").toString()) - .isEqualTo("gatewayTestInputChannel,gatewayTestService,gateway,requestChannel,replyChannel"); + .isEqualTo("gatewayTestInputChannel,gatewayTestService," + + "gateway#exchange(Message),requestChannel,replyChannel"); Message message2 = MessageBuilder.withPayload("foo").setReplyChannel(replyChannel).build(); @@ -197,19 +196,11 @@ public class ServiceActivatorDefaultFrameworkMethodTests { @Test public void testFailOnDoubleReference() { - try { - new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-fail-context.xml", - this.getClass()).close(); - fail("Expected exception due to 2 endpoints referencing the same bean"); - } - catch (Exception e) { - assertThat(e).isInstanceOf(BeanCreationException.class); - assertThat(e.getCause()).isInstanceOf(BeanCreationException.class); - assertThat(e.getCause().getCause()).isInstanceOf(IllegalArgumentException.class); - assertThat(e.getCause().getCause().getMessage()) - .contains("An AbstractMessageProducingMessageHandler may only be referenced once"); - } - + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(() -> + new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-fail-context.xml", getClass())) + .withMessageContaining("An AbstractMessageProducingMessageHandler may only be referenced once") + .withRootCauseExactlyInstanceOf(IllegalArgumentException.class); } @Test @@ -228,7 +219,7 @@ public class ServiceActivatorDefaultFrameworkMethodTests { @Test public void testAsyncWithDirectReply() { DirectChannel replyChannel = new DirectChannel(); - final AtomicReference> reply = new AtomicReference>(); + final AtomicReference> reply = new AtomicReference<>(); replyChannel.subscribe(reply::set); Message message = MessageBuilder.withPayload("testing").setReplyChannel(replyChannel).build(); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/history/MessageHistoryIntegrationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/history/MessageHistoryIntegrationTests.java index 8ae38a806e..94a63ab3bc 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/history/MessageHistoryIntegrationTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/history/MessageHistoryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -17,6 +17,7 @@ package org.springframework.integration.history; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.util.Iterator; import java.util.Map; @@ -24,8 +25,8 @@ import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.DirectFieldAccessor; @@ -77,7 +78,7 @@ public class MessageHistoryIntegrationTests { .get(MessageHistory.HEADER_NAME, MessageHistory.class).iterator(); Properties event = historyIterator.next(); - assertThat(event.getProperty(MessageHistory.NAME_PROPERTY)).isEqualTo("sampleGateway"); + assertThat(event.getProperty(MessageHistory.NAME_PROPERTY)).isEqualTo("sampleGateway#echo(String)"); assertThat(event.getProperty(MessageHistory.TYPE_PROPERTY)).isEqualTo("gateway"); event = historyIterator.next(); @@ -208,7 +209,7 @@ public class MessageHistoryIntegrationTests { .get(MessageHistory.HEADER_NAME, MessageHistory.class).iterator(); assertThat(historyIterator.hasNext()).isTrue(); Properties gatewayHistory = historyIterator.next(); - assertThat(gatewayHistory.get("name")).isEqualTo("sampleGateway"); + assertThat(gatewayHistory.get("name")).isEqualTo("sampleGateway#echo(String)"); assertThat(historyIterator.hasNext()).isTrue(); Properties chainHistory = historyIterator.next(); assertThat(chainHistory.get("name")).isEqualTo("sampleChain"); @@ -223,14 +224,16 @@ public class MessageHistoryIntegrationTests { ac.close(); } - @Test(expected = BeanCreationException.class) + @Test public void testMessageHistoryMoreThanOneNamespaceFail() { - new ClassPathXmlApplicationContext("messageHistoryWithHistoryWriterNamespace-fail.xml", - MessageHistoryIntegrationTests.class).close(); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(() -> + new ClassPathXmlApplicationContext("messageHistoryWithHistoryWriterNamespace-fail.xml", + MessageHistoryIntegrationTests.class)); } @Test - @Ignore + @Disabled public void testMessageHistoryWithHistoryPerformance() { ConfigurableApplicationContext acWithHistory = new ClassPathXmlApplicationContext("perfWithMessageHistory.xml", MessageHistoryIntegrationTests.class); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsTests.java b/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsTests.java index 21464bfdb4..7699406dac 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerMetricsTests.java @@ -221,15 +221,23 @@ public class MicrometerMetricsTests { this.gates.oneWay("foo"); this.gates.twoWay("bar"); assertThat(registry.get("spring.integration.send") - .tag("name", "gates") + .tag("name", "gates#oneWay(String)") .tag("result", "success") - .timer().count()).isEqualTo(2); + .timer().count()).isEqualTo(1); + assertThat(registry.get("spring.integration.send") + .tag("name", "gates#twoWay(String)") + .tag("result", "success") + .timer().count()).isEqualTo(1); this.gatesFlow.oneWay("foo"); this.gatesFlow.twoWay("bar"); assertThat(registry.get("spring.integration.send") - .tag("name", "gatesFlow.gateway") + .tag("name", "gatesFlow.gateway#oneWay(String)") .tag("result", "success") - .timer().count()).isEqualTo(2); + .timer().count()).isEqualTo(1); + assertThat(registry.get("spring.integration.send") + .tag("name", "gatesFlow.gateway#twoWay(String)") + .tag("result", "success") + .timer().count()).isEqualTo(1); assertThat(registry.get("spring.integration.send") .tag("name", "customGw") .tag("result", "success") diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java index 9e45c8bffc..1c392d9d0c 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/ServiceActivatorDefaultFrameworkMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -17,10 +17,9 @@ package org.springframework.integration.jmx; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; @@ -36,8 +35,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; /** * See INT-1688 for background. @@ -45,10 +43,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @author Mark Fisher * @author Artem Bilan * @author Gary Russell + * * @since 2.0.1 */ -@ContextConfiguration -@RunWith(SpringJUnit4ClassRunner.class) +@SpringJUnitConfig @DirtiesContext public class ServiceActivatorDefaultFrameworkMethodTests { @@ -86,7 +84,8 @@ public class ServiceActivatorDefaultFrameworkMethodTests { this.gatewayTestInputChannel.send(message); Message reply = replyChannel.receive(0); assertThat(reply.getHeaders().get("history").toString()) - .isEqualTo("gatewayTestInputChannel,gatewayTestService,gateway,requestChannel,bridge,replyChannel"); + .isEqualTo("gatewayTestInputChannel,gatewayTestService," + + "gateway#exchange(Message),requestChannel,bridge,replyChannel"); } @Test @@ -170,19 +169,11 @@ public class ServiceActivatorDefaultFrameworkMethodTests { @Test public void testFailOnDoubleReference() { - try { - new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-fail-context.xml", - this.getClass()).close(); - fail("Expected exception due to 2 endpoints referencing the same bean"); - } - catch (Exception e) { - assertThat(e).isInstanceOf(BeanCreationException.class); - assertThat(e.getCause()).isInstanceOf(BeanCreationException.class); - assertThat(e.getCause().getCause()).isInstanceOf(IllegalArgumentException.class); - assertThat(e.getCause().getCause().getMessage()) - .contains("An AbstractMessageProducingMessageHandler may only be referenced once"); - } - + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(() -> + new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-fail-context.xml", getClass())) + .withMessageContaining("An AbstractMessageProducingMessageHandler may only be referenced once") + .withRootCauseExactlyInstanceOf(IllegalArgumentException.class); } private interface Foo { diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index ca4cd8bb11..b4c057dbfe 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -53,6 +53,10 @@ See <<./zeromq.adoc#zeromq,ZeroMQ Support>> for more information. The one-way messaging gateway (the `void` method return type) now sets a `nullChannel` explicitly into the `replyChannel` header to ignore any possible downstream replies. See <<./gateway.adoc#gateway-default-reply-channel,Setting the Default Reply Channel>> for more information. +Also the gateway method invokers (`GatewayProxyFactoryBean.MethodInvocationGateway`) are now supplied with the managed bean name as a combination of gateway proxy bean name plus method signature. +For example: `sampleGateway#echo(String)`. +This effects message history and metrics exposed for the gateway method calls and also give fine-grained logs during start and close of application context. + The aggregator (and resequencer) can now expire orphaned groups (groups in a persistent store where no new messages arrive after an application restart). See <<./aggregator.adoc#aggregator-expiring-groups, Aggregator Expiring Groups>> for more information.