Skip to content

Commit eba8730

Browse files
committed
Better hide lazy resolution of JMS payloads
This commit fixes MessagingMessageConverter to no longer expose the lazy message resolution algorithm. This restores proper behaviour for converters used outside of that context. Instead, such arrangement is now private to AbstractAdaptableMessageListener (as it should). Issue: SPR-14389
1 parent 2d5496d commit eba8730

File tree

5 files changed

+118
-64
lines changed

5 files changed

+118
-64
lines changed

spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.jms.support.converter.SmartMessageConverter;
4040
import org.springframework.jms.support.destination.DestinationResolver;
4141
import org.springframework.jms.support.destination.DynamicDestinationResolver;
42+
import org.springframework.messaging.MessageHeaders;
4243
import org.springframework.util.Assert;
4344

4445
/**
@@ -417,11 +418,21 @@ protected void postProcessProducer(MessageProducer producer, Message response) t
417418

418419

419420
/**
420-
* Delegates payload extraction to {@link #extractMessage(javax.jms.Message)} to
421-
* enforce backward compatibility.
421+
* A {@link MessagingMessageConverter} that lazily invoke payload extraction and
422+
* delegate it to {@link #extractMessage(javax.jms.Message)} in order to enforce
423+
* backward compatibility.
422424
*/
423425
private class MessagingMessageConverterAdapter extends MessagingMessageConverter {
424426

427+
@SuppressWarnings("unchecked")
428+
@Override
429+
public Object fromMessage(javax.jms.Message message) throws JMSException, MessageConversionException {
430+
if (message == null) {
431+
return null;
432+
}
433+
return new LazyResolutionMessage(message);
434+
}
435+
425436
@Override
426437
protected Object extractPayload(Message message) throws JMSException {
427438
Object payload = extractMessage(message);
@@ -452,6 +463,57 @@ protected Message createMessageForPayload(Object payload, Session session, Objec
452463
}
453464
return converter.toMessage(payload, session);
454465
}
466+
467+
protected class LazyResolutionMessage implements org.springframework.messaging.Message<Object> {
468+
469+
private final javax.jms.Message message;
470+
471+
private Object payload;
472+
473+
private MessageHeaders headers;
474+
475+
public LazyResolutionMessage(javax.jms.Message message) {
476+
this.message = message;
477+
}
478+
479+
@Override
480+
public Object getPayload() {
481+
if (this.payload == null) {
482+
try {
483+
this.payload = unwrapPayload();
484+
}
485+
catch (JMSException ex) {
486+
throw new MessageConversionException(
487+
"Failed to extract payload from [" + this.message + "]", ex);
488+
}
489+
}
490+
//
491+
return this.payload;
492+
}
493+
494+
/**
495+
* Extract the payload of the current message. Since we deferred the resolution
496+
* of the payload, a custom converter may still return a full message for it. In
497+
* this case, its payload is returned.
498+
* @return the payload of the message
499+
*/
500+
private Object unwrapPayload() throws JMSException {
501+
Object payload = extractPayload(this.message);
502+
if (payload instanceof org.springframework.messaging.Message) {
503+
return ((org.springframework.messaging.Message) payload).getPayload();
504+
}
505+
return payload;
506+
}
507+
508+
@Override
509+
public MessageHeaders getHeaders() {
510+
if (this.headers == null) {
511+
this.headers = extractHeaders(this.message);
512+
}
513+
return this.headers;
514+
}
515+
}
516+
455517
}
456518

457519

spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.jms.support.converter;
1818

19+
import java.util.Map;
1920
import javax.jms.JMSException;
2021
import javax.jms.Session;
2122

@@ -25,6 +26,7 @@
2526
import org.springframework.messaging.Message;
2627
import org.springframework.messaging.MessageHeaders;
2728
import org.springframework.messaging.core.AbstractMessagingTemplate;
29+
import org.springframework.messaging.support.MessageBuilder;
2830
import org.springframework.util.Assert;
2931

3032
/**
@@ -107,7 +109,12 @@ public Object fromMessage(javax.jms.Message message) throws JMSException, Messag
107109
if (message == null) {
108110
return null;
109111
}
110-
return new LazyResolutionMessage(message);
112+
Map<String, Object> mappedHeaders = extractHeaders(message);
113+
Object convertedObject = extractPayload(message);
114+
MessageBuilder<Object> builder = (convertedObject instanceof org.springframework.messaging.Message) ?
115+
MessageBuilder.fromMessage((org.springframework.messaging.Message<Object>) convertedObject) :
116+
MessageBuilder.withPayload(convertedObject);
117+
return builder.copyHeadersIfAbsent(mappedHeaders).build();
111118
}
112119

113120
/**
@@ -141,44 +148,11 @@ protected javax.jms.Message createMessageForPayload(Object payload, Session sess
141148
return createMessageForPayload(payload, session);
142149
}
143150

144-
private MessageHeaders extractHeaders(javax.jms.Message message) {
151+
protected final MessageHeaders extractHeaders(javax.jms.Message message) {
145152
return this.headerMapper.toHeaders(message);
146153
}
147154

148155

149-
private class LazyResolutionMessage implements Message<Object> {
150156

151-
private final javax.jms.Message message;
152-
153-
private Object payload;
154-
155-
private MessageHeaders headers;
156-
157-
public LazyResolutionMessage(javax.jms.Message message) {
158-
this.message = message;
159-
}
160-
161-
@Override
162-
public Object getPayload() {
163-
if (this.payload == null) {
164-
try {
165-
this.payload = extractPayload(this.message);
166-
}
167-
catch (JMSException ex) {
168-
throw new MessageConversionException(
169-
"Failed to extract payload from [" + this.message + "]", ex);
170-
}
171-
}
172-
return this.payload;
173-
}
174-
175-
@Override
176-
public MessageHeaders getHeaders() {
177-
if (this.headers == null) {
178-
this.headers = extractHeaders(this.message);
179-
}
180-
return this.headers;
181-
}
182-
}
183157

184158
}

spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerFactoryIntegrationTests.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
3535
import org.springframework.jms.listener.SessionAwareMessageListener;
3636
import org.springframework.jms.support.converter.MessageConversionException;
3737
import org.springframework.jms.support.converter.MessageConverter;
38+
import org.springframework.jms.support.converter.MessagingMessageConverter;
3839
import org.springframework.messaging.handler.annotation.Header;
3940
import org.springframework.messaging.handler.annotation.Payload;
4041
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
@@ -65,10 +66,21 @@ public void setup() {
6566

6667
@Test
6768
public void messageConverterUsedIfSet() throws JMSException {
68-
containerFactory.setMessageConverter(new UpperCaseMessageConverter());
69+
this.containerFactory.setMessageConverter(new UpperCaseMessageConverter());
70+
testMessageConverterIsUsed();
71+
}
72+
73+
@Test
74+
public void messagingMessageConverterCanBeUsed() throws JMSException {
75+
MessagingMessageConverter converter = new MessagingMessageConverter();
76+
converter.setPayloadConverter(new UpperCaseMessageConverter());
77+
this.containerFactory.setMessageConverter(converter);
78+
testMessageConverterIsUsed();
79+
}
6980

81+
private void testMessageConverterIsUsed() throws JMSException {
7082
MethodJmsListenerEndpoint endpoint = createDefaultMethodJmsEndpoint(
71-
listener.getClass(), "handleIt", String.class, String.class);
83+
this.listener.getClass(), "handleIt", String.class, String.class);
7284
Message message = new StubTextMessage("foo-bar");
7385
message.setStringProperty("my-header", "my-value");
7486

spring-jms/src/test/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapterTests.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929

3030
import com.fasterxml.jackson.annotation.JsonView;
3131
import org.junit.Before;
32+
import org.junit.Rule;
3233
import org.junit.Test;
34+
import org.junit.rules.ExpectedException;
3335

3436
import org.springframework.beans.factory.support.StaticListableBeanFactory;
3537
import org.springframework.jms.StubTextMessage;
@@ -51,6 +53,9 @@
5153
*/
5254
public class MessagingMessageListenerAdapterTests {
5355

56+
@Rule
57+
public final ExpectedException thrown = ExpectedException.none();
58+
5459
private static final Destination sharedReplyDestination = mock(Destination.class);
5560

5661
private final DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
@@ -122,6 +127,31 @@ public void exceptionInInvocation() {
122127
}
123128
}
124129

130+
@Test
131+
public void payloadConversionLazilyInvoked() throws JMSException {
132+
javax.jms.Message jmsMessage = mock(javax.jms.Message.class);
133+
MessageConverter messageConverter = mock(MessageConverter.class);
134+
given(messageConverter.fromMessage(jmsMessage)).willReturn("FooBar");
135+
MessagingMessageListenerAdapter listener = getSimpleInstance("simple", Message.class);
136+
listener.setMessageConverter(messageConverter);
137+
Message<?> message = listener.toMessagingMessage(jmsMessage);
138+
verify(messageConverter, never()).fromMessage(jmsMessage);
139+
assertEquals("FooBar", message.getPayload());
140+
verify(messageConverter, times(1)).fromMessage(jmsMessage);
141+
}
142+
143+
@Test
144+
public void headerConversionLazilyInvoked() throws JMSException {
145+
javax.jms.Message jmsMessage = mock(javax.jms.Message.class);
146+
when(jmsMessage.getPropertyNames()).thenThrow(new IllegalArgumentException("Header failure"));
147+
MessagingMessageListenerAdapter listener = getSimpleInstance("simple", Message.class);
148+
Message<?> message = listener.toMessagingMessage(jmsMessage);
149+
150+
this.thrown.expect(IllegalArgumentException.class);
151+
this.thrown.expectMessage("Header failure");
152+
message.getHeaders(); // Triggers headers resolution
153+
}
154+
125155
@Test
126156
public void incomingMessageUsesMessageConverter() throws JMSException {
127157
javax.jms.Message jmsMessage = mock(javax.jms.Message.class);

spring-jms/src/test/java/org/springframework/jms/support/converter/MessagingMessageConverterTests.java

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -74,30 +74,6 @@ public void customPayloadConverter() throws JMSException {
7474
assertEquals(1224L, msg.getPayload());
7575
}
7676

77-
@Test
78-
public void payloadConversionLazilyInvoked() throws JMSException {
79-
TextMessage jmsMsg = new StubTextMessage("1224");
80-
TestMessageConverter converter = new TestMessageConverter();
81-
this.converter.setPayloadConverter(converter);
82-
Message<?> msg = (Message<?>) this.converter.fromMessage(jmsMsg);
83-
assertEquals("Converter should not have been called yet", false, converter.called);
84-
assertEquals(1224L, msg.getPayload());
85-
assertEquals("Converter should have been called", true, converter.called);
86-
}
87-
88-
@Test
89-
public void headerConversionLazilyInvoked() throws JMSException {
90-
javax.jms.Message jmsMsg = mock(javax.jms.Message.class);
91-
when(jmsMsg.getPropertyNames()).thenThrow(new IllegalArgumentException("Header failure"));
92-
93-
Message<?> msg = (Message<?>) this.converter.fromMessage(jmsMsg);
94-
95-
this.thrown.expect(IllegalArgumentException.class);
96-
this.thrown.expectMessage("Header failure");
97-
msg.getHeaders(); // Triggers headers resolution
98-
}
99-
100-
10177
static class TestMessageConverter extends SimpleMessageConverter {
10278

10379
private boolean called;

0 commit comments

Comments
 (0)