/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.jms.listener.adapter;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import javax.jms.BytesMessage;
import javax.jms.IllegalStateException;
import javax.jms.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.SimpleMessageConverter;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/**
* @author Rick Evans
* @author Juergen Hoeller
* @author Chris Beams
*/
public class MessageListenerAdapterTests {
private static final String TEXT = "I fancy a good cuppa right now";
private static final Integer NUMBER = new Integer(1);
private static final SerializableObject OBJECT = new SerializableObject();
private static final String CORRELATION_ID = "100";
private static final String RESPONSE_TEXT = "... wi' some full fat creamy milk. Top banana.";
@Test
public void testWithMessageContentsDelegateForTextMessage() throws Exception {
TextMessage textMessage = mock(TextMessage.class);
// TextMessage contents must be unwrapped...
given(textMessage.getText()).willReturn(TEXT);
MessageContentsDelegate delegate = mock(MessageContentsDelegate.class);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate);
adapter.onMessage(textMessage);
verify(delegate).handleMessage(TEXT);
}
@Test
public void testWithMessageContentsDelegateForBytesMessage() throws Exception {
BytesMessage bytesMessage = mock(BytesMessage.class);
// BytesMessage contents must be unwrapped...
given(bytesMessage.getBodyLength()).willReturn(new Long(TEXT.getBytes().length));
given(bytesMessage.readBytes(any(byte[].class))).willAnswer(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocation) throws Throwable {
byte[] bytes = (byte[]) invocation.getArguments()[0];
ByteArrayInputStream inputStream = new ByteArrayInputStream(TEXT.getBytes());
return inputStream.read(bytes);
}
});
MessageContentsDelegate delegate = mock(MessageContentsDelegate.class);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate);
adapter.onMessage(bytesMessage);
verify(delegate).handleMessage(TEXT.getBytes());
}
@Test
public void testWithMessageContentsDelegateForObjectMessage() throws Exception {
ObjectMessage objectMessage = mock(ObjectMessage.class);
given(objectMessage.getObject()).willReturn(NUMBER);
MessageContentsDelegate delegate = mock(MessageContentsDelegate.class);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate);
adapter.onMessage(objectMessage);
verify(delegate).handleMessage(NUMBER);
}
@Test
public void testWithMessageContentsDelegateForObjectMessageWithPlainObject() throws Exception {
ObjectMessage objectMessage = mock(ObjectMessage.class);
given(objectMessage.getObject()).willReturn(OBJECT);
MessageContentsDelegate delegate = mock(MessageContentsDelegate.class);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate);
adapter.onMessage(objectMessage);
verify(delegate).handleMessage(OBJECT);
}
@Test
public void testWithMessageDelegate() throws Exception {
TextMessage textMessage = mock(TextMessage.class);
MessageDelegate delegate = mock(MessageDelegate.class);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate);
// we DON'T want the default SimpleMessageConversion happening...
adapter.setMessageConverter(null);
adapter.onMessage(textMessage);
verify(delegate).handleMessage(textMessage);
}
@Test
public void testWhenTheAdapterItselfIsTheDelegate() throws Exception {
TextMessage textMessage = mock(TextMessage.class);
// TextMessage contents must be unwrapped...
given(textMessage.getText()).willReturn(TEXT);
StubMessageListenerAdapter adapter = new StubMessageListenerAdapter();
adapter.onMessage(textMessage);
assertTrue(adapter.wasCalled());
}
@Test
public void testRainyDayWithNoApplicableHandlingMethods() throws Exception {
TextMessage textMessage = mock(TextMessage.class);
// TextMessage contents must be unwrapped...
given(textMessage.getText()).willReturn(TEXT);
StubMessageListenerAdapter adapter = new StubMessageListenerAdapter();
adapter.setDefaultListenerMethod("walnutsRock");
adapter.onMessage(textMessage);
assertFalse(adapter.wasCalled());
}
@Test
public void testThatAnExceptionThrownFromTheHandlingMethodIsSimplySwallowedByDefault() throws Exception {
final IllegalArgumentException exception = new IllegalArgumentException();
TextMessage textMessage = mock(TextMessage.class);
MessageDelegate delegate = mock(MessageDelegate.class);
willThrow(exception).given(delegate).handleMessage(textMessage);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate) {
@Override
protected void handleListenerException(Throwable ex) {
assertNotNull("The Throwable passed to the handleListenerException(..) method must never be null.", ex);
assertTrue("The Throwable passed to the handleListenerException(..) method must be of type [ListenerExecutionFailedException].",
ex instanceof ListenerExecutionFailedException);
ListenerExecutionFailedException lefx = (ListenerExecutionFailedException) ex;
Throwable cause = lefx.getCause();
assertNotNull("The cause of a ListenerExecutionFailedException must be preserved.", cause);
assertSame(exception, cause);
}
};
// we DON'T want the default SimpleMessageConversion happening...
adapter.setMessageConverter(null);
adapter.onMessage(textMessage);
}
@Test
public void testThatTheDefaultMessageConverterisIndeedTheSimpleMessageConverter() throws Exception {
MessageListenerAdapter adapter = new MessageListenerAdapter();
assertNotNull("The default [MessageConverter] must never be null.", adapter.getMessageConverter());
assertTrue("The default [MessageConverter] must be of the type [SimpleMessageConverter]",
adapter.getMessageConverter() instanceof SimpleMessageConverter);
}
@Test
public void testThatWhenNoDelegateIsSuppliedTheDelegateIsAssumedToBeTheMessageListenerAdapterItself() throws Exception {
MessageListenerAdapter adapter = new MessageListenerAdapter();
assertSame(adapter, adapter.getDelegate());
}
@Test
public void testThatTheDefaultMessageHandlingMethodNameIsTheConstantDefault() throws Exception {
MessageListenerAdapter adapter = new MessageListenerAdapter();
assertEquals(MessageListenerAdapter.ORIGINAL_DEFAULT_LISTENER_METHOD, adapter.getDefaultListenerMethod());
}
@Test
public void testWithResponsiveMessageDelegate_DoesNotSendReturnTextMessageIfNoSessionSupplied() throws Exception {
TextMessage textMessage = mock(TextMessage.class);
ResponsiveMessageDelegate delegate = mock(ResponsiveMessageDelegate.class);
given(delegate.handleMessage(textMessage)).willReturn(TEXT);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate);
// we DON'T want the default SimpleMessageConversion happening...
adapter.setMessageConverter(null);
adapter.onMessage(textMessage);
}
@Test
public void testWithResponsiveMessageDelegateWithDefaultDestination_SendsReturnTextMessageWhenSessionSupplied() throws Exception {
Queue destination = mock(Queue.class);
TextMessage sentTextMessage = mock(TextMessage.class);
// correlation ID is queried when response is being created...
given(sentTextMessage.getJMSCorrelationID()).willReturn(
CORRELATION_ID);
// Reply-To is queried when response is being created...
given(sentTextMessage.getJMSReplyTo()).willReturn(null); // we want to fall back to the default...
TextMessage responseTextMessage = mock(TextMessage.class);
QueueSender queueSender = mock(QueueSender.class);
Session session = mock(Session.class);
given(session.createTextMessage(RESPONSE_TEXT)).willReturn(responseTextMessage);
given(session.createProducer(destination)).willReturn(queueSender);
ResponsiveMessageDelegate delegate = mock(ResponsiveMessageDelegate.class);
given(delegate.handleMessage(sentTextMessage)).willReturn(RESPONSE_TEXT);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate) {
@Override
protected Object extractMessage(Message message) {
return message;
}
};
adapter.setDefaultResponseDestination(destination);
adapter.onMessage(sentTextMessage, session);
verify(responseTextMessage).setJMSCorrelationID(CORRELATION_ID);
verify(queueSender).send(responseTextMessage);
verify(queueSender).close();
verify(delegate).handleMessage(sentTextMessage);
}
@Test
public void testWithResponsiveMessageDelegateNoDefaultDestination_SendsReturnTextMessageWhenSessionSupplied() throws Exception {
Queue destination = mock(Queue.class);
TextMessage sentTextMessage = mock(TextMessage.class);
// correlation ID is queried when response is being created...
given(sentTextMessage.getJMSCorrelationID()).willReturn(null);
given(sentTextMessage.getJMSMessageID()).willReturn(CORRELATION_ID);
// Reply-To is queried when response is being created...
given(sentTextMessage.getJMSReplyTo()).willReturn(destination);
TextMessage responseTextMessage = mock(TextMessage.class);
MessageProducer messageProducer = mock(MessageProducer.class);
Session session = mock(Session.class);
given(session.createTextMessage(RESPONSE_TEXT)).willReturn(responseTextMessage);
given(session.createProducer(destination)).willReturn(messageProducer);
ResponsiveMessageDelegate delegate = mock(ResponsiveMessageDelegate.class);
given(delegate.handleMessage(sentTextMessage)).willReturn(RESPONSE_TEXT);
MessageListenerAdapter adapter = new MessageListenerAdapter(delegate) {
@Override
protected Object extractMessage(Message message) {
return message;
}
};
adapter.onMessage(sentTextMessage, session);
verify(responseTextMessage).setJMSCorrelationID(CORRELATION_ID);
verify(messageProducer).send(responseTextMessage);
verify(messageProducer).close();
verify(delegate).handleMessage(sentTextMessage);
}
@Test
public void testWithResponsiveMessageDelegateNoDefaultDestinationAndNoReplyToDestination_SendsReturnTextMessageWhenSessionSupplied() throws Exception {
final TextMessage sentTextMessage = mock(TextMessage.class);
// correlation ID is queried when response is being created...
given(sentTextMessage.getJMSCorrelationID()).willReturn(CORRELATION_ID);
// Reply-To is queried when response is being created...
given(sentTextMessage.getJMSReplyTo()).willReturn(null);
TextMessage responseTextMessage = mock(TextMessage.class);
final QueueSession session = mock(QueueSession.class);
given(session.createTextMessage(RESPONSE_TEXT)).willReturn(responseTextMessage);
ResponsiveMessageDelegate delegate = mock(ResponsiveMessageDelegate.class);
given(delegate.handleMessage(sentTextMessage)).willReturn(RESPONSE_TEXT);
final MessageListenerAdapter adapter = new MessageListenerAdapter(delegate) {
@Override
protected Object extractMessage(Message message) {
return message;
}
};
try {
adapter.onMessage(sentTextMessage, session);
fail("expected CouldNotSendReplyException with InvalidDestinationException");
}
catch (ReplyFailureException ex) {
assertEquals(InvalidDestinationException.class, ex.getCause().getClass());
}
verify(responseTextMessage).setJMSCorrelationID(CORRELATION_ID);
verify(delegate).handleMessage(sentTextMessage);
}
@Test
public void testWithResponsiveMessageDelegateNoDefaultDestination_SendsReturnTextMessageWhenSessionSupplied_AndSendingThrowsJMSException() throws Exception {
Queue destination = mock(Queue.class);
final TextMessage sentTextMessage = mock(TextMessage.class);
// correlation ID is queried when response is being created...
given(sentTextMessage.getJMSCorrelationID()).willReturn(CORRELATION_ID);
// Reply-To is queried when response is being created...
given(sentTextMessage.getJMSReplyTo()).willReturn(destination);
TextMessage responseTextMessage = mock(TextMessage.class);
MessageProducer messageProducer = mock(MessageProducer.class);
willThrow(new JMSException("Doe!")).given(messageProducer).send(responseTextMessage);
final QueueSession session = mock(QueueSession.class);
given(session.createTextMessage(RESPONSE_TEXT)).willReturn(responseTextMessage);
given(session.createProducer(destination)).willReturn(messageProducer);
ResponsiveMessageDelegate delegate = mock(ResponsiveMessageDelegate.class);
given(delegate.handleMessage(sentTextMessage)).willReturn(RESPONSE_TEXT);
final MessageListenerAdapter adapter = new MessageListenerAdapter(delegate) {
@Override
protected Object extractMessage(Message message) {
return message;
}
};
try {
adapter.onMessage(sentTextMessage, session);
fail("expected CouldNotSendReplyException with JMSException");
}
catch (ReplyFailureException ex) {
assertEquals(JMSException.class, ex.getCause().getClass());
}
verify(responseTextMessage).setJMSCorrelationID(CORRELATION_ID);
verify(messageProducer).close();
verify(delegate).handleMessage(sentTextMessage);
}
@Test
public void testWithResponsiveMessageDelegateDoesNotSendReturnTextMessageWhenSessionSupplied_AndListenerMethodThrowsException() throws Exception {
final TextMessage message = mock(TextMessage.class);
final QueueSession session = mock(QueueSession.class);
ResponsiveMessageDelegate delegate = mock(ResponsiveMessageDelegate.class);
willThrow(new IllegalArgumentException("Doe!")).given(delegate).handleMessage(message);
final MessageListenerAdapter adapter = new MessageListenerAdapter(delegate) {
@Override
protected Object extractMessage(Message message) {
return message;
}
};
try {
adapter.onMessage(message, session);
fail("expected ListenerExecutionFailedException");
}
catch (ListenerExecutionFailedException ex) { /* expected */ }
}
@Test
public void testFailsIfNoDefaultListenerMethodNameIsSupplied() throws Exception {
final TextMessage message = mock(TextMessage.class);
given(message.getText()).willReturn(TEXT);
final MessageListenerAdapter adapter = new MessageListenerAdapter() {
@Override
protected void handleListenerException(Throwable ex) {
assertTrue(ex instanceof IllegalStateException);
}
};
adapter.setDefaultListenerMethod(null);
adapter.onMessage(message);
}
@Test
public void testFailsWhenOverriddenGetListenerMethodNameReturnsNull() throws Exception {
final TextMessage message = mock(TextMessage.class);
given(message.getText()).willReturn(TEXT);
final MessageListenerAdapter adapter = new MessageListenerAdapter() {
@Override
protected void handleListenerException(Throwable ex) {
assertTrue(ex instanceof javax.jms.IllegalStateException);
}
@Override
protected String getListenerMethodName(Message originalMessage, Object extractedMessage) {
return null;
}
};
adapter.setDefaultListenerMethod(null);
adapter.onMessage(message);
}
@Test
public void testWithResponsiveMessageDelegateWhenReturnTypeIsNotAJMSMessageAndNoMessageConverterIsSupplied() throws Exception {
final TextMessage sentTextMessage = mock(TextMessage.class);
final Session session = mock(Session.class);
ResponsiveMessageDelegate delegate = mock(ResponsiveMessageDelegate.class);
given(delegate.handleMessage(sentTextMessage)).willReturn(RESPONSE_TEXT);
final MessageListenerAdapter adapter = new MessageListenerAdapter(delegate) {
@Override
protected Object extractMessage(Message message) {
return message;
}
};
adapter.setMessageConverter(null);
try {
adapter.onMessage(sentTextMessage, session);
fail("expected CouldNotSendReplyException with MessageConversionException");
}
catch (ReplyFailureException ex) {
assertEquals(MessageConversionException.class, ex.getCause().getClass());
}
}
@Test
public void testWithResponsiveMessageDelegateWhenReturnTypeIsAJMSMessageAndNoMessageConverterIsSupplied() throws Exception {
Queue destination = mock(Queue.class);
final TextMessage sentTextMessage = mock(TextMessage.class);
// correlation ID is queried when response is being created...
given(sentTextMessage.getJMSCorrelationID()).willReturn(CORRELATION_ID);
// Reply-To is queried when response is being created...
given(sentTextMessage.getJMSReplyTo()).willReturn(destination);
TextMessage responseMessage = mock(TextMessage.class);
QueueSender queueSender = mock(QueueSender.class);
Session session = mock(Session.class);
given(session.createProducer(destination)).willReturn(queueSender);
ResponsiveJmsTextMessageReturningMessageDelegate delegate = mock(ResponsiveJmsTextMessageReturningMessageDelegate.class);
given(delegate.handleMessage(sentTextMessage)).willReturn(responseMessage);
final MessageListenerAdapter adapter = new MessageListenerAdapter(delegate) {
@Override
protected Object extractMessage(Message message) {
return message;
}
};
adapter.setMessageConverter(null);
adapter.onMessage(sentTextMessage, session);
verify(responseMessage).setJMSCorrelationID(CORRELATION_ID);
verify(queueSender).send(responseMessage);
verify(queueSender).close();
}
@SuppressWarnings("serial")
private static class SerializableObject implements Serializable {
}
}