diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationComponentScanRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationComponentScanRegistrar.java index 31e091e7d2..36d6ea1916 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationComponentScanRegistrar.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationComponentScanRegistrar.java @@ -61,12 +61,14 @@ import org.springframework.util.StringUtils; * * @author Artem Bilan * @author Gary Russell + * * @since 4.0 */ public class IntegrationComponentScanRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { - private final Map componentRegistrars = new HashMap(); + private final Map componentRegistrars = + new HashMap(); private ResourceLoader resourceLoader; @@ -98,14 +100,16 @@ public class IntegrationComponentScanRegistrar implements ImportBeanDefinitionRe basePackages = Collections.singleton(ClassUtils.getPackageName(importingClassMetadata.getClassName())); } - ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false) { + ClassPathScanningCandidateComponentProvider scanner = + new ClassPathScanningCandidateComponentProvider(false, this.environment) { - @Override - protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { - return beanDefinition.getMetadata().isIndependent() - && !beanDefinition.getMetadata().isAnnotation(); - } - }; + @Override + protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { + return beanDefinition.getMetadata().isIndependent() + && !beanDefinition.getMetadata().isAnnotation(); + } + + }; if ((boolean) componentScan.get("useDefaultFilters")) { // NOSONAR - never null for (TypeFilter typeFilter : this.componentRegistrars.keySet()) { diff --git a/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java b/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java index 34bd2fdf59..b795fcfacb 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; @@ -59,6 +60,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.Profile; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.core.task.SimpleAsyncTaskExecutor; @@ -77,6 +79,7 @@ import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.context.IntegrationProperties; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.handler.BridgeHandler; +import org.springframework.integration.handler.DelayHandler; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.lang.Nullable; @@ -93,6 +96,7 @@ import org.springframework.messaging.support.MessageHeaderAccessor; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.concurrent.ListenableFuture; @@ -107,6 +111,7 @@ import org.springframework.util.concurrent.ListenableFutureCallback; @ContextConfiguration(classes = GatewayInterfaceTests.TestConfig.class) @RunWith(SpringJUnit4ClassRunner.class) @DirtiesContext +@ActiveProfiles("gatewayTest") public class GatewayInterfaceTests { private static final String IGNORE_HEADER = "ignoreHeader"; @@ -153,20 +158,23 @@ public class GatewayInterfaceTests { @Autowired private IgnoredHeaderGateway ignoredHeaderGateway; + @Autowired(required = false) + private NotActivatedByProfileGateway notActivatedByProfileGateway; + @Test public void testWithServiceSuperclassAnnotatedMethod() throws Exception { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelFoo", DirectChannel.class); final Method fooMethod = Foo.class.getMethod("foo", String.class); final AtomicBoolean called = new AtomicBoolean(); MessageHandler handler = message -> { - assertThat((String) message.getHeaders().get("name"), equalTo("foo")); - assertThat( - (String) message.getHeaders().get("string"), - equalTo("public abstract void org.springframework.integration.gateway.GatewayInterfaceTests$Foo.foo(java.lang.String)")); - assertThat((Method) message.getHeaders().get("object"), equalTo(fooMethod)); - assertThat((String) message.getPayload(), equalTo("hello")); + assertThat(message.getHeaders().get("name"), equalTo("foo")); + assertThat(message.getHeaders().get("string"), + equalTo("public abstract void org.springframework.integration.gateway." + + "GatewayInterfaceTests$Foo.foo(java.lang.String)")); + assertThat(message.getHeaders().get("object"), equalTo(fooMethod)); + assertThat(message.getPayload(), equalTo("hello")); assertThat(new MessageHeaderAccessor(message).getErrorChannel(), equalTo("errorChannel")); called.set(true); }; @@ -181,18 +189,18 @@ public class GatewayInterfaceTests { @Test public void testWithServiceSuperclassAnnotatedMethodOverridePE() throws Exception { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests2-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests2-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelFoo", DirectChannel.class); final Method fooMethod = Foo.class.getMethod("foo", String.class); final AtomicBoolean called = new AtomicBoolean(); MessageHandler handler = message -> { - assertThat((String) message.getHeaders().get("name"), equalTo("foo")); - assertThat( - (String) message.getHeaders().get("string"), - equalTo("public abstract void org.springframework.integration.gateway.GatewayInterfaceTests$Foo.foo(java.lang.String)")); - assertThat((Method) message.getHeaders().get("object"), equalTo(fooMethod)); - assertThat((String) message.getPayload(), equalTo("foo")); + assertThat(message.getHeaders().get("name"), equalTo("foo")); + assertThat(message.getHeaders().get("string"), + equalTo("public abstract void org.springframework.integration.gateway." + + "GatewayInterfaceTests$Foo.foo(java.lang.String)")); + assertThat(message.getHeaders().get("object"), equalTo(fooMethod)); + assertThat(message.getPayload(), equalTo("foo")); called.set(true); }; channel.subscribe(handler); @@ -204,8 +212,8 @@ public class GatewayInterfaceTests { @Test public void testWithServiceAnnotatedMethod() { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBar", DirectChannel.class); MessageHandler handler = mock(MessageHandler.class); channel.subscribe(handler); @@ -217,15 +225,16 @@ public class GatewayInterfaceTests { @Test public void testWithServiceSuperclassUnAnnotatedMethod() throws Exception { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class); final Method bazMethod = Foo.class.getMethod("baz", String.class); final AtomicBoolean called = new AtomicBoolean(); MessageHandler handler = message -> { assertThat(message.getHeaders().get("name"), equalTo("overrideGlobal")); assertThat(message.getHeaders().get("string"), - equalTo("public abstract void org.springframework.integration.gateway.GatewayInterfaceTests$Foo.baz(java.lang.String)")); + equalTo("public abstract void org.springframework.integration.gateway." + + "GatewayInterfaceTests$Foo.baz(java.lang.String)")); assertThat(message.getHeaders().get("object"), equalTo(bazMethod)); assertThat(message.getPayload(), equalTo("hello")); called.set(true); @@ -239,15 +248,16 @@ public class GatewayInterfaceTests { @Test public void testWithServiceUnAnnotatedMethodGlobalHeaderDoesntOverride() throws Exception { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class); final Method quxMethod = Bar.class.getMethod("qux", String.class, String.class); final AtomicBoolean called = new AtomicBoolean(); MessageHandler handler = message -> { assertThat(message.getHeaders().get("name"), equalTo("arg1")); assertThat(message.getHeaders().get("string"), - equalTo("public abstract void org.springframework.integration.gateway.GatewayInterfaceTests$Bar.qux(java.lang.String,java.lang.String)")); + equalTo("public abstract void org.springframework.integration.gateway." + + "GatewayInterfaceTests$Bar.qux(java.lang.String,java.lang.String)")); assertThat(message.getHeaders().get("object"), equalTo(quxMethod)); assertThat(message.getPayload(), equalTo("hello")); called.set(true); @@ -261,8 +271,8 @@ public class GatewayInterfaceTests { @Test public void testWithServiceCastAsSuperclassAnnotatedMethod() { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelFoo", DirectChannel.class); MessageHandler handler = mock(MessageHandler.class); channel.subscribe(handler); @@ -274,8 +284,8 @@ public class GatewayInterfaceTests { @Test public void testWithServiceCastAsSuperclassUnAnnotatedMethod() { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class); MessageHandler handler = mock(MessageHandler.class); channel.subscribe(handler); @@ -287,8 +297,8 @@ public class GatewayInterfaceTests { @Test public void testWithServiceHashcode() { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class); MessageHandler handler = mock(MessageHandler.class); channel.subscribe(handler); @@ -300,8 +310,8 @@ public class GatewayInterfaceTests { @Test public void testWithServiceToString() { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class); MessageHandler handler = mock(MessageHandler.class); channel.subscribe(handler); @@ -313,13 +323,13 @@ public class GatewayInterfaceTests { @Test public void testWithServiceEquals() throws Exception { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class); MessageHandler handler = mock(MessageHandler.class); channel.subscribe(handler); Bar bar = ac.getBean(Bar.class); - assertTrue(bar.equals(ac.getBean(Bar.class))); + assertSame(bar, ac.getBean(Bar.class)); GatewayProxyFactoryBean fb = new GatewayProxyFactoryBean(Bar.class); DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerSingleton("requestChannelBar", channel); @@ -327,15 +337,15 @@ public class GatewayInterfaceTests { bf.registerSingleton("requestChannelFoo", channel); fb.setBeanFactory(bf); fb.afterPropertiesSet(); - assertFalse(bar.equals(fb.getObject())); + assertNotSame(bar, fb.getObject()); verify(handler, times(0)).handleMessage(Mockito.any(Message.class)); ac.close(); } @Test public void testWithServiceGetClass() { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class); MessageHandler handler = mock(MessageHandler.class); channel.subscribe(handler); @@ -352,12 +362,12 @@ public class GatewayInterfaceTests { @Test public void testWithCustomMapper() { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this - .getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class); final AtomicBoolean called = new AtomicBoolean(); MessageHandler handler = message -> { - assertThat((String) message.getPayload(), equalTo("fizbuz")); + assertThat(message.getPayload(), equalTo("fizbuz")); called.set(true); }; channel.subscribe(handler); @@ -369,8 +379,13 @@ public class GatewayInterfaceTests { @Test public void testLateReply() { - ConfigurableApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", - this.getClass()); + ConfigurableApplicationContext ac = + new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass()); + + DelayHandler delayHandler = ac.getBean(DelayHandler.class); + delayHandler.setMaxAttempts(2); + delayHandler.setRetryDelay(10); + Bar baz = ac.getBean(Bar.class); String reply = baz.lateReply("hello", 1000, 0); assertNull(reply); @@ -448,14 +463,15 @@ public class GatewayInterfaceTests { @SuppressWarnings("rawtypes") public void testAnnotationGatewayProxyFactoryBean() { assertNotNull(this.gatewayByAnnotationGPFB); + assertNull(this.notActivatedByProfileGateway); assertSame(this.exec, this.annotationGatewayProxyFactoryBean.getAsyncExecutor()); - assertEquals(1111L, TestUtils - .getPropertyValue(this.annotationGatewayProxyFactoryBean, "defaultRequestTimeout", Expression.class) - .getValue()); - assertEquals(222L, TestUtils - .getPropertyValue(this.annotationGatewayProxyFactoryBean, "defaultReplyTimeout", Expression.class) - .getValue()); + assertEquals(1111L, + TestUtils.getPropertyValue(this.annotationGatewayProxyFactoryBean, + "defaultRequestTimeout", Expression.class).getValue()); + assertEquals(222L, + TestUtils.getPropertyValue(this.annotationGatewayProxyFactoryBean, + "defaultReplyTimeout", Expression.class).getValue()); Collection messagingGateways = this.annotationGatewayProxyFactoryBean.getGateways().values(); @@ -563,7 +579,7 @@ public class GatewayInterfaceTests { public static class BazMapper implements MethodArgsMessageMapper { @Override - public Message toMessage(MethodArgsHolder object, @Nullable Map headers) throws Exception { + public Message toMessage(MethodArgsHolder object, @Nullable Map headers) { return MessageBuilder.withPayload("fizbuz") .copyHeadersIfAbsent(headers) .build(); @@ -652,6 +668,7 @@ public class GatewayInterfaceTests { @MessagingGateway @TestMessagingGateway + @Profile("gatewayTest") public interface Int2634Gateway { @Gateway(requestChannel = "gatewayChannel", payloadExpression = "#args[0]") @@ -665,6 +682,15 @@ public class GatewayInterfaceTests { } + @MessagingGateway(defaultRequestChannel = "errorChannel") + @TestMessagingGateway + @Profile("notActiveProfile") + public interface NotActivatedByProfileGateway { + + void send(String payload); + + } + @MessagingGateway(asyncExecutor = "exec") @TestMessagingGateway public interface ExecGateway { diff --git a/src/reference/asciidoc/gateway.adoc b/src/reference/asciidoc/gateway.adoc index 702918a2ac..6c805b03aa 100644 --- a/src/reference/asciidoc/gateway.adoc +++ b/src/reference/asciidoc/gateway.adoc @@ -188,7 +188,7 @@ Consider the following example: ---- public String send1(Object thing1, Map thing2); -public String send2(Map thing1, Map thing2);= +public String send2(Map thing1, Map thing2); ---- ==== @@ -313,8 +313,9 @@ The standard `@ComponentScan` infrastructure does not deal with interfaces. Consequently, we introduced the custom `@IntegrationComponentScan` logic to fine the `@MessagingGateway` annotation on the interfaces and register `GatewayProxyFactoryBean` instances for them. See also <>. -NOTE: If you have no XML configuration, the `@EnableIntegration` annotation is required on at least one `@Configuration` -class. +Along with the `@MessagingGateway` annotation you can mark a service interface with the `@Profile` annotation to avoid the bean creation, if such a profile is not active. + +NOTE: If you have no XML configuration, the `@EnableIntegration` annotation is required on at least one `@Configuration` class. See <> for more information. [[gateway-calling-no-argument-methods]] @@ -460,11 +461,9 @@ Similarly, if the error flow on the `error-channel` returns an `ErrorMessage`, i The same applies to any message with a `Throwable` payload. This can be useful in asynchronous situations when when you need to propagate an `Exception` directly to the caller. To do so, you can either return an `Exception` (as the `reply` from some service) or throw it. -Generally, even with an asynchronous flow, the framework takes care of propagating an exception thrown by the -downstream flow back to the gateway. +Generally, even with an asynchronous flow, the framework takes care of propagating an exception thrown by the downstream flow back to the gateway. The https://github.com/spring-projects/spring-integration-samples/tree/master/intermediate/tcp-client-server-multiplex[TCP Client-Server Multiplex] sample demonstrates both techniques to return the exception to the caller. -It emulates a socket IO error to the waiting thread by using an `aggregator` with `group-timeout` (see <>) -and a `MessagingTimeoutException` reply on the discard flow. +It emulates a socket IO error to the waiting thread by using an `aggregator` with `group-timeout` (see <>) and a `MessagingTimeoutException` reply on the discard flow. [[gateway-timeouts]] ==== Gateway Timeouts @@ -642,8 +641,7 @@ In this scenario, the downstream flow is expected to return a `CompletableFuture ====== Usage Scenarios -In the following scenario, the caller thread returns immediately with a `CompletableFuture`, which is completed -when the downstream flow replies to the gateway (with an `Invoice` object). +In the following scenario, the caller thread returns immediately with a `CompletableFuture`, which is completed when the downstream flow replies to the gateway (with an `Invoice` object). ==== [source, java] @@ -657,8 +655,7 @@ CompletableFuture order(Order order); ---- ==== -In the following scenario, the caller thread returns with a `CompletableFuture` when the downstream flow provides -it as the payload of the reply to the gateway. +In the following scenario, the caller thread returns with a `CompletableFuture` when the downstream flow provides it as the payload of the reply to the gateway. Some other process must complete the future when the invoice is ready. ==== @@ -674,8 +671,7 @@ CompletableFuture order(Order order); ---- ==== -In the following scenario, the caller thread returns with a `CompletableFuture` when the downstream flow provides -it as the payload of the reply to the gateway. +In the following scenario, the caller thread returns with a `CompletableFuture` when the downstream flow provides it as the payload of the reply to the gateway. Some other process must complete the future when the invoice is ready. If `DEBUG` logging is enabled, a log entry is emitted, indicating that the async executor cannot be used for this scenario.