001/*
002 * Copyright 2002-2014 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.messaging.Message;
022import org.springframework.messaging.MessageChannel;
023import org.springframework.messaging.MessageHeaders;
024import org.springframework.util.Assert;
025
026/**
027 * A builder for creating a {@link GenericMessage}
028 * (or {@link ErrorMessage} if the payload is of type {@link Throwable}).
029 *
030 * @author Arjen Poutsma
031 * @author Mark Fisher
032 * @author Rossen Stoyanchev
033 * @since 4.0
034 * @see GenericMessage
035 * @see ErrorMessage
036 */
037public final class MessageBuilder<T> {
038
039        private final T payload;
040
041        private final Message<T> originalMessage;
042
043        private MessageHeaderAccessor headerAccessor;
044
045
046        private MessageBuilder(Message<T> originalMessage) {
047                Assert.notNull(originalMessage, "Message must not be null");
048                this.payload = originalMessage.getPayload();
049                this.originalMessage = originalMessage;
050                this.headerAccessor = new MessageHeaderAccessor(originalMessage);
051        }
052
053        private MessageBuilder(T payload, MessageHeaderAccessor accessor) {
054                Assert.notNull(payload, "Payload must not be null");
055                Assert.notNull(accessor, "MessageHeaderAccessor must not be null");
056                this.payload = payload;
057                this.originalMessage = null;
058                this.headerAccessor = accessor;
059        }
060
061
062        /**
063         * Set the message headers to use by providing a {@code MessageHeaderAccessor}.
064         * @param accessor the headers to use
065         */
066        public MessageBuilder<T> setHeaders(MessageHeaderAccessor accessor) {
067                Assert.notNull(accessor, "MessageHeaderAccessor must not be null");
068                this.headerAccessor = accessor;
069                return this;
070        }
071
072        /**
073         * Set the value for the given header name. If the provided value is {@code null},
074         * the header will be removed.
075         */
076        public MessageBuilder<T> setHeader(String headerName, Object headerValue) {
077                this.headerAccessor.setHeader(headerName, headerValue);
078                return this;
079        }
080
081        /**
082         * Set the value for the given header name only if the header name is not already
083         * associated with a value.
084         */
085        public MessageBuilder<T> setHeaderIfAbsent(String headerName, Object headerValue) {
086                this.headerAccessor.setHeaderIfAbsent(headerName, headerValue);
087                return this;
088        }
089
090        /**
091         * Removes all headers provided via array of 'headerPatterns'. As the name suggests
092         * the array may contain simple matching patterns for header names. Supported pattern
093         * styles are: "xxx*", "*xxx", "*xxx*" and "xxx*yyy".
094         */
095        public MessageBuilder<T> removeHeaders(String... headerPatterns) {
096                this.headerAccessor.removeHeaders(headerPatterns);
097                return this;
098        }
099        /**
100         * Remove the value for the given header name.
101         */
102        public MessageBuilder<T> removeHeader(String headerName) {
103                this.headerAccessor.removeHeader(headerName);
104                return this;
105        }
106
107        /**
108         * Copy the name-value pairs from the provided Map. This operation will overwrite any
109         * existing values. Use { {@link #copyHeadersIfAbsent(Map)} to avoid overwriting
110         * values. Note that the 'id' and 'timestamp' header values will never be overwritten.
111         */
112        public MessageBuilder<T> copyHeaders(Map<String, ?> headersToCopy) {
113                this.headerAccessor.copyHeaders(headersToCopy);
114                return this;
115        }
116
117        /**
118         * Copy the name-value pairs from the provided Map. This operation will <em>not</em>
119         * overwrite any existing values.
120         */
121        public MessageBuilder<T> copyHeadersIfAbsent(Map<String, ?> headersToCopy) {
122                this.headerAccessor.copyHeadersIfAbsent(headersToCopy);
123                return this;
124        }
125
126        public MessageBuilder<T> setReplyChannel(MessageChannel replyChannel) {
127                this.headerAccessor.setReplyChannel(replyChannel);
128                return this;
129        }
130
131        public MessageBuilder<T> setReplyChannelName(String replyChannelName) {
132                this.headerAccessor.setReplyChannelName(replyChannelName);
133                return this;
134        }
135
136        public MessageBuilder<T> setErrorChannel(MessageChannel errorChannel) {
137                this.headerAccessor.setErrorChannel(errorChannel);
138                return this;
139        }
140
141        public MessageBuilder<T> setErrorChannelName(String errorChannelName) {
142                this.headerAccessor.setErrorChannelName(errorChannelName);
143                return this;
144        }
145
146        @SuppressWarnings("unchecked")
147        public Message<T> build() {
148                if (this.originalMessage != null && !this.headerAccessor.isModified()) {
149                        return this.originalMessage;
150                }
151                MessageHeaders headersToUse = this.headerAccessor.toMessageHeaders();
152                if (this.payload instanceof Throwable) {
153                        return (Message<T>) new ErrorMessage((Throwable) this.payload, headersToUse);
154                }
155                else {
156                        return new GenericMessage<T>(this.payload, headersToUse);
157                }
158        }
159
160
161        /**
162         * Create a builder for a new {@link Message} instance pre-populated with all of the
163         * headers copied from the provided message. The payload of the provided Message will
164         * also be used as the payload for the new message.
165         * @param message the Message from which the payload and all headers will be copied
166         */
167        public static <T> MessageBuilder<T> fromMessage(Message<T> message) {
168                return new MessageBuilder<T>(message);
169        }
170
171        /**
172         * Create a new builder for a message with the given payload.
173         * @param payload the payload
174         */
175        public static <T> MessageBuilder<T> withPayload(T payload) {
176                return new MessageBuilder<T>(payload, new MessageHeaderAccessor());
177        }
178
179        /**
180         * A shortcut factory method for creating a message with the given payload
181         * and {@code MessageHeaders}.
182         * <p><strong>Note:</strong> the given {@code MessageHeaders} instance is used
183         * directly in the new message, i.e. it is not copied.
184         * @param payload the payload to use (never {@code null})
185         * @param messageHeaders the headers to use (never {@code null})
186         * @return the created message
187         * @since 4.1
188         */
189        @SuppressWarnings("unchecked")
190        public static <T> Message<T> createMessage(T payload, MessageHeaders messageHeaders) {
191                Assert.notNull(payload, "Payload must not be null");
192                Assert.notNull(messageHeaders, "MessageHeaders must not be null");
193                if (payload instanceof Throwable) {
194                        return (Message<T>) new ErrorMessage((Throwable) payload, messageHeaders);
195                }
196                else {
197                        return new GenericMessage<T>(payload, messageHeaders);
198                }
199        }
200
201}