/* * Copyright 2006-2007 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.batch.container.jms; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.Session; import org.aopalliance.aop.Advice; import org.junit.Test; import org.springframework.batch.repeat.interceptor.RepeatOperationsInterceptor; import org.springframework.batch.repeat.policy.SimpleCompletionPolicy; import org.springframework.batch.repeat.support.RepeatTemplate; import org.springframework.util.ReflectionUtils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class BatchMessageListenerContainerTests { BatchMessageListenerContainer container; @Test public void testReceiveAndExecuteWithCallback() throws Exception { RepeatTemplate template = new RepeatTemplate(); template.setCompletionPolicy(new SimpleCompletionPolicy(2)); container = getContainer(template); container.setMessageListener(new MessageListener() { @Override public void onMessage(Message arg0) { } }); Session session = mock(Session.class); MessageConsumer consumer = mock(MessageConsumer.class); Message message = mock(Message.class); // Expect two calls to consumer (chunk size)... when(session.getTransacted()).thenReturn(true); when(session.getTransacted()).thenReturn(true); when(consumer.receive(1000)).thenReturn(message); boolean received = doExecute(session, consumer); assertTrue("Message not received", received); } @Test public void testReceiveAndExecuteWithCallbackReturningNull() throws Exception { RepeatTemplate template = new RepeatTemplate(); template.setCompletionPolicy(new SimpleCompletionPolicy(2)); container = getContainer(template); Session session = mock(Session.class); MessageConsumer consumer = mock(MessageConsumer.class); Message message = null; // Expect one call to consumer (chunk size is 2 but terminates on // first)... when(consumer.receive(1000)).thenReturn(message); when(session.getTransacted()).thenReturn(false); boolean received = doExecute(session, consumer); assertFalse("Message not received", received); } @Test public void testTransactionalReceiveAndExecuteWithCallbackThrowingException() throws Exception { RepeatTemplate template = new RepeatTemplate(); template.setCompletionPolicy(new SimpleCompletionPolicy(2)); container = getContainer(template); container.setSessionTransacted(true); try { boolean received = doTestWithException(new IllegalStateException("No way!"), true, 2); assertFalse("Message received", received); fail("Expected IllegalStateException"); } catch (IllegalStateException e) { assertEquals("No way!", e.getMessage()); } } @Test public void testNonTransactionalReceiveAndExecuteWithCallbackThrowingException() throws Exception { RepeatTemplate template = new RepeatTemplate(); template.setCompletionPolicy(new SimpleCompletionPolicy(2)); container = getContainer(template); container.setSessionTransacted(false); boolean received = doTestWithException(new IllegalStateException("No way!"), false, 2); assertTrue("Message not received but listener not transactional so this should be true", received); } @Test public void testNonTransactionalReceiveAndExecuteWithCallbackThrowingError() throws Exception { RepeatTemplate template = new RepeatTemplate(); template.setCompletionPolicy(new SimpleCompletionPolicy(2)); container = getContainer(template); container.setSessionTransacted(false); try { boolean received = doTestWithException(new RuntimeException("No way!"), false, 2); assertTrue("Message not received but listener not transactional so this should be true", received); } catch (RuntimeException e) { assertEquals("No way!", e.getMessage()); fail("Unexpected Error - should be swallowed"); } } private BatchMessageListenerContainer getContainer(RepeatTemplate template) { ConnectionFactory connectionFactory = mock(ConnectionFactory.class); // Yuck: we need to turn these method in base class to no-ops because the invoker is a private class // we can't create for test purposes... BatchMessageListenerContainer container = new BatchMessageListenerContainer() { @Override protected void messageReceived(Object invoker, Session session) { } @Override protected void noMessageReceived(Object invoker, Session session) { } }; RepeatOperationsInterceptor interceptor = new RepeatOperationsInterceptor(); interceptor.setRepeatOperations(template); container.setAdviceChain(new Advice[] {interceptor}); container.setConnectionFactory(connectionFactory); container.setDestinationName("queue"); container.afterPropertiesSet(); return container; } private boolean doTestWithException(final Throwable t, boolean expectRollback, int expectGetTransactionCount) throws JMSException, IllegalAccessException { container.setAcceptMessagesWhileStopping(true); container.setMessageListener(new MessageListener() { @Override public void onMessage(Message arg0) { if (t instanceof RuntimeException) throw (RuntimeException) t; else throw (Error) t; } }); Session session = mock(Session.class); MessageConsumer consumer = mock(MessageConsumer.class); Message message = mock(Message.class); if (expectGetTransactionCount>0) { when(session.getTransacted()).thenReturn(true); } // Expect only one call to consumer (chunk size is 2, but first one // rolls back terminating batch)... when(consumer.receive(1000)).thenReturn(message); if (expectRollback) { session.rollback(); } boolean received = doExecute(session, consumer); return received; } private boolean doExecute(Session session, MessageConsumer consumer) throws IllegalAccessException { Method method = ReflectionUtils.findMethod(container.getClass(), "receiveAndExecute", Object.class, Session.class, MessageConsumer.class); method.setAccessible(true); boolean received; try { // A null invoker is not normal, but we don't care about the invoker for a unit test received = (Boolean) method.invoke(container, null, session, consumer); } catch (InvocationTargetException e) { if (e.getCause() instanceof RuntimeException) { throw (RuntimeException) e.getCause(); } else { throw (Error) e.getCause(); } } return received; } }