1 /* 2 * Copyright 2002-2018 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 module hunt.stomp.support.MessageBuilder; 18 19 import hunt.stomp.support.ErrorMessage; 20 import hunt.stomp.support.GenericMessage; 21 import hunt.stomp.support.MessageHeaderAccessor; 22 23 import hunt.stomp.Message; 24 import hunt.stomp.MessageChannel; 25 import hunt.stomp.MessageHeaders; 26 27 import hunt.collection.Map; 28 29 /** 30 * A builder for creating a {@link GenericMessage} 31 * (or {@link ErrorMessage} if the payload is of type {@link Throwable}). 32 * 33 * @author Arjen Poutsma 34 * @author Mark Fisher 35 * @author Rossen Stoyanchev 36 * @since 4.0 37 * @param (T) the message payload type 38 * @see GenericMessage 39 * @see ErrorMessage 40 */ 41 final class MessageBuilder(T) { 42 43 private T payload; 44 45 private Message!(T) originalMessage; 46 47 private MessageHeaderAccessor headerAccessor; 48 49 50 private this(Message!(T) originalMessage) { 51 assert(originalMessage, "Message must not be null"); 52 this.payload = originalMessage.getPayload(); 53 this.originalMessage = originalMessage; 54 this.headerAccessor = new MessageHeaderAccessor(originalMessage); 55 } 56 57 private this(T payload, MessageHeaderAccessor accessor) { 58 // assert(payload, "Payload must not be null"); 59 assert(accessor, "MessageHeaderAccessor must not be null"); 60 this.payload = payload; 61 this.originalMessage = null; 62 this.headerAccessor = accessor; 63 } 64 65 66 /** 67 * Set the message headers to use by providing a {@code MessageHeaderAccessor}. 68 * @param accessor the headers to use 69 */ 70 MessageBuilder!(T) setHeaders(MessageHeaderAccessor accessor) { 71 assert(accessor, "MessageHeaderAccessor must not be null"); 72 this.headerAccessor = accessor; 73 return this; 74 } 75 76 /** 77 * Set the value for the given header name. If the provided value is {@code null}, 78 * the header will be removed. 79 */ 80 MessageBuilder!(T) setHeader(string headerName, Object headerValue) { 81 this.headerAccessor.setHeader(headerName, headerValue); 82 return this; 83 } 84 85 /** 86 * Set the value for the given header name only if the header name is not already 87 * associated with a value. 88 */ 89 MessageBuilder!(T) setHeaderIfAbsent(string headerName, Object headerValue) { 90 this.headerAccessor.setHeaderIfAbsent(headerName, headerValue); 91 return this; 92 } 93 94 /** 95 * Removes all headers provided via array of 'headerPatterns'. As the name suggests 96 * the array may contain simple matching patterns for header names. Supported pattern 97 * styles are: "xxx*", "*xxx", "*xxx*" and "xxx*yyy". 98 */ 99 MessageBuilder!(T) removeHeaders(string[] headerPatterns... ) { 100 this.headerAccessor.removeHeaders(headerPatterns.dup); 101 return this; 102 } 103 /** 104 * Remove the value for the given header name. 105 */ 106 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 MessageBuilder!(T) copyHeaders(Map!(string, Object) 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 MessageBuilder!(T) copyHeadersIfAbsent(Map!(string, Object) headersToCopy) { 126 this.headerAccessor.copyHeadersIfAbsent(headersToCopy); 127 return this; 128 } 129 130 MessageBuilder!(T) setReplyChannel(MessageChannel replyChannel) { 131 this.headerAccessor.setReplyChannel(replyChannel); 132 return this; 133 } 134 135 MessageBuilder!(T) setReplyChannelName(string replyChannelName) { 136 this.headerAccessor.setReplyChannelName(replyChannelName); 137 return this; 138 } 139 140 MessageBuilder!(T) setErrorChannel(MessageChannel errorChannel) { 141 this.headerAccessor.setErrorChannel(errorChannel); 142 return this; 143 } 144 145 MessageBuilder!(T) setErrorChannelName(string errorChannelName) { 146 this.headerAccessor.setErrorChannelName(errorChannelName); 147 return this; 148 } 149 150 151 Message!(T) build() { 152 if (this.originalMessage !is null && !this.headerAccessor.isModified()) { 153 return this.originalMessage; 154 } 155 MessageHeaders headersToUse = this.headerAccessor.toMessageHeaders(); 156 static if(is(T == class) || is(T == interface)) { 157 Throwable th = cast(Throwable) this.payload; 158 if (th !is null) { 159 return cast(Message!(T)) new ErrorMessage(th, headersToUse); 160 } 161 } 162 163 return new GenericMessage!(T)(this.payload, headersToUse); 164 } 165 166 } 167 168 169 /** 170 */ 171 class MessageHelper { 172 173 /** 174 * Create a builder for a new {@link Message} instance pre-populated with all of the 175 * headers copied from the provided message. The payload of the provided Message will 176 * also be used as the payload for the new message. 177 * @param message the Message from which the payload and all headers will be copied 178 */ 179 static MessageBuilder!(T) fromMessage(T)(Message!(T) message) { 180 return new MessageBuilder!(T)(message); 181 } 182 183 /** 184 * Create a new builder for a message with the given payload. 185 * @param payload the payload 186 */ 187 static MessageBuilder!(T) withPayload(T)(T payload) { 188 return new MessageBuilder!(T)(payload, new MessageHeaderAccessor()); 189 } 190 191 /** 192 * A shortcut factory method for creating a message with the given payload 193 * and {@code MessageHeaders}. 194 * <p><strong>Note:</strong> the given {@code MessageHeaders} instance is used 195 * directly in the new message, i.e. it is not copied. 196 * @param payload the payload to use (never {@code null}) 197 * @param messageHeaders the headers to use (never {@code null}) 198 * @return the created message 199 * @since 4.1 200 */ 201 static MessageBase createMessage(T)(Object payload, MessageHeaders messageHeaders) 202 if(is(T == class) || is(T == interface)) { 203 // assert(payload, "Payload must not be null"); 204 assert(messageHeaders, "MessageHeaders must not be null"); 205 Throwable th = cast(Throwable) payload; 206 if (th !is null) 207 return new ErrorMessage(th, messageHeaders); 208 return new GenericMessage!(T)(payload, messageHeaders); 209 } 210 211 static Message!(T) createMessage(T)(T payload, MessageHeaders messageHeaders) 212 if(!is(T == class) && !is(T == interface)) { 213 // assert(payload, "Payload must not be null"); 214 assert(messageHeaders, "MessageHeaders must not be null"); 215 216 return new GenericMessage!(T)(payload, messageHeaders); 217 } 218 }