001/* 002 * Copyright 2002-2017 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.core; 018 019import java.util.Map; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023 024import org.springframework.messaging.Message; 025import org.springframework.messaging.MessageHeaders; 026import org.springframework.messaging.MessagingException; 027import org.springframework.messaging.converter.MessageConversionException; 028import org.springframework.messaging.converter.MessageConverter; 029import org.springframework.messaging.converter.SimpleMessageConverter; 030import org.springframework.messaging.converter.SmartMessageConverter; 031import org.springframework.util.Assert; 032 033/** 034 * Abstract base class for implementations of {@link MessageSendingOperations}. 035 * 036 * @author Mark Fisher 037 * @author Rossen Stoyanchev 038 * @author Stephane Nicoll 039 * @since 4.0 040 */ 041public abstract class AbstractMessageSendingTemplate<D> implements MessageSendingOperations<D> { 042 043 /** 044 * Name of the header that can be set to provide further information 045 * (e.g. a {@code MethodParameter} instance) about the origin of the 046 * payload, to be taken into account as a conversion hint. 047 * @since 4.2 048 */ 049 public static final String CONVERSION_HINT_HEADER = "conversionHint"; 050 051 052 protected final Log logger = LogFactory.getLog(getClass()); 053 054 private volatile D defaultDestination; 055 056 private volatile MessageConverter converter = new SimpleMessageConverter(); 057 058 059 /** 060 * Configure the default destination to use in send methods that don't have 061 * a destination argument. If a default destination is not configured, send methods 062 * without a destination argument will raise an exception if invoked. 063 */ 064 public void setDefaultDestination(D defaultDestination) { 065 this.defaultDestination = defaultDestination; 066 } 067 068 /** 069 * Return the configured default destination. 070 */ 071 public D getDefaultDestination() { 072 return this.defaultDestination; 073 } 074 075 /** 076 * Set the {@link MessageConverter} to use in {@code convertAndSend} methods. 077 * <p>By default, {@link SimpleMessageConverter} is used. 078 * @param messageConverter the message converter to use 079 */ 080 public void setMessageConverter(MessageConverter messageConverter) { 081 Assert.notNull(messageConverter, "MessageConverter must not be null"); 082 this.converter = messageConverter; 083 } 084 085 /** 086 * Return the configured {@link MessageConverter}. 087 */ 088 public MessageConverter getMessageConverter() { 089 return this.converter; 090 } 091 092 093 @Override 094 public void send(Message<?> message) { 095 send(getRequiredDefaultDestination(), message); 096 } 097 098 protected final D getRequiredDefaultDestination() { 099 Assert.state(this.defaultDestination != null, "No 'defaultDestination' configured"); 100 return this.defaultDestination; 101 } 102 103 @Override 104 public void send(D destination, Message<?> message) { 105 doSend(destination, message); 106 } 107 108 protected abstract void doSend(D destination, Message<?> message); 109 110 111 @Override 112 public void convertAndSend(Object payload) throws MessagingException { 113 convertAndSend(payload, null); 114 } 115 116 @Override 117 public void convertAndSend(D destination, Object payload) throws MessagingException { 118 convertAndSend(destination, payload, (Map<String, Object>) null); 119 } 120 121 @Override 122 public void convertAndSend(D destination, Object payload, Map<String, Object> headers) throws MessagingException { 123 convertAndSend(destination, payload, headers, null); 124 } 125 126 @Override 127 public void convertAndSend(Object payload, MessagePostProcessor postProcessor) throws MessagingException { 128 convertAndSend(getRequiredDefaultDestination(), payload, postProcessor); 129 } 130 131 @Override 132 public void convertAndSend(D destination, Object payload, MessagePostProcessor postProcessor) 133 throws MessagingException { 134 135 convertAndSend(destination, payload, null, postProcessor); 136 } 137 138 @Override 139 public void convertAndSend(D destination, Object payload, Map<String, Object> headers, 140 MessagePostProcessor postProcessor) throws MessagingException { 141 142 Message<?> message = doConvert(payload, headers, postProcessor); 143 send(destination, message); 144 } 145 146 /** 147 * Convert the given Object to serialized form, possibly using a 148 * {@link MessageConverter}, wrap it as a message with the given 149 * headers and apply the given post processor. 150 * @param payload the Object to use as payload 151 * @param headers headers for the message to send 152 * @param postProcessor the post processor to apply to the message 153 * @return the converted message 154 */ 155 protected Message<?> doConvert(Object payload, Map<String, Object> headers, MessagePostProcessor postProcessor) { 156 MessageHeaders messageHeaders = null; 157 Object conversionHint = (headers != null ? headers.get(CONVERSION_HINT_HEADER) : null); 158 159 Map<String, Object> headersToUse = processHeadersToSend(headers); 160 if (headersToUse != null) { 161 if (headersToUse instanceof MessageHeaders) { 162 messageHeaders = (MessageHeaders) headersToUse; 163 } 164 else { 165 messageHeaders = new MessageHeaders(headersToUse); 166 } 167 } 168 169 MessageConverter converter = getMessageConverter(); 170 Message<?> message = (converter instanceof SmartMessageConverter ? 171 ((SmartMessageConverter) converter).toMessage(payload, messageHeaders, conversionHint) : 172 converter.toMessage(payload, messageHeaders)); 173 if (message == null) { 174 String payloadType = (payload != null ? payload.getClass().getName() : null); 175 Object contentType = (messageHeaders != null ? messageHeaders.get(MessageHeaders.CONTENT_TYPE) : null); 176 throw new MessageConversionException("Unable to convert payload with type='" + payloadType + 177 "', contentType='" + contentType + "', converter=[" + getMessageConverter() + "]"); 178 } 179 if (postProcessor != null) { 180 message = postProcessor.postProcessMessage(message); 181 } 182 return message; 183 } 184 185 /** 186 * Provides access to the map of input headers before a send operation. 187 * Subclasses can modify the headers and then return the same or a different map. 188 * <p>This default implementation in this class returns the input map. 189 * @param headers the headers to send (or {@code null} if none) 190 * @return the actual headers to send (or {@code null} if none) 191 */ 192 protected Map<String, Object> processHeadersToSend(Map<String, Object> headers) { 193 return headers; 194 } 195 196}