/* * Copyright 2016-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.dsl; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.function.Consumer; import java.util.function.Function; import org.springframework.expression.Expression; import org.springframework.integration.config.ConsumerEndpointFactoryBean; import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.expression.FunctionExpression; import org.springframework.integration.handler.BeanNameMessageProcessor; import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor; import org.springframework.integration.handler.MessageProcessor; import org.springframework.integration.support.MapBuilder; import org.springframework.integration.support.StringStringMapBuilder; import org.springframework.integration.transformer.HeaderEnricher; import org.springframework.integration.transformer.MessageTransformingHandler; import org.springframework.integration.transformer.support.AbstractHeaderValueMessageProcessor; import org.springframework.integration.transformer.support.ExpressionEvaluatingHeaderValueMessageProcessor; import org.springframework.integration.transformer.support.HeaderValueMessageProcessor; import org.springframework.integration.transformer.support.StaticHeaderValueMessageProcessor; import org.springframework.messaging.Message; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import reactor.util.function.Tuple2; /** * An {@link IntegrationComponentSpec} for a {@link HeaderEnricher}. * * @author Artem Bilan * @author Gary Russell * * @since 5.0 */ public class HeaderEnricherSpec extends ConsumerEndpointSpec<HeaderEnricherSpec, MessageTransformingHandler> { private final Map<String, HeaderValueMessageProcessor<?>> headerToAdd = new HashMap<>(); private boolean defaultOverwrite = false; private boolean shouldSkipNulls = true; private MessageProcessor<?> messageProcessor; HeaderEnricherSpec() { super(null); } /** * Determine the default action to take when setting individual header specifications * without an explicit 'overwrite' argument. * @param defaultOverwrite the defaultOverwrite. * @return the header enricher spec. * @see HeaderEnricher#setDefaultOverwrite(boolean) */ public HeaderEnricherSpec defaultOverwrite(boolean defaultOverwrite) { this.defaultOverwrite = defaultOverwrite; return _this(); } /** * @param shouldSkipNulls the shouldSkipNulls. * @return the header enricher spec. * @see HeaderEnricher#setShouldSkipNulls(boolean) */ public HeaderEnricherSpec shouldSkipNulls(boolean shouldSkipNulls) { this.shouldSkipNulls = shouldSkipNulls; return _this(); } /** * Configure an optional custom {@link MessageProcessor} for the enricher. The * processor must return a {@link Map} of header names and values. They will be added * to the inbound message headers before evaluating the individual configured header * specifications. * @param messageProcessor the messageProcessor. * @return the header enricher spec. * @see HeaderEnricher#setMessageProcessor(MessageProcessor) */ public HeaderEnricherSpec messageProcessor(MessageProcessor<?> messageProcessor) { this.messageProcessor = messageProcessor; return _this(); } /** * Configure an {@link ExpressionEvaluatingMessageProcessor} that evaluates to a * {@link Map} of additional headers. They will be added to the inbound message * headers before evaluating the individual configured header specifications. * @param expression the expression. * @return the header enricher spec. * @see #messageProcessor(MessageProcessor) */ public HeaderEnricherSpec messageProcessor(String expression) { return messageProcessor(new ExpressionEvaluatingMessageProcessor<>(PARSER.parseExpression(expression))); } /** * Configure an * {@link org.springframework.integration.handler.MethodInvokingMessageProcessor} that * invokes the method on the bean - the method must return a {@link Map} of headers. * They will be added to the inbound message headers before evaluating the individual * configured header specifications. * @param beanName The bean name. * @param methodName The method name. * @return the header enricher spec. * @see #messageProcessor(MessageProcessor) */ public HeaderEnricherSpec messageProcessor(String beanName, String methodName) { return messageProcessor(new BeanNameMessageProcessor<>(beanName, methodName)); } /** * Add header specifications from the {@link MapBuilder}; if a map value is an * {@link Expression}, it will be evaluated at run time when the message headers are * enriched. Otherwise the value is simply added to the headers. Headers derived from * the map will <b>not</b> overwrite existing headers, unless * {@link #defaultOverwrite(boolean)} is true. * @param headers the header map builder. * @return the header enricher spec. */ public HeaderEnricherSpec headers(MapBuilder<?, String, Object> headers) { return headers(headers, null); } /** * Add header specifications from the {@link MapBuilder}; if a map value is an * {@link Expression}, it will be evaluated at run time when the message headers are * enriched. Otherwise the value is simply added to the headers. * @param headers the header map builder. * @param overwrite true to overwrite existing headers. * @return the header enricher spec. */ public HeaderEnricherSpec headers(MapBuilder<?, String, Object> headers, Boolean overwrite) { Assert.notNull(headers, "'headers' must not be null"); return headers(headers.get(), overwrite); } /** * Add header specifications from the {@link Map}; if a map value is an * {@link Expression}, it will be evaluated at run time when the message headers are * enriched. Otherwise the value is simply added to the headers. Headers derived from * the map will <em>not</em> overwrite existing headers, unless * {@link #defaultOverwrite(boolean)} is true. * @param headers The header builder. * @return the header enricher spec. */ public HeaderEnricherSpec headers(Map<String, Object> headers) { return headers(headers, null); } /** * Add header specifications from the {@link Map}; if a map value is an * {@link Expression}, it will be evaluated at run time when the message headers are * enriched. Otherwise the value is simply added to the headers. * @param headers The header builder. * @param overwrite true to overwrite existing headers. * @return the header enricher spec. */ public HeaderEnricherSpec headers(Map<String, Object> headers, Boolean overwrite) { Assert.notNull(headers, "'headers' must not be null"); for (Entry<String, Object> entry : headers.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); if (value instanceof Expression) { AbstractHeaderValueMessageProcessor<Object> processor = new ExpressionEvaluatingHeaderValueMessageProcessor<>((Expression) value, null); processor.setOverwrite(overwrite); header(name, processor); } else { header(name, value, overwrite); } } return this; } /** * Add header specifications from the {@link MapBuilder}; the {@link Map} values must * be String representations of SpEL expressions that will be evaluated at run time * when the message headers are enriched. Headers derived from the map will <b>not</b> * overwrite existing headers, unless {@link #defaultOverwrite(boolean)} is true. * @param headers the header map builder. * @return the header enricher spec. */ public HeaderEnricherSpec headerExpressions(MapBuilder<?, String, String> headers) { return headerExpressions(headers, null); } /** * Add header specifications from the {@link MapBuilder}; the {@link Map} values must * be String representations of SpEL expressions that will be evaluated at run time * when the message headers are enriched. * @param headers the header map builder. * @param overwrite true to overwrite existing headers. * @return the header enricher spec. */ public HeaderEnricherSpec headerExpressions(MapBuilder<?, String, String> headers, Boolean overwrite) { Assert.notNull(headers, "'headers' must not be null"); return headerExpressions(headers.get(), overwrite); } /** * Add header specifications via the consumer callback, which receives a * {@link StringStringMapBuilder}; the {@link Map} values must be String * representations of SpEL expressions that will be evaluated at run time when the * message headers are enriched. Headers derived from the map will <b>not</b> * overwrite existing headers, unless {@link #defaultOverwrite(boolean)} is true. * Usually used with a JDK8 lambda: * <pre class="code"> * {@code * .enrichHeaders(s -> s.headerExpressions(c -> c * .put(MailHeaders.SUBJECT, "payload.subject") * .put(MailHeaders.FROM, "payload.from[0].toString()"))) * } * </pre> * @param configurer the configurer. * @return the header enricher spec. */ public HeaderEnricherSpec headerExpressions(Consumer<StringStringMapBuilder> configurer) { return headerExpressions(configurer, null); } /** * Add header specifications via the consumer callback, which receives a * {@link StringStringMapBuilder}; the {@link Map} values must be String * representations of SpEL expressions that will be evaluated at run time when the * message headers are enriched. Usually used with a JDK8 lambda: * <pre class="code"> * {@code * .enrichHeaders(s -> s.headerExpressions(c -> c * .put(MailHeaders.SUBJECT, "payload.subject") * .put(MailHeaders.FROM, "payload.from[0].toString()"), true)) * } * </pre> * @param configurer the configurer. * @param overwrite true to overwrite existing headers. * @return the header enricher spec. */ public HeaderEnricherSpec headerExpressions(Consumer<StringStringMapBuilder> configurer, Boolean overwrite) { Assert.notNull(configurer, "'configurer' must not be null"); StringStringMapBuilder builder = new StringStringMapBuilder(); configurer.accept(builder); return headerExpressions(builder.get(), overwrite); } /** * Add header specifications; the {@link Map} values must be String representations * of SpEL expressions that will be evaluated at run time when the message headers are * enriched. Headers derived from the map will <b>not</b> overwrite existing headers, * unless {@link #defaultOverwrite(boolean)} is true. * @param headers the headers. * @return the header enricher spec. */ public HeaderEnricherSpec headerExpressions(Map<String, String> headers) { return headerExpressions(headers, null); } /** * Add header specifications; the {@link Map} values must be String representations of * SpEL expressions that will be evaluated at run time when the message headers are * enriched. * @param headers the headers. * @param overwrite true to overwrite existing headers. * @return the header enricher spec. */ public HeaderEnricherSpec headerExpressions(Map<String, String> headers, Boolean overwrite) { Assert.notNull(headers, "'headers' must not be null"); for (Entry<String, String> entry : headers.entrySet()) { AbstractHeaderValueMessageProcessor<Object> processor = new ExpressionEvaluatingHeaderValueMessageProcessor<>(entry.getValue(), null); processor.setOverwrite(overwrite); header(entry.getKey(), processor); } return this; } /** * Add a single header specification. If the header exists, it will <b>not</b> be * overwritten unless {@link #defaultOverwrite(boolean)} is true. * @param name the header name. * @param value the header value (not an {@link Expression}). * @param <V> the value type. * @return the header enricher spec. */ public <V> HeaderEnricherSpec header(String name, V value) { return header(name, value, null); } /** * Add a single header specification. * @param name the header name. * @param value the header value (not an {@link Expression}). * @param overwrite true to overwrite an existing header. * @param <V> the value type. * @return the header enricher spec. */ public <V> HeaderEnricherSpec header(String name, V value, Boolean overwrite) { AbstractHeaderValueMessageProcessor<V> headerValueMessageProcessor = new StaticHeaderValueMessageProcessor<>(value); headerValueMessageProcessor.setOverwrite(overwrite); return header(name, headerValueMessageProcessor); } /** * Add a single header specification where the value is a String representation of a * SpEL {@link Expression}. If the header exists, it will <b>not</b> be overwritten * unless {@link #defaultOverwrite(boolean)} is true. * @param name the header name. * @param expression the expression. * @return the header enricher spec. */ public HeaderEnricherSpec headerExpression(String name, String expression) { return headerExpression(name, expression, null); } /** * Add a single header specification where the value is a String representation of a * SpEL {@link Expression}. * @param name the header name. * @param expression the expression. * @param overwrite true to overwrite an existing header. * @return the header enricher spec. */ public HeaderEnricherSpec headerExpression(String name, String expression, Boolean overwrite) { Assert.hasText(expression, "'expression' must not be empty"); return headerExpression(name, PARSER.parseExpression(expression), overwrite); } /** * Add a single header specification where the value is obtained by invoking the * {@link Function} callback. If the header exists, it will <b>not</b> be overwritten * unless {@link #defaultOverwrite(boolean)} is true. * @param name the header name. * @param function the function. * @param <P> the payload type. * @return the header enricher spec. * @see FunctionExpression */ public <P> HeaderEnricherSpec headerFunction(String name, Function<Message<P>, Object> function) { return headerFunction(name, function, null); } /** * Add a single header specification where the value is obtained by invoking the * {@link Function} callback. * @param name the header name. * @param function the function. * @param overwrite true to overwrite an existing header. * @param <P> the payload type. * @return the header enricher spec. * @see FunctionExpression */ public <P> HeaderEnricherSpec headerFunction(String name, Function<Message<P>, Object> function, Boolean overwrite) { return headerExpression(name, new FunctionExpression<>(function), overwrite); } private HeaderEnricherSpec headerExpression(String name, Expression expression, Boolean overwrite) { AbstractHeaderValueMessageProcessor<?> headerValueMessageProcessor = new ExpressionEvaluatingHeaderValueMessageProcessor<>(expression, null); headerValueMessageProcessor.setOverwrite(overwrite); return header(name, headerValueMessageProcessor); } /** * Add a single header specification where the value is obtained by calling the * {@link HeaderValueMessageProcessor}. * @param headerName the header name. * @param headerValueMessageProcessor the message processor. * @param <V> the value type. * @return the header enricher spec. */ public <V> HeaderEnricherSpec header(String headerName, HeaderValueMessageProcessor<V> headerValueMessageProcessor) { Assert.hasText(headerName, "'headerName' must not be empty"); this.headerToAdd.put(headerName, headerValueMessageProcessor); return _this(); } /** * Add header specifications to automatically convert header channels (reply, error * channels) to Strings and store them in a header channel registry. Allows * persistence and serialization of messages without losing these important framework * headers. * @return the header enricher spec. * @see org.springframework.integration.support.channel.HeaderChannelRegistry */ public HeaderEnricherSpec headerChannelsToString() { return headerChannelsToString(null); } /** * Add header specifications to automatically convert header channels (reply, error * channels) to Strings and store them in a header channel registry. Allows * persistence and serialization of messages without losing these important framework * headers. * @param timeToLiveExpression the minimum time that the mapping will remain in the registry. * @return the header enricher spec. * @see org.springframework.integration.support.channel.HeaderChannelRegistry */ public HeaderEnricherSpec headerChannelsToString(String timeToLiveExpression) { return headerExpression("replyChannel", "@" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME + ".channelToChannelName(headers.replyChannel" + (StringUtils.hasText(timeToLiveExpression) ? ", " + timeToLiveExpression : "") + ")", true) .headerExpression("errorChannel", "@" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME + ".channelToChannelName(headers.errorChannel" + (StringUtils.hasText(timeToLiveExpression) ? ", " + timeToLiveExpression : "") + ")", true); } @Override protected Tuple2<ConsumerEndpointFactoryBean, MessageTransformingHandler> doGet() { HeaderEnricher headerEnricher = new HeaderEnricher(new HashMap<>(this.headerToAdd)); headerEnricher.setDefaultOverwrite(this.defaultOverwrite); headerEnricher.setShouldSkipNulls(this.shouldSkipNulls); headerEnricher.setMessageProcessor(this.messageProcessor); this.componentsToRegister.add(headerEnricher); this.handler = new MessageTransformingHandler(headerEnricher); return super.doGet(); } }