001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.messaging.support;
018
019import java.util.Map;
020
021import org.springframework.lang.Nullable;
022import org.springframework.messaging.Message;
023import org.springframework.messaging.MessageChannel;
024import org.springframework.messaging.MessageHeaders;
025import org.springframework.util.Assert;
026
027/**
028 * A builder for creating a {@link GenericMessage}
029 * (or {@link ErrorMessage} if the payload is of type {@link Throwable}).
030 *
031 * @author Arjen Poutsma
032 * @author Mark Fisher
033 * @author Rossen Stoyanchev
034 * @since 4.0
035 * @param <T> the message payload type
036 * @see GenericMessage
037 * @see ErrorMessage
038 */
039public final class MessageBuilder<T> {
040
041        private final T payload;
042
043        @Nullable
044        private final Message<T> providedMessage;
045
046        private MessageHeaderAccessor headerAccessor;
047
048
049        private MessageBuilder(Message<T> providedMessage) {
050                Assert.notNull(providedMessage, "Message must not be null");
051                this.payload = providedMessage.getPayload();
052                this.providedMessage = providedMessage;
053                this.headerAccessor = new MessageHeaderAccessor(providedMessage);
054        }
055
056        private MessageBuilder(T payload, MessageHeaderAccessor accessor) {
057                Assert.notNull(payload, "Payload must not be null");
058                Assert.notNull(accessor, "MessageHeaderAccessor must not be null");
059                this.payload = payload;
060                this.providedMessage = null;
061                this.headerAccessor = accessor;
062        }
063
064
065        /**
066         * Set the message headers to use by providing a {@code MessageHeaderAccessor}.
067         * @param accessor the headers to use
068         */
069        public MessageBuilder<T> setHeaders(MessageHeaderAccessor accessor) {
070                Assert.notNull(accessor, "MessageHeaderAccessor must not be null");
071                this.headerAccessor = accessor;
072                return this;
073        }
074
075        /**
076         * Set the value for the given header name. If the provided value is {@code null},
077         * the header will be removed.
078         */
079        public MessageBuilder<T> setHeader(String headerName, @Nullable Object headerValue) {
080                this.headerAccessor.setHeader(headerName, headerValue);
081                return this;
082        }
083
084        /**
085         * Set the value for the given header name only if the header name is not already
086         * associated with a value.
087         */
088        public MessageBuilder<T> setHeaderIfAbsent(String headerName, Object headerValue) {
089                this.headerAccessor.setHeaderIfAbsent(headerName, headerValue);
090                return this;
091        }
092
093        /**
094         * Removes all headers provided via array of 'headerPatterns'. As the name suggests
095         * the array may contain simple matching patterns for header names. Supported pattern
096         * styles are: "xxx*", "*xxx", "*xxx*" and "xxx*yyy".
097         */
098        public MessageBuilder<T> removeHeaders(String... headerPatterns) {
099                this.headerAccessor.removeHeaders(headerPatterns);
100                return this;
101        }
102
103        /**
104         * Remove the value for the given header name.
105         */
106        public MessageBuilder<T> removeHeader(String headerName) {
107                this.headerAccessor.removeHeader(headerName);
108                return this;
109        }
110
111        /**
112         * Copy the name-value pairs from the provided Map. This operation will overwrite any
113         * existing values. Use { {@link #copyHeadersIfAbsent(Map)} to avoid overwriting
114         * values. Note that the 'id' and 'timestamp' header values will never be overwritten.
115         */
116        public MessageBuilder<T> copyHeaders(@Nullable Map<String, ?> headersToCopy) {
117                this.headerAccessor.copyHeaders(headersToCopy);
118                return this;
119        }
120
121        /**
122         * Copy the name-value pairs from the provided Map. This operation will <em>not</em>
123         * overwrite any existing values.
124         */
125        public MessageBuilder<T> copyHeadersIfAbsent(@Nullable Map<String, ?> headersToCopy) {
126                this.headerAccessor.copyHeadersIfAbsent(headersToCopy);
127                return this;
128        }
129
130        public MessageBuilder<T> setReplyChannel(MessageChannel replyChannel) {
131                this.headerAccessor.setReplyChannel(replyChannel);
132                return this;
133        }
134
135        public MessageBuilder<T> setReplyChannelName(String replyChannelName) {
136                this.headerAccessor.setReplyChannelName(replyChannelName);
137                return this;
138        }
139
140        public MessageBuilder<T> setErrorChannel(MessageChannel errorChannel) {
141                this.headerAccessor.setErrorChannel(errorChannel);
142                return this;
143        }
144
145        public MessageBuilder<T> setErrorChannelName(String errorChannelName) {
146                this.headerAccessor.setErrorChannelName(errorChannelName);
147                return this;
148        }
149
150        @SuppressWarnings("unchecked")
151        public Message<T> build() {
152                if (this.providedMessage != null && !this.headerAccessor.isModified()) {
153                        return this.providedMessage;
154                }
155                MessageHeaders headersToUse = this.headerAccessor.toMessageHeaders();
156                if (this.payload instanceof Throwable) {
157                        if (this.providedMessage != null && this.providedMessage instanceof ErrorMessage) {
158                                Message<?> message = ((ErrorMessage) this.providedMessage).getOriginalMessage();
159                                if (message != null) {
160                                        return (Message<T>) new ErrorMessage((Throwable) this.payload, headersToUse, message);
161                                }
162                        }
163                        return (Message<T>) new ErrorMessage((Throwable) this.payload, headersToUse);
164                }
165                else {
166                        return new GenericMessage<>(this.payload, headersToUse);
167                }
168        }
169
170
171        /**
172         * Create a builder for a new {@link Message} instance pre-populated with all of the
173         * headers copied from the provided message. The payload of the provided Message will
174         * also be used as the payload for the new message.
175         * <p>If the provided message is an {@link ErrorMessage}, the
176         * {@link ErrorMessage#getOriginalMessage() originalMessage} it contains, will be
177         * passed on to new instance.
178         * @param message the Message from which the payload and all headers will be copied
179         */
180        public static <T> MessageBuilder<T> fromMessage(Message<T> message) {
181                return new MessageBuilder<>(message);
182        }
183
184        /**
185         * Create a new builder for a message with the given payload.
186         * @param payload the payload
187         */
188        public static <T> MessageBuilder<T> withPayload(T payload) {
189                return new MessageBuilder<>(payload, new MessageHeaderAccessor());
190        }
191
192        /**
193         * A shortcut factory method for creating a message with the given payload
194         * and {@code MessageHeaders}.
195         * <p><strong>Note:</strong> the given {@code MessageHeaders} instance is used
196         * directly in the new message, i.e. it is not copied.
197         * @param payload the payload to use (never {@code null})
198         * @param messageHeaders the headers to use (never {@code null})
199         * @return the created message
200         * @since 4.1
201         */
202        @SuppressWarnings("unchecked")
203        public static <T> Message<T> createMessage(@Nullable T payload, MessageHeaders messageHeaders) {
204                Assert.notNull(payload, "Payload must not be null");
205                Assert.notNull(messageHeaders, "MessageHeaders must not be null");
206                if (payload instanceof Throwable) {
207                        return (Message<T>) new ErrorMessage((Throwable) payload, messageHeaders);
208                }
209                else {
210                        return new GenericMessage<>(payload, messageHeaders);
211                }
212        }
213
214}