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 }