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.jms.core;
018
019import java.util.Map;
020
021import javax.jms.ConnectionFactory;
022import javax.jms.Destination;
023import javax.jms.JMSException;
024import javax.jms.Session;
025
026import org.springframework.beans.factory.InitializingBean;
027import org.springframework.jms.InvalidDestinationException;
028import org.springframework.jms.JmsException;
029import org.springframework.jms.support.converter.MessageConverter;
030import org.springframework.jms.support.converter.MessagingMessageConverter;
031import org.springframework.jms.support.converter.SimpleMessageConverter;
032import org.springframework.lang.Nullable;
033import org.springframework.messaging.Message;
034import org.springframework.messaging.MessagingException;
035import org.springframework.messaging.converter.MessageConversionException;
036import org.springframework.messaging.core.AbstractMessagingTemplate;
037import org.springframework.messaging.core.DestinationResolutionException;
038import org.springframework.messaging.core.MessagePostProcessor;
039import org.springframework.util.Assert;
040
041/**
042 * An implementation of {@link JmsMessageOperations}.
043 *
044 * @author Stephane Nicoll
045 * @author Juergen Hoeller
046 * @since 4.1
047 */
048public class JmsMessagingTemplate extends AbstractMessagingTemplate<Destination>
049                implements JmsMessageOperations, InitializingBean {
050
051        @Nullable
052        private JmsTemplate jmsTemplate;
053
054        private MessageConverter jmsMessageConverter = new MessagingMessageConverter();
055
056        private boolean converterSet;
057
058        @Nullable
059        private String defaultDestinationName;
060
061
062        /**
063         * Constructor for use with bean properties.
064         * Requires {@link #setConnectionFactory} or {@link #setJmsTemplate} to be called.
065         */
066        public JmsMessagingTemplate() {
067        }
068
069        /**
070         * Create a {@code JmsMessagingTemplate} instance with the JMS {@link ConnectionFactory}
071         * to use, implicitly building a {@link JmsTemplate} based on it.
072         * @since 4.1.2
073         */
074        public JmsMessagingTemplate(ConnectionFactory connectionFactory) {
075                this.jmsTemplate = new JmsTemplate(connectionFactory);
076        }
077
078        /**
079         * Create a {@code JmsMessagingTemplate} instance with the {@link JmsTemplate} to use.
080         */
081        public JmsMessagingTemplate(JmsTemplate jmsTemplate) {
082                Assert.notNull(jmsTemplate, "JmsTemplate must not be null");
083                this.jmsTemplate = jmsTemplate;
084        }
085
086
087        /**
088         * Set the ConnectionFactory to use for the underlying {@link JmsTemplate}.
089         * @since 4.1.2
090         */
091        public void setConnectionFactory(ConnectionFactory connectionFactory) {
092                if (this.jmsTemplate != null) {
093                        this.jmsTemplate.setConnectionFactory(connectionFactory);
094                }
095                else {
096                        this.jmsTemplate = new JmsTemplate(connectionFactory);
097                }
098        }
099
100        /**
101         * Return the ConnectionFactory that the underlying {@link JmsTemplate} uses.
102         * @since 4.1.2
103         */
104        @Nullable
105        public ConnectionFactory getConnectionFactory() {
106                return (this.jmsTemplate != null ? this.jmsTemplate.getConnectionFactory() : null);
107        }
108
109        /**
110         * Set the {@link JmsTemplate} to use.
111         */
112        public void setJmsTemplate(@Nullable JmsTemplate jmsTemplate) {
113                this.jmsTemplate = jmsTemplate;
114        }
115
116        /**
117         * Return the configured {@link JmsTemplate}.
118         */
119        @Nullable
120        public JmsTemplate getJmsTemplate() {
121                return this.jmsTemplate;
122        }
123
124        /**
125         * Set the {@link MessageConverter} to use to convert a {@link Message} from
126         * the messaging to and from a {@link javax.jms.Message}. By default, a
127         * {@link MessagingMessageConverter} is defined using a {@link SimpleMessageConverter}
128         * to convert the payload of the message.
129         * <p>Consider configuring a {@link MessagingMessageConverter} with a different
130         * {@link MessagingMessageConverter#setPayloadConverter(MessageConverter) payload converter}
131         * for more advanced scenarios.
132         * @see org.springframework.jms.support.converter.MessagingMessageConverter
133         */
134        public void setJmsMessageConverter(MessageConverter jmsMessageConverter) {
135                Assert.notNull(jmsMessageConverter, "MessageConverter must not be null");
136                this.jmsMessageConverter = jmsMessageConverter;
137                this.converterSet = true;
138        }
139
140        /**
141         * Return the {@link MessageConverter} to use to convert a {@link Message}
142         * from the messaging to and from a {@link javax.jms.Message}.
143         */
144        public MessageConverter getJmsMessageConverter() {
145                return this.jmsMessageConverter;
146        }
147
148        /**
149         * Configure the default destination name to use in send methods that don't have
150         * a destination argument. If a default destination is not configured, send methods
151         * without a destination argument will raise an exception if invoked.
152         * @see #setDefaultDestination(Object)
153         */
154        public void setDefaultDestinationName(@Nullable String defaultDestinationName) {
155                this.defaultDestinationName = defaultDestinationName;
156        }
157
158        /**
159         * Return the configured default destination name.
160         */
161        @Nullable
162        public String getDefaultDestinationName() {
163                return this.defaultDestinationName;
164        }
165
166        @Override
167        public void afterPropertiesSet() {
168                Assert.notNull(this.jmsTemplate, "Property 'connectionFactory' or 'jmsTemplate' is required");
169                if (!this.converterSet && this.jmsTemplate.getMessageConverter() != null) {
170                        ((MessagingMessageConverter) this.jmsMessageConverter)
171                                        .setPayloadConverter(this.jmsTemplate.getMessageConverter());
172                }
173        }
174
175        private JmsTemplate obtainJmsTemplate() {
176                Assert.state(this.jmsTemplate != null, "No JmsTemplate set");
177                return this.jmsTemplate;
178        }
179
180
181        @Override
182        public void send(Message<?> message) {
183                Destination defaultDestination = getDefaultDestination();
184                if (defaultDestination != null) {
185                        send(defaultDestination, message);
186                }
187                else {
188                        send(getRequiredDefaultDestinationName(), message);
189                }
190        }
191
192        @Override
193        public void convertAndSend(Object payload) throws MessagingException {
194                convertAndSend(payload, null);
195        }
196
197        @Override
198        public void convertAndSend(Object payload, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
199                Destination defaultDestination = getDefaultDestination();
200                if (defaultDestination != null) {
201                        convertAndSend(defaultDestination, payload, postProcessor);
202                }
203                else {
204                        convertAndSend(getRequiredDefaultDestinationName(), payload, postProcessor);
205                }
206        }
207
208        @Override
209        public void send(String destinationName, Message<?> message) throws MessagingException {
210                doSend(destinationName, message);
211        }
212
213        @Override
214        public void convertAndSend(String destinationName, Object payload) throws MessagingException {
215                convertAndSend(destinationName, payload, (Map<String, Object>) null);
216        }
217
218        @Override
219        public void convertAndSend(String destinationName, Object payload, @Nullable Map<String, Object> headers)
220                        throws MessagingException {
221
222                convertAndSend(destinationName, payload, headers, null);
223        }
224
225        @Override
226        public void convertAndSend(String destinationName, Object payload, @Nullable MessagePostProcessor postProcessor)
227                        throws MessagingException {
228
229                convertAndSend(destinationName, payload, null, postProcessor);
230        }
231
232        @Override
233        public void convertAndSend(String destinationName, Object payload, @Nullable Map<String, Object> headers,
234                        @Nullable MessagePostProcessor postProcessor) throws MessagingException {
235
236                Message<?> message = doConvert(payload, headers, postProcessor);
237                send(destinationName, message);
238        }
239
240        @Override
241        @Nullable
242        public Message<?> receive() {
243                Destination defaultDestination = getDefaultDestination();
244                if (defaultDestination != null) {
245                        return receive(defaultDestination);
246                }
247                else {
248                        return receive(getRequiredDefaultDestinationName());
249                }
250        }
251
252        @Override
253        @Nullable
254        public <T> T receiveAndConvert(Class<T> targetClass) {
255                Destination defaultDestination = getDefaultDestination();
256                if (defaultDestination != null) {
257                        return receiveAndConvert(defaultDestination, targetClass);
258                }
259                else {
260                        return receiveAndConvert(getRequiredDefaultDestinationName(), targetClass);
261                }
262        }
263
264        @Override
265        @Nullable
266        public Message<?> receive(String destinationName) throws MessagingException {
267                return doReceive(destinationName);
268        }
269
270        @Override
271        @Nullable
272        public <T> T receiveAndConvert(String destinationName, Class<T> targetClass) throws MessagingException {
273                Message<?> message = doReceive(destinationName);
274                if (message != null) {
275                        return doConvert(message, targetClass);
276                }
277                else {
278                        return null;
279                }
280        }
281
282        @Override
283        @Nullable
284        public Message<?> sendAndReceive(Message<?> requestMessage) {
285                Destination defaultDestination = getDefaultDestination();
286                if (defaultDestination != null) {
287                        return sendAndReceive(defaultDestination, requestMessage);
288                }
289                else {
290                        return sendAndReceive(getRequiredDefaultDestinationName(), requestMessage);
291                }
292        }
293
294        @Override
295        @Nullable
296        public Message<?> sendAndReceive(String destinationName, Message<?> requestMessage) throws MessagingException {
297                return doSendAndReceive(destinationName, requestMessage);
298        }
299
300        @Override
301        @Nullable
302        public <T> T convertSendAndReceive(String destinationName, Object request, Class<T> targetClass)
303                        throws MessagingException {
304
305                return convertSendAndReceive(destinationName, request, null, targetClass);
306        }
307
308        @Override
309        @Nullable
310        public <T> T convertSendAndReceive(Object request, Class<T> targetClass) {
311                return convertSendAndReceive(request, targetClass, null);
312        }
313
314        @Override
315        @Nullable
316        public <T> T convertSendAndReceive(String destinationName, Object request,
317                        @Nullable Map<String, Object> headers, Class<T> targetClass) throws MessagingException {
318
319                return convertSendAndReceive(destinationName, request, headers, targetClass, null);
320        }
321
322        @Override
323        @Nullable
324        public <T> T convertSendAndReceive(Object request, Class<T> targetClass, @Nullable MessagePostProcessor postProcessor) {
325                Destination defaultDestination = getDefaultDestination();
326                if (defaultDestination != null) {
327                        return convertSendAndReceive(defaultDestination, request, targetClass, postProcessor);
328                }
329                else {
330                        return convertSendAndReceive(getRequiredDefaultDestinationName(), request, targetClass, postProcessor);
331                }
332        }
333
334        @Override
335        @Nullable
336        public <T> T convertSendAndReceive(String destinationName, Object request, Class<T> targetClass,
337                        @Nullable MessagePostProcessor requestPostProcessor) throws MessagingException {
338
339                return convertSendAndReceive(destinationName, request, null, targetClass, requestPostProcessor);
340        }
341
342        @SuppressWarnings("unchecked")
343        @Override
344        @Nullable
345        public <T> T convertSendAndReceive(String destinationName, Object request, @Nullable Map<String, Object> headers,
346                        Class<T> targetClass, @Nullable MessagePostProcessor postProcessor) {
347
348                Message<?> requestMessage = doConvert(request, headers, postProcessor);
349                Message<?> replyMessage = sendAndReceive(destinationName, requestMessage);
350                return (replyMessage != null ? (T) getMessageConverter().fromMessage(replyMessage, targetClass) : null);
351        }
352
353        @Override
354        protected void doSend(Destination destination, Message<?> message) {
355                try {
356                        obtainJmsTemplate().send(destination, createMessageCreator(message));
357                }
358                catch (JmsException ex) {
359                        throw convertJmsException(ex);
360                }
361        }
362
363        protected void doSend(String destinationName, Message<?> message) {
364                try {
365                        obtainJmsTemplate().send(destinationName, createMessageCreator(message));
366                }
367                catch (JmsException ex) {
368                        throw convertJmsException(ex);
369                }
370        }
371
372        @Override
373        @Nullable
374        protected Message<?> doReceive(Destination destination) {
375                try {
376                        javax.jms.Message jmsMessage = obtainJmsTemplate().receive(destination);
377                        return convertJmsMessage(jmsMessage);
378                }
379                catch (JmsException ex) {
380                        throw convertJmsException(ex);
381                }
382        }
383
384        @Nullable
385        protected Message<?> doReceive(String destinationName) {
386                try {
387                        javax.jms.Message jmsMessage = obtainJmsTemplate().receive(destinationName);
388                        return convertJmsMessage(jmsMessage);
389                }
390                catch (JmsException ex) {
391                        throw convertJmsException(ex);
392                }
393        }
394
395        @Override
396        @Nullable
397        protected Message<?> doSendAndReceive(Destination destination, Message<?> requestMessage) {
398                try {
399                        javax.jms.Message jmsMessage = obtainJmsTemplate().sendAndReceive(
400                                        destination, createMessageCreator(requestMessage));
401                        return convertJmsMessage(jmsMessage);
402                }
403                catch (JmsException ex) {
404                        throw convertJmsException(ex);
405                }
406        }
407
408        @Nullable
409        protected Message<?> doSendAndReceive(String destinationName, Message<?> requestMessage) {
410                try {
411                        javax.jms.Message jmsMessage = obtainJmsTemplate().sendAndReceive(
412                                        destinationName, createMessageCreator(requestMessage));
413                        return convertJmsMessage(jmsMessage);
414                }
415                catch (JmsException ex) {
416                        throw convertJmsException(ex);
417                }
418        }
419
420        private MessagingMessageCreator createMessageCreator(Message<?> message) {
421                return new MessagingMessageCreator(message, getJmsMessageConverter());
422        }
423
424        protected String getRequiredDefaultDestinationName() {
425                String name = getDefaultDestinationName();
426                if (name == null) {
427                        throw new IllegalStateException("No 'defaultDestination' or 'defaultDestinationName' specified. " +
428                                        "Check configuration of JmsMessagingTemplate.");
429                }
430                return name;
431        }
432
433        @Nullable
434        protected Message<?> convertJmsMessage(@Nullable javax.jms.Message message) {
435                if (message == null) {
436                        return null;
437                }
438                try {
439                        return (Message<?>) getJmsMessageConverter().fromMessage(message);
440                }
441                catch (Exception ex) {
442                        throw new MessageConversionException("Could not convert '" + message + "'", ex);
443                }
444        }
445
446        protected MessagingException convertJmsException(JmsException ex) {
447                if (ex instanceof org.springframework.jms.support.destination.DestinationResolutionException ||
448                                ex instanceof InvalidDestinationException) {
449                        return new DestinationResolutionException(ex.getMessage(), ex);
450                }
451                if (ex instanceof org.springframework.jms.support.converter.MessageConversionException) {
452                        return new MessageConversionException(ex.getMessage(), ex);
453                }
454                // Fallback
455                return new MessagingException(ex.getMessage(), ex);
456        }
457
458
459        private static class MessagingMessageCreator implements MessageCreator {
460
461                private final Message<?> message;
462
463                private final MessageConverter messageConverter;
464
465                public MessagingMessageCreator(Message<?> message, MessageConverter messageConverter) {
466                        this.message = message;
467                        this.messageConverter = messageConverter;
468                }
469
470                @Override
471                public javax.jms.Message createMessage(Session session) throws JMSException {
472                        try {
473                                return this.messageConverter.toMessage(this.message, session);
474                        }
475                        catch (Exception ex) {
476                                throw new MessageConversionException("Could not convert '" + this.message + "'", ex);
477                        }
478                }
479        }
480
481}