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}