/* * Copyright 2002-2017 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.core; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.DeliveryMode; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TemporaryQueue; import javax.jms.TextMessage; import javax.naming.Context; import org.junit.Before; import org.junit.Test; import org.springframework.jms.InvalidClientIDException; import org.springframework.jms.InvalidDestinationException; import org.springframework.jms.InvalidSelectorException; import org.springframework.jms.JmsException; import org.springframework.jms.JmsSecurityException; import org.springframework.jms.MessageEOFException; import org.springframework.jms.MessageFormatException; import org.springframework.jms.MessageNotReadableException; import org.springframework.jms.MessageNotWriteableException; import org.springframework.jms.ResourceAllocationException; import org.springframework.jms.TransactionInProgressException; import org.springframework.jms.TransactionRolledBackException; import org.springframework.jms.UncategorizedJmsException; import org.springframework.jms.connection.ConnectionFactoryUtils; import org.springframework.jms.connection.SingleConnectionFactory; import org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy; import org.springframework.jms.support.JmsUtils; import org.springframework.jms.support.QosSettings; import org.springframework.jms.support.converter.SimpleMessageConverter; import org.springframework.jms.support.destination.JndiDestinationResolver; import org.springframework.jndi.JndiTemplate; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; /** * Unit tests for the JmsTemplate implemented using JMS 1.1. * * @author Andre Biryukov * @author Mark Pollack * @author Stephane Nicoll */ public class JmsTemplateTests { private Context jndiContext; private ConnectionFactory connectionFactory; protected Connection connection; private Session session; private Destination queue; private QosSettings qosSettings = new QosSettings(DeliveryMode.PERSISTENT, 9, 10000); /** * Create the mock objects for testing. */ @Before public void setupMocks() throws Exception { this.jndiContext = mock(Context.class); this.connectionFactory = mock(ConnectionFactory.class); this.connection = mock(Connection.class); this.session = mock(Session.class); this.queue = mock(Queue.class); given(this.connectionFactory.createConnection()).willReturn(this.connection); given(this.connection.createSession(useTransactedTemplate(), Session.AUTO_ACKNOWLEDGE)).willReturn(this.session); given(this.session.getTransacted()).willReturn(useTransactedSession()); given(this.jndiContext.lookup("testDestination")).willReturn(this.queue); } private JmsTemplate createTemplate() { JmsTemplate template = new JmsTemplate(); JndiDestinationResolver destMan = new JndiDestinationResolver(); destMan.setJndiTemplate(new JndiTemplate() { @Override protected Context createInitialContext() { return JmsTemplateTests.this.jndiContext; } }); template.setDestinationResolver(destMan); template.setSessionTransacted(useTransactedTemplate()); return template; } protected boolean useTransactedSession() { return false; } protected boolean useTransactedTemplate() { return false; } protected Session getLocalSession() { return this.session; } @Test public void testExceptionStackTrace() { JMSException jmsEx = new JMSException("could not connect"); Exception innerEx = new Exception("host not found"); jmsEx.setLinkedException(innerEx); JmsException springJmsEx = JmsUtils.convertJmsAccessException(jmsEx); StringWriter sw = new StringWriter(); PrintWriter out = new PrintWriter(sw); springJmsEx.printStackTrace(out); String trace = sw.toString(); assertTrue("inner jms exception not found", trace.indexOf("host not found") > 0); } @Test public void testProducerCallback() throws Exception { JmsTemplate template = createTemplate(); template.setConnectionFactory(this.connectionFactory); MessageProducer messageProducer = mock(MessageProducer.class); given(this.session.createProducer(null)).willReturn(messageProducer); given(messageProducer.getPriority()).willReturn(4); template.execute(new ProducerCallback<Void>() { @Override public Void doInJms(Session session, MessageProducer producer) throws JMSException { session.getTransacted(); producer.getPriority(); return null; } }); verify(messageProducer).close(); verify(this.session).close(); verify(this.connection).close(); } @Test public void testProducerCallbackWithIdAndTimestampDisabled() throws Exception { JmsTemplate template = createTemplate(); template.setConnectionFactory(this.connectionFactory); template.setMessageIdEnabled(false); template.setMessageTimestampEnabled(false); MessageProducer messageProducer = mock(MessageProducer.class); given(this.session.createProducer(null)).willReturn(messageProducer); given(messageProducer.getPriority()).willReturn(4); template.execute(new ProducerCallback<Void>() { @Override public Void doInJms(Session session, MessageProducer producer) throws JMSException { session.getTransacted(); producer.getPriority(); return null; } }); verify(messageProducer).setDisableMessageID(true); verify(messageProducer).setDisableMessageTimestamp(true); verify(messageProducer).close(); verify(this.session).close(); verify(this.connection).close(); } /** * Test the method execute(SessionCallback action). */ @Test public void testSessionCallback() throws Exception { JmsTemplate template = createTemplate(); template.setConnectionFactory(this.connectionFactory); template.execute(new SessionCallback<Void>() { @Override public Void doInJms(Session session) throws JMSException { session.getTransacted(); return null; } }); verify(this.session).close(); verify(this.connection).close(); } @Test public void testSessionCallbackWithinSynchronizedTransaction() throws Exception { SingleConnectionFactory scf = new SingleConnectionFactory(this.connectionFactory); JmsTemplate template = createTemplate(); template.setConnectionFactory(scf); TransactionSynchronizationManager.initSynchronization(); try { template.execute(new SessionCallback<Void>() { @Override public Void doInJms(Session session) throws JMSException { session.getTransacted(); return null; } }); template.execute(new SessionCallback<Void>() { @Override public Void doInJms(Session session) throws JMSException { session.getTransacted(); return null; } }); assertSame(this.session, ConnectionFactoryUtils.getTransactionalSession(scf, null, false)); assertSame(this.session, ConnectionFactoryUtils.getTransactionalSession(scf, scf.createConnection(), false)); TransactionAwareConnectionFactoryProxy tacf = new TransactionAwareConnectionFactoryProxy(scf); Connection tac = tacf.createConnection(); Session tas = tac.createSession(false, Session.AUTO_ACKNOWLEDGE); tas.getTransacted(); tas.close(); tac.close(); List<TransactionSynchronization> synchs = TransactionSynchronizationManager.getSynchronizations(); assertEquals(1, synchs.size()); TransactionSynchronization synch = synchs.get(0); synch.beforeCommit(false); synch.beforeCompletion(); synch.afterCommit(); synch.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN); } finally { TransactionSynchronizationManager.clearSynchronization(); scf.destroy(); } assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty()); verify(this.connection).start(); if (useTransactedTemplate()) { verify(this.session).commit(); } verify(this.session).close(); verify(this.connection).stop(); verify(this.connection).close(); } /** * Test sending to a destination using the method * send(Destination d, MessageCreator messageCreator) */ @Test public void testSendDestination() throws Exception { doTestSendDestination(true, false, true, false); } /** * Test seding to a destination using the method * send(String d, MessageCreator messageCreator) */ @Test public void testSendDestinationName() throws Exception { doTestSendDestination(false, false, true, false); } /** * Test sending to a destination using the method * send(Destination d, MessageCreator messageCreator) using QOS parameters. */ @Test public void testSendDestinationWithQOS() throws Exception { doTestSendDestination(true, false, false, true); } /** * Test sending to a destination using the method * send(String d, MessageCreator messageCreator) using QOS parameters. */ @Test public void testSendDestinationNameWithQOS() throws Exception { doTestSendDestination(false, false, false, true); } /** * Test sending to the default destination. */ @Test public void testSendDefaultDestination() throws Exception { doTestSendDestination(true, true, true, true); } /** * Test sending to the default destination name. */ @Test public void testSendDefaultDestinationName() throws Exception { doTestSendDestination(false, true, true, true); } /** * Test sending to the default destination using explicit QOS parameters. */ @Test public void testSendDefaultDestinationWithQOS() throws Exception { doTestSendDestination(true, true, false, false); } /** * Test sending to the default destination name using explicit QOS parameters. */ @Test public void testSendDefaultDestinationNameWithQOS() throws Exception { doTestSendDestination(false, true, false, false); } /** * Common method for testing a send method that uses the MessageCreator * callback but with different QOS options. * @param ignoreQOS test using default QOS options. */ private void doTestSendDestination( boolean explicitDestination, boolean useDefaultDestination, boolean ignoreQOS, boolean disableIdAndTimestamp) throws Exception { JmsTemplate template = createTemplate(); template.setConnectionFactory(this.connectionFactory); String destinationName = "testDestination"; if (useDefaultDestination) { if (explicitDestination) { template.setDefaultDestination(this.queue); } else { template.setDefaultDestinationName(destinationName); } } if (disableIdAndTimestamp) { template.setMessageIdEnabled(false); template.setMessageTimestampEnabled(false); } MessageProducer messageProducer = mock(MessageProducer.class); TextMessage textMessage = mock(TextMessage.class); given(this.session.createProducer(this.queue)).willReturn(messageProducer); given(this.session.createTextMessage("just testing")).willReturn(textMessage); if (!ignoreQOS) { template.setQosSettings(this.qosSettings); } if (useDefaultDestination) { template.send(new MessageCreator() { @Override public Message createMessage(Session session) throws JMSException { return session.createTextMessage("just testing"); } }); } else { if (explicitDestination) { template.send(this.queue, new MessageCreator() { @Override public Message createMessage(Session session) throws JMSException { return session.createTextMessage("just testing"); } }); } else { template.send(destinationName, new MessageCreator() { @Override public Message createMessage(Session session) throws JMSException { return session.createTextMessage("just testing"); } }); } } if (useTransactedTemplate()) { verify(this.session).commit(); } if (disableIdAndTimestamp) { verify(messageProducer).setDisableMessageID(true); verify(messageProducer).setDisableMessageTimestamp(true); } if (ignoreQOS) { verify(messageProducer).send(textMessage); } else { verify(messageProducer).send(textMessage, this.qosSettings.getDeliveryMode(), this.qosSettings.getPriority(), this.qosSettings.getTimeToLive()); } verify(messageProducer).close(); verify(this.session).close(); verify(this.connection).close(); } @Test public void testConverter() throws Exception { JmsTemplate template = createTemplate(); template.setConnectionFactory(this.connectionFactory); template.setMessageConverter(new SimpleMessageConverter()); String s = "Hello world"; MessageProducer messageProducer = mock(MessageProducer.class); TextMessage textMessage = mock(TextMessage.class); given(this.session.createProducer(this.queue)).willReturn(messageProducer); given(this.session.createTextMessage("Hello world")).willReturn(textMessage); template.convertAndSend(this.queue, s); verify(messageProducer).send(textMessage); verify(messageProducer).close(); if (useTransactedTemplate()) { verify(this.session).commit(); } verify(this.session).close(); verify(this.connection).close(); } @Test public void testReceiveDefaultDestination() throws Exception { doTestReceive(true, true, false, false, false, false, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT); } @Test public void testReceiveDefaultDestinationName() throws Exception { doTestReceive(false, true, false, false, false, false, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT); } @Test public void testReceiveDestination() throws Exception { doTestReceive(true, false, false, false, false, true, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT); } @Test public void testReceiveDestinationWithClientAcknowledge() throws Exception { doTestReceive(true, false, false, true, false, false, 1000); } @Test public void testReceiveDestinationName() throws Exception { doTestReceive(false, false, false, false, false, true, 1000); } @Test public void testReceiveDefaultDestinationWithSelector() throws Exception { doTestReceive(true, true, false, false, true, true, 1000); } @Test public void testReceiveDefaultDestinationNameWithSelector() throws Exception { doTestReceive(false, true, false, false, true, true, JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT); } @Test public void testReceiveDestinationWithSelector() throws Exception { doTestReceive(true, false, false, false, true, false, 1000); } @Test public void testReceiveDestinationWithClientAcknowledgeWithSelector() throws Exception { doTestReceive(true, false, false, true, true, true, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT); } @Test public void testReceiveDestinationNameWithSelector() throws Exception { doTestReceive(false, false, false, false, true, false, JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT); } @Test public void testReceiveAndConvertDefaultDestination() throws Exception { doTestReceive(true, true, true, false, false, false, 1000); } @Test public void testReceiveAndConvertDefaultDestinationName() throws Exception { doTestReceive(false, true, true, false, false, false, 1000); } @Test public void testReceiveAndConvertDestinationName() throws Exception { doTestReceive(false, false, true, false, false, true, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT); } @Test public void testReceiveAndConvertDestination() throws Exception { doTestReceive(true, false, true, false, false, true, 1000); } @Test public void testReceiveAndConvertDefaultDestinationWithSelector() throws Exception { doTestReceive(true, true, true, false, true, true, JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT); } @Test public void testReceiveAndConvertDestinationNameWithSelector() throws Exception { doTestReceive(false, false, true, false, true, true, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT); } @Test public void testReceiveAndConvertDestinationWithSelector() throws Exception { doTestReceive(true, false, true, false, true, false, 1000); } private void doTestReceive( boolean explicitDestination, boolean useDefaultDestination, boolean testConverter, boolean clientAcknowledge, boolean messageSelector, boolean noLocal, long timeout) throws Exception { JmsTemplate template = createTemplate(); template.setConnectionFactory(this.connectionFactory); String destinationName = "testDestination"; if (useDefaultDestination) { if (explicitDestination) { template.setDefaultDestination(this.queue); } else { template.setDefaultDestinationName(destinationName); } } if (noLocal) { template.setPubSubNoLocal(true); } template.setReceiveTimeout(timeout); MessageConsumer messageConsumer = mock(MessageConsumer.class); String selectorString = "selector"; given(this.session.createConsumer(this.queue, messageSelector ? selectorString : null)).willReturn(messageConsumer); if (!useTransactedTemplate() && !useTransactedSession()) { given(this.session.getAcknowledgeMode()).willReturn( clientAcknowledge ? Session.CLIENT_ACKNOWLEDGE : Session.AUTO_ACKNOWLEDGE); } TextMessage textMessage = mock(TextMessage.class); if (testConverter) { given(textMessage.getText()).willReturn("Hello World!"); } if (timeout == JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT) { given(messageConsumer.receiveNoWait()).willReturn(textMessage); } else if (timeout == JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT) { given(messageConsumer.receive()).willReturn(textMessage); } else { given(messageConsumer.receive(timeout)).willReturn(textMessage); } Message message = null; String textFromMessage = null; if (useDefaultDestination) { if (testConverter) { textFromMessage = (String) (messageSelector ? template.receiveSelectedAndConvert(selectorString) : template.receiveAndConvert()); } else { message = (messageSelector ? template.receiveSelected(selectorString) : template.receive()); } } else if (explicitDestination) { if (testConverter) { textFromMessage = (String) (messageSelector ? template.receiveSelectedAndConvert(this.queue, selectorString) : template.receiveAndConvert(this.queue)); } else { message = (messageSelector ? template.receiveSelected(this.queue, selectorString) : template.receive(this.queue)); } } else { if (testConverter) { textFromMessage = (String) (messageSelector ? template.receiveSelectedAndConvert(destinationName, selectorString) : template.receiveAndConvert(destinationName)); } else { message = (messageSelector ? template.receiveSelected(destinationName, selectorString) : template.receive(destinationName)); } } if (testConverter) { assertEquals("Message text should be equal", "Hello World!", textFromMessage); } else { assertEquals("Messages should refer to the same object", message, textMessage); } verify(this.connection).start(); verify(this.connection).close(); if (useTransactedTemplate()) { verify(this.session).commit(); } verify(this.session).close(); if (!useTransactedSession() && clientAcknowledge) { verify(textMessage).acknowledge(); } verify(messageConsumer).close(); } @Test public void testSendAndReceiveDefaultDestination() throws Exception { doTestSendAndReceive(true, true, 1000L); } @Test public void testSendAndReceiveDefaultDestinationName() throws Exception { doTestSendAndReceive(false, true, 1000L); } @Test public void testSendAndReceiveDestination() throws Exception { doTestSendAndReceive(true, false, 1000L); } @Test public void testSendAndReceiveDestinationName() throws Exception { doTestSendAndReceive(false, false, 1000L); } private void doTestSendAndReceive(boolean explicitDestination, boolean useDefaultDestination, long timeout) throws Exception { JmsTemplate template = createTemplate(); template.setConnectionFactory(this.connectionFactory); String destinationName = "testDestination"; if (useDefaultDestination) { if (explicitDestination) { template.setDefaultDestination(this.queue); } else { template.setDefaultDestinationName(destinationName); } } template.setReceiveTimeout(timeout); Session localSession = getLocalSession(); TemporaryQueue replyDestination = mock(TemporaryQueue.class); MessageProducer messageProducer = mock(MessageProducer.class); given(localSession.createProducer(this.queue)).willReturn(messageProducer); given(localSession.createTemporaryQueue()).willReturn(replyDestination); MessageConsumer messageConsumer = mock(MessageConsumer.class); given(localSession.createConsumer(replyDestination)).willReturn(messageConsumer); TextMessage request = mock(TextMessage.class); MessageCreator messageCreator = mock(MessageCreator.class); given(messageCreator.createMessage(localSession)).willReturn(request); TextMessage reply = mock(TextMessage.class); if (timeout == JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT) { given(messageConsumer.receiveNoWait()).willReturn(reply); } else if (timeout == JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT) { given(messageConsumer.receive()).willReturn(reply); } else { given(messageConsumer.receive(timeout)).willReturn(reply); } Message message = null; if (useDefaultDestination) { message = template.sendAndReceive(messageCreator); } else if (explicitDestination) { message = template.sendAndReceive(this.queue, messageCreator); } else { message = template.sendAndReceive(destinationName, messageCreator); } // replyTO set on the request verify(request).setJMSReplyTo(replyDestination); assertSame("Reply message not received", reply, message); verify(this.connection).start(); verify(this.connection).close(); verify(localSession).close(); verify(messageConsumer).close(); verify(messageProducer).close(); } @Test public void testIllegalStateException() throws Exception { doTestJmsException(new javax.jms.IllegalStateException(""), org.springframework.jms.IllegalStateException.class); } @Test public void testInvalidClientIDException() throws Exception { doTestJmsException(new javax.jms.InvalidClientIDException(""), InvalidClientIDException.class); } @Test public void testInvalidDestinationException() throws Exception { doTestJmsException(new javax.jms.InvalidDestinationException(""), InvalidDestinationException.class); } @Test public void testInvalidSelectorException() throws Exception { doTestJmsException(new javax.jms.InvalidSelectorException(""), InvalidSelectorException.class); } @Test public void testJmsSecurityException() throws Exception { doTestJmsException(new javax.jms.JMSSecurityException(""), JmsSecurityException.class); } @Test public void testMessageEOFException() throws Exception { doTestJmsException(new javax.jms.MessageEOFException(""), MessageEOFException.class); } @Test public void testMessageFormatException() throws Exception { doTestJmsException(new javax.jms.MessageFormatException(""), MessageFormatException.class); } @Test public void testMessageNotReadableException() throws Exception { doTestJmsException(new javax.jms.MessageNotReadableException(""), MessageNotReadableException.class); } @Test public void testMessageNotWriteableException() throws Exception { doTestJmsException(new javax.jms.MessageNotWriteableException(""), MessageNotWriteableException.class); } @Test public void testResourceAllocationException() throws Exception { doTestJmsException(new javax.jms.ResourceAllocationException(""), ResourceAllocationException.class); } @Test public void testTransactionInProgressException() throws Exception { doTestJmsException(new javax.jms.TransactionInProgressException(""), TransactionInProgressException.class); } @Test public void testTransactionRolledBackException() throws Exception { doTestJmsException(new javax.jms.TransactionRolledBackException(""), TransactionRolledBackException.class); } @Test public void testUncategorizedJmsException() throws Exception { doTestJmsException(new javax.jms.JMSException(""), UncategorizedJmsException.class); } protected void doTestJmsException(JMSException original, Class<? extends JmsException> thrownExceptionClass) throws Exception { JmsTemplate template = createTemplate(); template.setConnectionFactory(this.connectionFactory); template.setMessageConverter(new SimpleMessageConverter()); String s = "Hello world"; MessageProducer messageProducer = mock(MessageProducer.class); TextMessage textMessage = mock(TextMessage.class); reset(this.session); given(this.session.createProducer(this.queue)).willReturn(messageProducer); given(this.session.createTextMessage("Hello world")).willReturn(textMessage); willThrow(original).given(messageProducer).send(textMessage); try { template.convertAndSend(this.queue, s); fail("Should have thrown JmsException"); } catch (JmsException wrappedEx) { // expected assertEquals(thrownExceptionClass, wrappedEx.getClass()); assertEquals(original, wrappedEx.getCause()); } verify(messageProducer).close(); verify(this.session).close(); verify(this.connection).close(); } }