diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Aggregator.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Aggregator.java index 5fa70a2660..1210052b27 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Aggregator.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Aggregator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -34,6 +34,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants; * @author Marius Bogoevici * @author Oleg Zhurakousky * @author Artem Bilan + * @author Chris Bono */ @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @@ -105,10 +106,9 @@ public @interface Aggregator { /** * @return the {@link Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). * Mutually exclusive with {@link #reactive()}. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); /** * @return the {@link Reactive} marker for a consumer endpoint. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/BridgeFrom.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/BridgeFrom.java index ba72f9df35..325f05abc0 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/BridgeFrom.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/BridgeFrom.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 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. @@ -38,6 +38,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants; * is used as the {@code outputChannel} of the {@link org.springframework.integration.handler.BridgeHandler}. * * @author Artem Bilan + * @author Chris Bono * * @since 4.0 */ @@ -70,10 +71,9 @@ public @interface BridgeFrom { /** * @return the {@link Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). * Mutually exclusive with {@link #reactive()}. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); /** * @return the {@link Reactive} marker for a consumer endpoint. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/BridgeTo.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/BridgeTo.java index b1d9687059..836d47eb07 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/BridgeTo.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/BridgeTo.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 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. @@ -43,6 +43,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants; * If no output channel is provided and no reply-channel exists, an exception is thrown. * * @author Artem Bilan + * @author Chris Bono * * @since 4.0 */ @@ -77,10 +78,9 @@ public @interface BridgeTo { /** * @return the {@link Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). * Mutually exclusive with {@link #reactive()}. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); /** * @return the {@link Reactive} marker for a consumer endpoint. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Filter.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Filter.java index de1e0c0e81..6d3696bd82 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Filter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Filter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -40,6 +40,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants; * @author Mark Fisher * @author Gary Russell * @author Artem Bilan + * @author Chris Bono * * @since 2.0 */ @@ -128,10 +129,9 @@ public @interface Filter { /** * @return the {@link Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). * Mutually exclusive with {@link #reactive()}. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); /** * @return the {@link Reactive} marker for a consumer endpoint. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/InboundChannelAdapter.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/InboundChannelAdapter.java index d214ff03dc..6c8e2d0d9b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/InboundChannelAdapter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/InboundChannelAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2022 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. @@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; +import org.springframework.messaging.handler.annotation.ValueConstants; /** * Indicates that a method is capable of producing a {@link org.springframework.messaging.Message} @@ -45,6 +46,7 @@ import org.springframework.core.annotation.AliasFor; * * @author Artem Bilan * @author Gary Russell + * @author Chris Bono * * @since 4.0 */ @@ -86,10 +88,8 @@ public @interface InboundChannelAdapter { /** * @return the {@link org.springframework.integration.annotation.Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). - * Only one {@link org.springframework.integration.annotation.Poller} element is allowed. * NOTE: a {@link Poller} here has {@link Poller#maxMessagesPerPoll()} set to 1 by default. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Router.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Router.java index e77cbd85a6..db55137564 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Router.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Router.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -45,6 +45,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants; * * @author Mark Fisher * @author Artem Bilan + * @author Chris Bono */ @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @@ -151,10 +152,9 @@ public @interface Router { /** * @return the {@link Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). * Mutually exclusive with {@link #reactive()}. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); /** * @return the {@link Reactive} marker for a consumer endpoint. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/ServiceActivator.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/ServiceActivator.java index 86be941aab..7d64d8e25e 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/ServiceActivator.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/ServiceActivator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -43,6 +43,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants; * @author Gary Russell * @author Artem Bilan * @author Yilin Wei + * @author Chris Bono */ @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @@ -121,11 +122,9 @@ public @interface ServiceActivator { /** * @return the {@link Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). - * Only one {@link Poller} element is allowed. * Mutually exclusive with {@link #reactive()}. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); /** * @return the {@link Reactive} marker for a consumer endpoint. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Splitter.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Splitter.java index 78cfcbf7f8..09f1be27c8 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Splitter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Splitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -43,6 +43,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants; * @author Mark Fisher * @author Gary Russell * @author Artem Bilan + * @author Chris Bono */ @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @@ -116,10 +117,9 @@ public @interface Splitter { /** * @return the {@link Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). * Mutually exclusive with {@link #reactive()}. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); /** * @return the {@link Reactive} marker for a consumer endpoint. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Transformer.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Transformer.java index a93595ed0a..2277febb07 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Transformer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Transformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -31,6 +31,7 @@ import org.springframework.messaging.handler.annotation.ValueConstants; * @author Mark Fisher * @author Gary Russell * @author Artem Bilan + * @author Chris Bono */ @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @@ -92,10 +93,9 @@ public @interface Transformer { /** * @return the {@link Poller} options for a polled endpoint * ({@link org.springframework.integration.scheduling.PollerMetadata}). - * This attribute is an {@code array} just to allow an empty default (no poller). * Mutually exclusive with {@link #reactive()}. */ - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); /** * @return the {@link Reactive} marker for a consumer endpoint. diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/AbstractMethodAnnotationPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/AbstractMethodAnnotationPostProcessor.java index 45195b9be8..a1bdb36693 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/AbstractMethodAnnotationPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/AbstractMethodAnnotationPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -84,7 +84,6 @@ import org.springframework.messaging.PollableChannel; import org.springframework.messaging.SubscribableChannel; import org.springframework.messaging.core.DestinationResolutionException; import org.springframework.messaging.core.DestinationResolver; -import org.springframework.messaging.handler.annotation.ValueConstants; import org.springframework.scheduling.Trigger; import org.springframework.scheduling.support.CronTrigger; import org.springframework.scheduling.support.PeriodicTrigger; @@ -103,6 +102,7 @@ import reactor.core.publisher.Flux; * @author Mark Fisher * @author Artem Bilan * @author Gary Russell + * @author Chris Bono */ public abstract class AbstractMethodAnnotationPostProcessor implements MethodAnnotationPostProcessor { @@ -363,24 +363,24 @@ public abstract class AbstractMethodAnnotationPostProcessor annotations) { - Poller[] pollers = MessagingAnnotationUtils.resolveAttribute(annotations, "poller", Poller[].class); - Reactive reactive = MessagingAnnotationUtils.resolveAttribute(annotations, "reactive", Reactive.class); - boolean reactiveProvided = reactive != null && !ValueConstants.DEFAULT_NONE.equals(reactive.value()); + Poller poller = MessagingAnnotationUtils.resolveAttribute(annotations, "poller", Poller.class); - Assert.state(!reactiveProvided || ObjectUtils.isEmpty(pollers), + Reactive reactive = MessagingAnnotationUtils.resolveAttribute(annotations, "reactive", Reactive.class); + + Assert.state(reactive == null || poller == null, "The 'poller' and 'reactive' are mutually exclusive."); - if (inputChannel instanceof Publisher || handler instanceof ReactiveMessageHandlerAdapter || reactiveProvided) { - return reactiveStreamsConsumer(inputChannel, handler, reactiveProvided ? reactive : null); + if (inputChannel instanceof Publisher || handler instanceof ReactiveMessageHandlerAdapter || reactive != null) { + return reactiveStreamsConsumer(inputChannel, handler, reactive); } else if (inputChannel instanceof SubscribableChannel) { - Assert.state(ObjectUtils.isEmpty(pollers), () -> + Assert.state(poller == null, () -> "A '@Poller' should not be specified for Annotation-based " + "endpoint, since '" + inputChannel + "' is a SubscribableChannel (not pollable)."); return new EventDrivenConsumer((SubscribableChannel) inputChannel, handler); } else if (inputChannel instanceof PollableChannel) { - return pollingConsumer(inputChannel, handler, pollers); + return pollingConsumer(inputChannel, handler, poller); } else { throw new IllegalArgumentException("Unsupported 'inputChannel' type: '" @@ -414,19 +414,15 @@ public abstract class AbstractMethodAnnotationPostProcessor 0); + /** + * Determine if the value of a named attribute from an annotation instance contains an actual value. + * @param annotationValue the value of the annotation attribute + * @return {@code false} when {@code annotationValue} is null, an empty string, an empty array, or any annotation + * whose 'value' field is set to {@link ValueConstants#DEFAULT_NONE} - {@code true} otherwise + */ + public static boolean hasValue(Object annotationValue) { + if (annotationValue == null) { + return false; + } + // Empty array + if (ObjectUtils.isArray(annotationValue) && ObjectUtils.isEmpty(annotationValue)) { + return false; + } + // Empty string + if ((annotationValue instanceof String) && ObjectUtils.isEmpty(annotationValue)) { + return false; + } + // Annotation with 'value' set to special 'none' string + if ((annotationValue instanceof Annotation) && + ValueConstants.DEFAULT_NONE.equals(AnnotationUtils.getValue((Annotation) annotationValue))) { + return false; + } + return true; } public static Method findAnnotatedMethod(Object target, final Class annotationType) { diff --git a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java index 5e984a4514..4d24c8aa0e 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2021 the original author or authors. + * Copyright 2014-2022 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. @@ -127,6 +127,7 @@ import org.springframework.messaging.MessageDeliveryException; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.messaging.handler.annotation.ValueConstants; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.messaging.support.GenericMessage; import org.springframework.scheduling.Trigger; @@ -148,6 +149,7 @@ import reactor.core.publisher.Mono; * @author Artem Bilan * @author Gary Russell * @author Michael Wiles + * @author Chris Bono * * @since 4.0 */ @@ -1506,7 +1508,7 @@ public class EnableIntegrationTests { String phase() default ""; - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); } @@ -1525,7 +1527,7 @@ public class EnableIntegrationTests { String phase() default ""; - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); } @@ -1631,7 +1633,7 @@ public class EnableIntegrationTests { String phase() default ""; - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); } @@ -1664,7 +1666,7 @@ public class EnableIntegrationTests { String phase() default ""; - Poller[] poller() default { }; + Poller poller() default @Poller(ValueConstants.DEFAULT_NONE); } diff --git a/spring-integration-core/src/test/kotlin/org/springframework/integration/function/FunctionsTests.kt b/spring-integration-core/src/test/kotlin/org/springframework/integration/function/FunctionsTests.kt index 323b047e40..82fad5b495 100644 --- a/spring-integration-core/src/test/kotlin/org/springframework/integration/function/FunctionsTests.kt +++ b/spring-integration-core/src/test/kotlin/org/springframework/integration/function/FunctionsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018-2021 the original author or authors. + * Copyright 2018-2022 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. @@ -56,6 +56,7 @@ import java.util.stream.Collectors /** * @author Artem Bilan + * @author Chris Bono * * @since 5.1 */ @@ -171,7 +172,7 @@ class FunctionsTests { @Bean @InboundChannelAdapter(value = "counterChannel", autoStartup = "false", - poller = [Poller(fixedRate = "10", maxMessagesPerPoll = "1")]) + poller = Poller(fixedRate = "10", maxMessagesPerPoll = "1")) @EndpointId("kotlinSupplierChannelAdapter") fun kotlinSupplier(): () -> String { return { "baz" } diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index b030a4cd67..4b6e550af8 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -26,6 +26,9 @@ See <<./graphql.adoc#graphql,GraphQL Support>> for more information. [[x6.0-general]] === General Changes +The messaging annotations don't require a poller attribute as an array of @Poller any more. +See <<./configuration.adoc#configuration-using-poller-annotation,Using the @Poller Annotation>> for more information. + [[x6.0-http]] === HTTP Changes @@ -36,4 +39,4 @@ See <<./http.adoc#http,HTTP Support>> for more information. === RMI Removal The `spring-integration-rmi` module has been removed altogether after being deprecated in previous versions. -There is no replacement: it is recommended to migrate to more secure network and application protocols, such as WebSockets, RSockets, gRPC or REST. \ No newline at end of file +There is no replacement: it is recommended to migrate to more secure network and application protocols, such as WebSockets, RSockets, gRPC or REST.