diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/Default.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Default.java new file mode 100644 index 0000000000..7596d2eb82 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/Default.java @@ -0,0 +1,48 @@ +/* + * Copyright 2017 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.annotation; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the class member has some default meaning. + *
+ * The target parsing logic may vary. + * One of the use-cases is service with several methods which are + * selected for invocation at runtime by some condition. + * The method with this {@link Default} annotation may mean + * a fallback option when no one other method meets condition. + * + * @author Artem Bilan + * + * @since 5.0 + */ +@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Default { + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java index 679b579a3f..186281ac5e 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2017 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. @@ -47,8 +47,10 @@ import org.springframework.integration.config.annotation.MessagingAnnotationPost import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.context.IntegrationProperties; import org.springframework.integration.support.DefaultMessageBuilderFactory; +import org.springframework.integration.support.converter.ConfigurableCompositeMessageConverter; import org.springframework.integration.support.converter.DefaultDatatypeChannelMessageConverter; import org.springframework.integration.support.utils.IntegrationUtils; +import org.springframework.messaging.converter.CompositeMessageConverter; import org.springframework.util.ClassUtils; /** @@ -86,19 +88,20 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { - this.registerImplicitChannelCreator(registry); - this.registerIntegrationConfigurationBeanFactoryPostProcessor(registry); - this.registerIntegrationEvaluationContext(registry); - this.registerIntegrationProperties(registry); - this.registerHeaderChannelRegistry(registry); - this.registerGlobalChannelInterceptorProcessor(registry); - this.registerBuiltInBeans(registry); - this.registerDefaultConfiguringBeanFactoryPostProcessor(registry); - this.registerDefaultDatatypeChannelMessageConverter(registry); + registerImplicitChannelCreator(registry); + registerIntegrationConfigurationBeanFactoryPostProcessor(registry); + registerIntegrationEvaluationContext(registry); + registerIntegrationProperties(registry); + registerHeaderChannelRegistry(registry); + registerGlobalChannelInterceptorProcessor(registry); + registerBuiltInBeans(registry); + registerDefaultConfiguringBeanFactoryPostProcessor(registry); + registerDefaultDatatypeChannelMessageConverter(registry); + registerArgumentResolverMessageConverter(registry); if (importingClassMetadata != null) { - this.registerMessagingAnnotationPostProcessors(importingClassMetadata, registry); + registerMessagingAnnotationPostProcessors(importingClassMetadata, registry); } - this.registerMessageBuilderFactory(registry); + registerMessageBuilderFactory(registry); IntegrationConfigUtils.registerRoleControllerDefinitionIfNecessary(registry); } @@ -405,6 +408,21 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean } + /** + * Register the default {@link CompositeMessageConverter} for argument resolvers + * during handler method invocation. + * @param registry the registry. + */ + private void registerArgumentResolverMessageConverter(BeanDefinitionRegistry registry) { + if (!registry.containsBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)) { + BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder + .genericBeanDefinition(ConfigurableCompositeMessageConverter.class); + registry.registerBeanDefinition(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME, + postProcessorBuilder.getBeanDefinition()); + } + } + + private void registerMessageBuilderFactory(BeanDefinitionRegistry registry) { boolean alreadyRegistered = false; if (registry instanceof ListableBeanFactory) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java index a283bcebdd..2a9db2d3b9 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java @@ -81,8 +81,11 @@ public abstract class IntegrationContextUtils { public static final String INTEGRATION_GRAPH_SERVER_BEAN_NAME = "integrationGraphServer"; + public static final String SPEL_PROPERTY_ACCESSOR_REGISTRAR_BEAN_NAME = "spelPropertyAccessorRegistrar"; + public static final String ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME = "integrationArgumentResolverMessageConverter"; + /** * @param beanFactory BeanFactory for lookup, must not be null. * @return The {@link MetadataStore} bean whose name is "metadataStore". diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/converter/ConfigurableCompositeMessageConverter.java b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/ConfigurableCompositeMessageConverter.java new file mode 100644 index 0000000000..92036f09d8 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/converter/ConfigurableCompositeMessageConverter.java @@ -0,0 +1,94 @@ +/* + * Copyright 2017 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.converter; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.integration.support.json.JacksonJsonUtils; +import org.springframework.messaging.converter.ByteArrayMessageConverter; +import org.springframework.messaging.converter.CompositeMessageConverter; +import org.springframework.messaging.converter.GenericMessageConverter; +import org.springframework.messaging.converter.MappingJackson2MessageConverter; +import org.springframework.messaging.converter.MessageConverter; + +/** + * A {@link CompositeMessageConverter} extension with some default {@link MessageConverter}s + * which can be overridden with the given converters + * or added in the end of target {@code converters} collection. + *
+ * The default converts are (declared exactly in this order): + *
+ * Delegates to super when payload is {@code byte[]} or {@code String}.
+ * Performs {@link Object#toString()} in other cases.
+ *
+ * @author Marius Bogoevici
+ * @author Artem Bilan
+ *
+ * @since 5.0
+ */
+public class ObjectStringMessageConverter extends StringMessageConverter {
+
+ @Override
+ protected Object convertFromInternal(Message> message, Class> targetClass, Object conversionHint) {
+ Object payload = message.getPayload();
+ if (payload instanceof String || payload instanceof byte[]) {
+ return super.convertFromInternal(message, targetClass, conversionHint);
+ }
+ else {
+ return payload.toString();
+ }
+ }
+
+}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java
index 2bf31e0485..51e1f6c134 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java
@@ -57,8 +57,10 @@ import org.springframework.expression.Expression;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.integration.annotation.Default;
import org.springframework.integration.annotation.Payloads;
import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.handler.support.CollectionArgumentResolver;
import org.springframework.integration.handler.support.MapArgumentResolver;
import org.springframework.integration.handler.support.PayloadExpressionArgumentResolver;
@@ -67,6 +69,7 @@ import org.springframework.integration.support.MutableMessage;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.converter.MessageConversionException;
+import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
@@ -158,6 +161,7 @@ public class MessagingMethodInvokerHelper