提交 eba87306 编写于 作者: S Stephane Nicoll

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
上级 2d5496df
......@@ -39,6 +39,7 @@ import org.springframework.jms.support.converter.SimpleMessageConverter;
import org.springframework.jms.support.converter.SmartMessageConverter;
import org.springframework.jms.support.destination.DestinationResolver;
import org.springframework.jms.support.destination.DynamicDestinationResolver;
import org.springframework.messaging.MessageHeaders;
import org.springframework.util.Assert;
/**
......@@ -417,11 +418,21 @@ public abstract class AbstractAdaptableMessageListener
/**
* Delegates payload extraction to {@link #extractMessage(javax.jms.Message)} to
* enforce backward compatibility.
* A {@link MessagingMessageConverter} that lazily invoke payload extraction and
* delegate it to {@link #extractMessage(javax.jms.Message)} in order to enforce
* backward compatibility.
*/
private class MessagingMessageConverterAdapter extends MessagingMessageConverter {
@SuppressWarnings("unchecked")
@Override
public Object fromMessage(javax.jms.Message message) throws JMSException, MessageConversionException {
if (message == null) {
return null;
}
return new LazyResolutionMessage(message);
}
@Override
protected Object extractPayload(Message message) throws JMSException {
Object payload = extractMessage(message);
......@@ -452,6 +463,57 @@ public abstract class AbstractAdaptableMessageListener
}
return converter.toMessage(payload, session);
}
protected class LazyResolutionMessage implements org.springframework.messaging.Message<Object> {
private final javax.jms.Message message;
private Object payload;
private MessageHeaders headers;
public LazyResolutionMessage(javax.jms.Message message) {
this.message = message;
}
@Override
public Object getPayload() {
if (this.payload == null) {
try {
this.payload = unwrapPayload();
}
catch (JMSException ex) {
throw new MessageConversionException(
"Failed to extract payload from [" + this.message + "]", ex);
}
}
//
return this.payload;
}
/**
* Extract the payload of the current message. Since we deferred the resolution
* of the payload, a custom converter may still return a full message for it. In
* this case, its payload is returned.
* @return the payload of the message
*/
private Object unwrapPayload() throws JMSException {
Object payload = extractPayload(this.message);
if (payload instanceof org.springframework.messaging.Message) {
return ((org.springframework.messaging.Message) payload).getPayload();
}
return payload;
}
@Override
public MessageHeaders getHeaders() {
if (this.headers == null) {
this.headers = extractHeaders(this.message);
}
return this.headers;
}
}
}
......
......@@ -16,6 +16,7 @@
package org.springframework.jms.support.converter;
import java.util.Map;
import javax.jms.JMSException;
import javax.jms.Session;
......@@ -25,6 +26,7 @@ import org.springframework.jms.support.SimpleJmsHeaderMapper;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.core.AbstractMessagingTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.Assert;
/**
......@@ -107,7 +109,12 @@ public class MessagingMessageConverter implements MessageConverter, Initializing
if (message == null) {
return null;
}
return new LazyResolutionMessage(message);
Map<String, Object> mappedHeaders = extractHeaders(message);
Object convertedObject = extractPayload(message);
MessageBuilder<Object> builder = (convertedObject instanceof org.springframework.messaging.Message) ?
MessageBuilder.fromMessage((org.springframework.messaging.Message<Object>) convertedObject) :
MessageBuilder.withPayload(convertedObject);
return builder.copyHeadersIfAbsent(mappedHeaders).build();
}
/**
......@@ -141,44 +148,11 @@ public class MessagingMessageConverter implements MessageConverter, Initializing
return createMessageForPayload(payload, session);
}
private MessageHeaders extractHeaders(javax.jms.Message message) {
protected final MessageHeaders extractHeaders(javax.jms.Message message) {
return this.headerMapper.toHeaders(message);
}
private class LazyResolutionMessage implements Message<Object> {
private final javax.jms.Message message;
private Object payload;
private MessageHeaders headers;
public LazyResolutionMessage(javax.jms.Message message) {
this.message = message;
}
@Override
public Object getPayload() {
if (this.payload == null) {
try {
this.payload = extractPayload(this.message);
}
catch (JMSException ex) {
throw new MessageConversionException(
"Failed to extract payload from [" + this.message + "]", ex);
}
}
return this.payload;
}
@Override
public MessageHeaders getHeaders() {
if (this.headers == null) {
this.headers = extractHeaders(this.message);
}
return this.headers;
}
}
}
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -35,6 +35,7 @@ import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.jms.listener.SessionAwareMessageListener;
import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessagingMessageConverter;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
......@@ -65,10 +66,21 @@ public class JmsListenerContainerFactoryIntegrationTests {
@Test
public void messageConverterUsedIfSet() throws JMSException {
containerFactory.setMessageConverter(new UpperCaseMessageConverter());
this.containerFactory.setMessageConverter(new UpperCaseMessageConverter());
testMessageConverterIsUsed();
}
@Test
public void messagingMessageConverterCanBeUsed() throws JMSException {
MessagingMessageConverter converter = new MessagingMessageConverter();
converter.setPayloadConverter(new UpperCaseMessageConverter());
this.containerFactory.setMessageConverter(converter);
testMessageConverterIsUsed();
}
private void testMessageConverterIsUsed() throws JMSException {
MethodJmsListenerEndpoint endpoint = createDefaultMethodJmsEndpoint(
listener.getClass(), "handleIt", String.class, String.class);
this.listener.getClass(), "handleIt", String.class, String.class);
Message message = new StubTextMessage("foo-bar");
message.setStringProperty("my-header", "my-value");
......
......@@ -29,7 +29,9 @@ import javax.jms.Topic;
import com.fasterxml.jackson.annotation.JsonView;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.jms.StubTextMessage;
......@@ -51,6 +53,9 @@ import static org.mockito.BDDMockito.*;
*/
public class MessagingMessageListenerAdapterTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private static final Destination sharedReplyDestination = mock(Destination.class);
private final DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
......@@ -122,6 +127,31 @@ public class MessagingMessageListenerAdapterTests {
}
}
@Test
public void payloadConversionLazilyInvoked() throws JMSException {
javax.jms.Message jmsMessage = mock(javax.jms.Message.class);
MessageConverter messageConverter = mock(MessageConverter.class);
given(messageConverter.fromMessage(jmsMessage)).willReturn("FooBar");
MessagingMessageListenerAdapter listener = getSimpleInstance("simple", Message.class);
listener.setMessageConverter(messageConverter);
Message<?> message = listener.toMessagingMessage(jmsMessage);
verify(messageConverter, never()).fromMessage(jmsMessage);
assertEquals("FooBar", message.getPayload());
verify(messageConverter, times(1)).fromMessage(jmsMessage);
}
@Test
public void headerConversionLazilyInvoked() throws JMSException {
javax.jms.Message jmsMessage = mock(javax.jms.Message.class);
when(jmsMessage.getPropertyNames()).thenThrow(new IllegalArgumentException("Header failure"));
MessagingMessageListenerAdapter listener = getSimpleInstance("simple", Message.class);
Message<?> message = listener.toMessagingMessage(jmsMessage);
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Header failure");
message.getHeaders(); // Triggers headers resolution
}
@Test
public void incomingMessageUsesMessageConverter() throws JMSException {
javax.jms.Message jmsMessage = mock(javax.jms.Message.class);
......
......@@ -74,30 +74,6 @@ public class MessagingMessageConverterTests {
assertEquals(1224L, msg.getPayload());
}
@Test
public void payloadConversionLazilyInvoked() throws JMSException {
TextMessage jmsMsg = new StubTextMessage("1224");
TestMessageConverter converter = new TestMessageConverter();
this.converter.setPayloadConverter(converter);
Message<?> msg = (Message<?>) this.converter.fromMessage(jmsMsg);
assertEquals("Converter should not have been called yet", false, converter.called);
assertEquals(1224L, msg.getPayload());
assertEquals("Converter should have been called", true, converter.called);
}
@Test
public void headerConversionLazilyInvoked() throws JMSException {
javax.jms.Message jmsMsg = mock(javax.jms.Message.class);
when(jmsMsg.getPropertyNames()).thenThrow(new IllegalArgumentException("Header failure"));
Message<?> msg = (Message<?>) this.converter.fromMessage(jmsMsg);
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Header failure");
msg.getHeaders(); // Triggers headers resolution
}
static class TestMessageConverter extends SimpleMessageConverter {
private boolean called;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册