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}