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.core.AbstractMessageSendingTemplate; 18 19 import hunt.collection.Map; 20 import hunt.logging; 21 import hunt.Exceptions; 22 23 import hunt.stomp.converter.MessageConverter; 24 import hunt.stomp.converter.SimpleMessageConverter; 25 import hunt.stomp.converter.SmartMessageConverter; 26 import hunt.stomp.core.MessagePostProcessor; 27 import hunt.stomp.core.MessageSendingOperations; 28 import hunt.stomp.Message; 29 import hunt.stomp.MessageHeaders; 30 import hunt.stomp.MessagingException; 31 32 33 /** 34 * Abstract base class for implementations of {@link MessageSendingOperations}. 35 * 36 * @author Mark Fisher 37 * @author Rossen Stoyanchev 38 * @author Stephane Nicoll 39 * @since 4.0 40 * @param (T) the destination type 41 */ 42 abstract class AbstractMessageSendingTemplate(T) : MessageSendingOperations!(T) { 43 44 /** 45 * Name of the header that can be set to provide further information 46 * (e.g. a {@code MethodParameter} instance) about the origin of the 47 * payload, to be taken into account as a conversion hint. 48 * @since 4.2 49 */ 50 enum string CONVERSION_HINT_HEADER = "conversionHint"; 51 52 private T defaultDestination; 53 54 private MessageConverter converter; 55 56 this() { 57 converter = new SimpleMessageConverter(); 58 } 59 60 61 /** 62 * Configure the default destination to use in send methods that don't have 63 * a destination argument. If a default destination is not configured, send methods 64 * without a destination argument will raise an exception if invoked. 65 */ 66 void setDefaultDestination(T defaultDestination) { 67 this.defaultDestination = defaultDestination; 68 } 69 70 /** 71 * Return the configured default destination. 72 */ 73 74 T getDefaultDestination() { 75 return this.defaultDestination; 76 } 77 78 /** 79 * Set the {@link MessageConverter} to use in {@code convertAndSend} methods. 80 * <p>By default, {@link SimpleMessageConverter} is used. 81 * @param messageConverter the message converter to use 82 */ 83 void setMessageConverter(MessageConverter messageConverter) { 84 assert(messageConverter, "MessageConverter must not be null"); 85 this.converter = messageConverter; 86 } 87 88 /** 89 * Return the configured {@link MessageConverter}. 90 */ 91 MessageConverter getMessageConverter() { 92 return this.converter; 93 } 94 95 96 override 97 void send(MessageBase message) { 98 send(getRequiredDefaultDestination(), message); 99 } 100 101 protected final T getRequiredDefaultDestination() { 102 assert(this.defaultDestination !is null, "No 'defaultDestination' configured"); 103 return this.defaultDestination; 104 } 105 106 override 107 void send(T destination, MessageBase message) { 108 doSend(destination, message); 109 } 110 111 protected abstract void doSend(T destination, MessageBase message); 112 113 114 override 115 void convertAndSend(Object payload) { 116 convertAndSend(payload, null); 117 } 118 119 override 120 void convertAndSend(T destination, Object payload) { 121 convertAndSend(destination, payload, cast(Map!(string, Object)) null); 122 } 123 124 override 125 void convertAndSend(T destination, Object payload, Map!(string, Object) headers) { 126 127 convertAndSend(destination, payload, headers, null); 128 } 129 130 override 131 void convertAndSend(Object payload, MessagePostProcessor postProcessor) { 132 convertAndSend(getRequiredDefaultDestination(), payload, postProcessor); 133 } 134 135 override 136 void convertAndSend(T destination, Object payload, MessagePostProcessor postProcessor) { 137 convertAndSend(destination, payload, null, postProcessor); 138 } 139 140 override 141 void convertAndSend(T destination, Object payload, Map!(string, Object) headers, 142 MessagePostProcessor postProcessor) { 143 144 MessageBase message = doConvert(payload, headers, postProcessor); 145 send(destination, message); 146 } 147 148 /** 149 * Convert the given Object to serialized form, possibly using a 150 * {@link MessageConverter}, wrap it as a message with the given 151 * headers and apply the given post processor. 152 * @param payload the Object to use as payload 153 * @param headers headers for the message to send 154 * @param postProcessor the post processor to apply to the message 155 * @return the converted message 156 */ 157 protected Message!(T) doConvert(Object payload, Map!(string, Object) headers, 158 MessagePostProcessor postProcessor) { 159 160 MessageHeaders messageHeaders = null; 161 162 Map!(string, Object) headersToUse = processHeadersToSend(headers); 163 if (headersToUse !is null) { 164 messageHeaders = cast(MessageHeaders) headersToUse; 165 if (messageHeaders is null) { 166 messageHeaders = new MessageHeaders(headersToUse); 167 } 168 } 169 170 Object conversionHintObj = (headers !is null ? headers.get(CONVERSION_HINT_HEADER) : null); 171 TypeInfo conversionHint = cast(TypeInfo)conversionHintObj; 172 MessageConverter converter = getMessageConverter(); 173 174 version(HUNT_DEBUG) { 175 if(conversionHint is null) 176 warningf("conversionHintObj: %s", conversionHintObj); 177 else 178 tracef("conversionHint: %s", conversionHint); 179 tracef("MessageConverter is: %s", typeid(cast(Object)converter)); 180 } 181 182 SmartMessageConverter smc = cast(SmartMessageConverter) converter; 183 MessageBase message; 184 if(smc is null) 185 message = converter.toMessage(payload, messageHeaders); 186 else 187 message = smc.toMessage(payload, messageHeaders, conversionHint); 188 189 if (message is null) { 190 version(HUNT_DEBUG) warning("message is null"); 191 string payloadType = typeid(payload).toString(); 192 Object contentType = (messageHeaders !is null ? messageHeaders.get(MessageHeaders.CONTENT_TYPE) : null); 193 if(contentType is null) { 194 throw new MessageConversionException("Unable to convert payload with type='null', converter=[" ~ 195 (cast(Object) getMessageConverter()).toString() ~ "]"); 196 } else { 197 throw new MessageConversionException("Unable to convert payload with type='" ~ payloadType ~ 198 "', contentType='" ~ contentType.toString() ~ "', converter=[" ~ 199 (cast(Object) getMessageConverter()).toString() ~ "]"); 200 } 201 } 202 203 if (postProcessor !is null) { 204 message = postProcessor.postProcessMessage(message); 205 } 206 Message!(T) r = cast(Message!(T)) message; 207 if(r is null) 208 warningf("Message conversion error, expected: %s, actual: %s", typeid(Message!(T)), typeid(message)); 209 return r; 210 } 211 212 /** 213 * Provides access to the map of input headers before a send operation. 214 * Subclasses can modify the headers and then return the same or a different map. 215 * <p>This default implementation in this class returns the input map. 216 * @param headers the headers to send (or {@code null} if none) 217 * @return the actual headers to send (or {@code null} if none) 218 */ 219 220 protected Map!(string, Object) processHeadersToSend(Map!(string, Object) headers) { 221 return headers; 222 } 223 224 }