/*
 * Copyright 2002-2009 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.TextMessage;
import javax.naming.Context;
import javax.naming.NamingException;

import junit.framework.TestCase;
import org.easymock.MockControl;

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.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;

/**
 * Unit tests for the JmsTemplate implemented using JMS 1.1.
 *
 * @author Andre Biryukov
 * @author Mark Pollack
 */
public class JmsTemplateTests extends TestCase {

	private Context mockJndiContext;
	private MockControl mockJndiControl;

	private MockControl connectionFactoryControl;
	private ConnectionFactory mockConnectionFactory;

	private MockControl connectionControl;
	private Connection mockConnection;

	private MockControl sessionControl;
	private Session mockSession;

	private MockControl queueControl;
	private Destination mockQueue;

	private int deliveryMode = DeliveryMode.PERSISTENT;
	private int priority = 9;
	private int timeToLive = 10000;


	/**
	 * Create the mock objects for testing.
	 */
	protected void setUp() throws Exception {
		mockJndiControl = MockControl.createControl(Context.class);
		mockJndiContext = (Context) this.mockJndiControl.getMock();

		createMockforDestination();

		mockJndiContext.close();
		mockJndiControl.replay();
	}

	private void createMockforDestination() throws JMSException, NamingException {
		connectionFactoryControl = MockControl.createControl(ConnectionFactory.class);
		mockConnectionFactory = (ConnectionFactory) connectionFactoryControl.getMock();

		connectionControl = MockControl.createControl(Connection.class);
		mockConnection = (Connection) connectionControl.getMock();

		sessionControl = MockControl.createControl(Session.class);
		mockSession = (Session) sessionControl.getMock();

		queueControl = MockControl.createControl(Queue.class);
		mockQueue = (Queue) queueControl.getMock();

		mockConnectionFactory.createConnection();
		connectionFactoryControl.setReturnValue(mockConnection);
		connectionFactoryControl.replay();

		mockConnection.createSession(useTransactedTemplate(), Session.AUTO_ACKNOWLEDGE);
		connectionControl.setReturnValue(mockSession);
		mockSession.getTransacted();
		sessionControl.setReturnValue(useTransactedSession());

		mockJndiContext.lookup("testDestination");
		mockJndiControl.setReturnValue(mockQueue);
	}

	private JmsTemplate createTemplate() {
		JmsTemplate template = new JmsTemplate();
		JndiDestinationResolver destMan = new JndiDestinationResolver();
		destMan.setJndiTemplate(new JndiTemplate() {
			protected Context createInitialContext() {
				return mockJndiContext;
			}
		});
		template.setDestinationResolver(destMan);
		template.setSessionTransacted(useTransactedTemplate());
		return template;
	}

	protected boolean useTransactedSession() {
		return false;
	}

	protected boolean useTransactedTemplate() {
		return false;
	}


	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);
	}

	public void testProducerCallback() throws Exception {
		JmsTemplate template = createTemplate();
		template.setConnectionFactory(mockConnectionFactory);

		MockControl messageProducerControl = MockControl.createControl(MessageProducer.class);
		MessageProducer mockMessageProducer = (MessageProducer) messageProducerControl.getMock();

		mockSession.createProducer(null);
		sessionControl.setReturnValue(mockMessageProducer);

		mockMessageProducer.getPriority();
		messageProducerControl.setReturnValue(4);

		mockMessageProducer.close();
		messageProducerControl.setVoidCallable(1);
		mockSession.close();
		sessionControl.setVoidCallable(1);
		mockConnection.close();
		connectionControl.setVoidCallable(1);

		messageProducerControl.replay();
		sessionControl.replay();
		connectionControl.replay();

		template.execute(new ProducerCallback() {
			public Object doInJms(Session session, MessageProducer producer) throws JMSException {
				boolean b = session.getTransacted();
				int i = producer.getPriority();
				return null;
			}
		});

		connectionFactoryControl.verify();
		connectionControl.verify();
		sessionControl.verify();
	}

	public void testProducerCallbackWithIdAndTimestampDisabled() throws Exception {
		JmsTemplate template = createTemplate();
		template.setConnectionFactory(mockConnectionFactory);
		template.setMessageIdEnabled(false);
		template.setMessageTimestampEnabled(false);

		MockControl messageProducerControl = MockControl.createControl(MessageProducer.class);
		MessageProducer mockMessageProducer = (MessageProducer) messageProducerControl.getMock();

		mockSession.createProducer(null);
		sessionControl.setReturnValue(mockMessageProducer);

		mockMessageProducer.setDisableMessageID(true);
		messageProducerControl.setVoidCallable(1);
		mockMessageProducer.setDisableMessageTimestamp(true);
		messageProducerControl.setVoidCallable(1);
		mockMessageProducer.getPriority();
		messageProducerControl.setReturnValue(4);

		mockMessageProducer.close();
		messageProducerControl.setVoidCallable(1);
		mockSession.close();
		sessionControl.setVoidCallable(1);
		mockConnection.close();
		connectionControl.setVoidCallable(1);

		messageProducerControl.replay();
		sessionControl.replay();
		connectionControl.replay();

		template.execute(new ProducerCallback() {
			public Object doInJms(Session session, MessageProducer producer) throws JMSException {
				boolean b = session.getTransacted();
				int i = producer.getPriority();
				return null;
			}
		});

		connectionFactoryControl.verify();
		connectionControl.verify();
		sessionControl.verify();
	}

	/**
	 * Test the method execute(SessionCallback action).
	 */
	public void testSessionCallback() throws Exception {
		JmsTemplate template = createTemplate();
		template.setConnectionFactory(mockConnectionFactory);

		mockSession.close();
		sessionControl.setVoidCallable(1);

		mockConnection.close();
		connectionControl.setVoidCallable(1);

		sessionControl.replay();
		connectionControl.replay();

		template.execute(new SessionCallback() {
			public Object doInJms(Session session) throws JMSException {
				boolean b = session.getTransacted();
				return null;
			}
		});

		connectionFactoryControl.verify();
		connectionControl.verify();
		sessionControl.verify();
	}

	public void testSessionCallbackWithinSynchronizedTransaction() throws Exception {
		SingleConnectionFactory scf = new SingleConnectionFactory(mockConnectionFactory);
		JmsTemplate template = createTemplate();
		template.setConnectionFactory(scf);

		mockConnection.start();
		connectionControl.setVoidCallable(1);
		// We're gonna call getTransacted 3 times, i.e. 2 more times.
		mockSession.getTransacted();
		sessionControl.setReturnValue(useTransactedSession(), 2);
		if (useTransactedTemplate()) {
			mockSession.commit();
			sessionControl.setVoidCallable(1);
		}
		mockSession.close();
		sessionControl.setVoidCallable(1);
		mockConnection.stop();
		connectionControl.setVoidCallable(1);
		mockConnection.close();
		connectionControl.setVoidCallable(1);

		sessionControl.replay();
		connectionControl.replay();

		TransactionSynchronizationManager.initSynchronization();
		try {
			template.execute(new SessionCallback() {
				public Object doInJms(Session session) throws JMSException {
					boolean b = session.getTransacted();
					return null;
				}
			});
			template.execute(new SessionCallback() {
				public Object doInJms(Session session) throws JMSException {
					boolean b = session.getTransacted();
					return null;
				}
			});

			assertSame(mockSession, ConnectionFactoryUtils.getTransactionalSession(scf, null, false));
			assertSame(mockSession, ConnectionFactoryUtils.getTransactionalSession(scf, scf.createConnection(), false));

			TransactionAwareConnectionFactoryProxy tacf = new TransactionAwareConnectionFactoryProxy(scf);
			Connection tac = tacf.createConnection();
			Session tas = tac.createSession(false, Session.AUTO_ACKNOWLEDGE);
			boolean b = tas.getTransacted();
			tas.close();
			tac.close();

			List synchs = TransactionSynchronizationManager.getSynchronizations();
			assertEquals(1, synchs.size());
			TransactionSynchronization synch = (TransactionSynchronization) synchs.get(0);
			synch.beforeCommit(false);
			synch.beforeCompletion();
			synch.afterCommit();
			synch.afterCompletion(TransactionSynchronization.STATUS_UNKNOWN);
		}
		finally {
			TransactionSynchronizationManager.clearSynchronization();
			scf.destroy();
		}
		assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty());

		connectionFactoryControl.verify();
		connectionControl.verify();
		sessionControl.verify();
	}

	/**
	 * Test sending to a destination using the method
	 * send(Destination d, MessageCreator messageCreator)
	 */
	public void testSendDestination() throws Exception {
		doTestSendDestination(true, false, true, false);
	}

	/**
	 * Test seding to a destination using the method
	 * send(String d, MessageCreator messageCreator)
	 */
	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.
	 */
	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.
	 */
	public void testSendDestinationNameWithQOS() throws Exception {
		doTestSendDestination(false, false, false, true);
	}

	/**
	 * Test sending to the default destination.
	 */
	public void testSendDefaultDestination() throws Exception {
		doTestSendDestination(true, true, true, true);
	}

	/**
	 * Test sending to the default destination name.
	 */
	public void testSendDefaultDestinationName() throws Exception {
		doTestSendDestination(false, true, true, true);
	}

	/**
	 * Test sending to the default destination using explicit QOS parameters.
	 */
	public void testSendDefaultDestinationWithQOS() throws Exception {
		doTestSendDestination(true, true, false, false);
	}

	/**
	 * Test sending to the default destination name using explicit QOS parameters.
	 */
	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(mockConnectionFactory);

		String destinationName = "testDestination";

		if (useDefaultDestination) {
			if (explicitDestination) {
				template.setDefaultDestination(mockQueue);
			}
			else {
				template.setDefaultDestinationName(destinationName);
			}
		}
		if (disableIdAndTimestamp) {
			template.setMessageIdEnabled(false);
			template.setMessageTimestampEnabled(false);
		}

		MockControl messageProducerControl = MockControl.createControl(MessageProducer.class);
		MessageProducer mockMessageProducer = (MessageProducer) messageProducerControl.getMock();

		MockControl messageControl = MockControl.createControl(TextMessage.class);
		TextMessage mockMessage = (TextMessage) messageControl.getMock();

		mockSession.createProducer(mockQueue);
		sessionControl.setReturnValue(mockMessageProducer);
		mockSession.createTextMessage("just testing");
		sessionControl.setReturnValue(mockMessage);

		if (useTransactedTemplate()) {
			mockSession.commit();
			sessionControl.setVoidCallable(1);
		}

		if (disableIdAndTimestamp) {
			mockMessageProducer.setDisableMessageID(true);
			messageProducerControl.setVoidCallable(1);
			mockMessageProducer.setDisableMessageTimestamp(true);
			messageProducerControl.setVoidCallable(1);
		}

		if (ignoreQOS) {
			mockMessageProducer.send(mockMessage);
		}
		else {
			template.setExplicitQosEnabled(true);
			template.setDeliveryMode(deliveryMode);
			template.setPriority(priority);
			template.setTimeToLive(timeToLive);
			mockMessageProducer.send(mockMessage, deliveryMode, priority, timeToLive);
		}
		messageProducerControl.setVoidCallable(1);

		mockMessageProducer.close();
		messageProducerControl.setVoidCallable(1);
		mockSession.close();
		sessionControl.setVoidCallable(1);
		mockConnection.close();
		connectionControl.setVoidCallable(1);

		messageProducerControl.replay();
		sessionControl.replay();
		connectionControl.replay();

		if (useDefaultDestination) {
			template.send(new MessageCreator() {
				public Message createMessage(Session session) throws JMSException {
					return session.createTextMessage("just testing");
				}
			});
		}
		else {
			if (explicitDestination) {
				template.send(mockQueue, new MessageCreator() {
					public Message createMessage(Session session) throws JMSException {
						return session.createTextMessage("just testing");
					}
				});
			}
			else {
				template.send(destinationName, new MessageCreator() {
					public Message createMessage(Session session) throws JMSException {
						return session.createTextMessage("just testing");
					}
				});
			}
		}

		connectionFactoryControl.verify();
		connectionControl.verify();
		sessionControl.verify();
		messageProducerControl.verify();
	}

	public void testConverter() throws Exception {
		JmsTemplate template = createTemplate();
		template.setConnectionFactory(mockConnectionFactory);
		template.setMessageConverter(new SimpleMessageConverter());
		String s = "Hello world";

		MockControl messageProducerControl = MockControl.createControl(MessageProducer.class);
		MessageProducer mockMessageProducer = (MessageProducer) messageProducerControl.getMock();
		MockControl messageControl = MockControl.createControl(TextMessage.class);
		TextMessage mockMessage = (TextMessage) messageControl.getMock();

		mockSession.createProducer(mockQueue);
		sessionControl.setReturnValue(mockMessageProducer);
		mockSession.createTextMessage("Hello world");
		sessionControl.setReturnValue(mockMessage);

		mockMessageProducer.send(mockMessage);
		messageProducerControl.setVoidCallable(1);
		mockMessageProducer.close();
		messageProducerControl.setVoidCallable(1);

		if (useTransactedTemplate()) {
			mockSession.commit();
			sessionControl.setVoidCallable(1);
		}

		mockSession.close();
		sessionControl.setVoidCallable(1);
		mockConnection.close();
		connectionControl.setVoidCallable(1);

		messageProducerControl.replay();
		sessionControl.replay();
		connectionControl.replay();

		template.convertAndSend(mockQueue, s);

		messageProducerControl.verify();
		sessionControl.verify();
		connectionControl.verify();
		connectionFactoryControl.verify();
	}

	public void testReceiveDefaultDestination() throws Exception {
		doTestReceive(true, true, false, false, false, false, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT);
	}

	public void testReceiveDefaultDestinationName() throws Exception {
		doTestReceive(false, true, false, false, false, false, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT);
	}

	public void testReceiveDestination() throws Exception {
		doTestReceive(true, false, false, false, false, true, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT);
	}

	public void testReceiveDestinationWithClientAcknowledge() throws Exception {
		doTestReceive(true, false, false, true, false, false, 1000);
	}

	public void testReceiveDestinationName() throws Exception {
		doTestReceive(false, false, false, false, false, true, 1000);
	}

	public void testReceiveDefaultDestinationWithSelector() throws Exception {
		doTestReceive(true, true, false, false, true, true, 1000);
	}

	public void testReceiveDefaultDestinationNameWithSelector() throws Exception {
		doTestReceive(false, true, false, false, true, true, JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT);
	}

	public void testReceiveDestinationWithSelector() throws Exception {
		doTestReceive(true, false, false, false, true, false, 1000);
	}

	public void testReceiveDestinationWithClientAcknowledgeWithSelector() throws Exception {
		doTestReceive(true, false, false, true, true, true, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT);
	}

	public void testReceiveDestinationNameWithSelector() throws Exception {
		doTestReceive(false, false, false, false, true, false, JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT);
	}

	public void testReceiveAndConvertDefaultDestination() throws Exception {
		doTestReceive(true, true, true, false, false, false, 1000);
	}

	public void testReceiveAndConvertDefaultDestinationName() throws Exception {
		doTestReceive(false, true, true, false, false, false, 1000);
	}

	public void testReceiveAndConvertDestinationName() throws Exception {
		doTestReceive(false, false, true, false, false, true, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT);
	}

	public void testReceiveAndConvertDestination() throws Exception {
		doTestReceive(true, false, true, false, false, true, 1000);
	}

	public void testReceiveAndConvertDefaultDestinationWithSelector() throws Exception {
		doTestReceive(true, true, true, false, true, true, JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT);
	}

	public void testReceiveAndConvertDestinationNameWithSelector() throws Exception {
		doTestReceive(false, false, true, false, true, true, JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT);
	}

	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(mockConnectionFactory);

		String destinationName = "testDestination";

		if (useDefaultDestination) {
			if (explicitDestination) {
				template.setDefaultDestination(mockQueue);
			}
			else {
				template.setDefaultDestinationName(destinationName);
			}
		}
		if (noLocal) {
			template.setPubSubNoLocal(true);
		}
		template.setReceiveTimeout(timeout);

		mockConnection.start();
		connectionControl.setVoidCallable(1);
		mockConnection.close();
		connectionControl.setVoidCallable(1);

		MockControl messageConsumerControl = MockControl.createControl(MessageConsumer.class);
		MessageConsumer mockMessageConsumer = (MessageConsumer) messageConsumerControl.getMock();

		String selectorString = "selector";
		mockSession.createConsumer(mockQueue, messageSelector ? selectorString : null);
		sessionControl.setReturnValue(mockMessageConsumer);

		if (useTransactedTemplate()) {
			mockSession.commit();
			sessionControl.setVoidCallable(1);
		}
		else if (!useTransactedSession()) {
			mockSession.getAcknowledgeMode();
			if (clientAcknowledge) {
				sessionControl.setReturnValue(Session.CLIENT_ACKNOWLEDGE, 1);
			}
			else {
				sessionControl.setReturnValue(Session.AUTO_ACKNOWLEDGE, 1);
			}
		}

		mockSession.close();
		sessionControl.setVoidCallable(1);

		MockControl messageControl = MockControl.createControl(TextMessage.class);
		TextMessage mockMessage = (TextMessage) messageControl.getMock();

		if (testConverter) {
			mockMessage.getText();
			messageControl.setReturnValue("Hello World!");
		}
		if (!useTransactedSession() && clientAcknowledge) {
			mockMessage.acknowledge();
			messageControl.setVoidCallable(1);
		}

		sessionControl.replay();
		connectionControl.replay();
		messageControl.replay();

		if (timeout == JmsTemplate.RECEIVE_TIMEOUT_NO_WAIT) {
			mockMessageConsumer.receiveNoWait();
		}
		else if (timeout == JmsTemplate.RECEIVE_TIMEOUT_INDEFINITE_WAIT) {
			mockMessageConsumer.receive();
		}
		else {
			mockMessageConsumer.receive(timeout);
		}

		messageConsumerControl.setReturnValue(mockMessage);
		mockMessageConsumer.close();
		messageConsumerControl.setVoidCallable(1);

		messageConsumerControl.replay();

		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(mockQueue, selectorString) :
						template.receiveAndConvert(mockQueue));
			}
			else {
				message = (messageSelector ? template.receiveSelected(mockQueue, selectorString) :
						template.receive(mockQueue));
			}
		}
		else {
			if (testConverter) {
				textFromMessage = (String)
						(messageSelector ? template.receiveSelectedAndConvert(destinationName, selectorString) :
						template.receiveAndConvert(destinationName));
			}
			else {
				message = (messageSelector ? template.receiveSelected(destinationName, selectorString) :
						template.receive(destinationName));
			}
		}

		connectionFactoryControl.verify();
		connectionControl.verify();
		sessionControl.verify();
		messageConsumerControl.verify();
		messageControl.verify();

		if (testConverter) {
			assertEquals("Message text should be equal", "Hello World!", textFromMessage);
		}
		else {
			assertEquals("Messages should refer to the same object", message, mockMessage);
		}
	}

	public void testIllegalStateException() throws Exception {
		doTestJmsException(new javax.jms.IllegalStateException(""), org.springframework.jms.IllegalStateException.class);
	}

	public void testInvalidClientIDException() throws Exception {
		doTestJmsException(new javax.jms.InvalidClientIDException(""), InvalidClientIDException.class);
	}

	public void testInvalidDestinationException() throws Exception {
		doTestJmsException(new javax.jms.InvalidDestinationException(""), InvalidDestinationException.class);
	}

	public void testInvalidSelectorException() throws Exception {
		doTestJmsException(new javax.jms.InvalidSelectorException(""), InvalidSelectorException.class);
	}

	public void testJmsSecurityException() throws Exception {
		doTestJmsException(new javax.jms.JMSSecurityException(""), JmsSecurityException.class);
	}

	public void testMessageEOFException() throws Exception {
		doTestJmsException(new javax.jms.MessageEOFException(""), MessageEOFException.class);
	}

	public void testMessageFormatException() throws Exception {
		doTestJmsException(new javax.jms.MessageFormatException(""), MessageFormatException.class);
	}

	public void testMessageNotReadableException() throws Exception {
		doTestJmsException(new javax.jms.MessageNotReadableException(""), MessageNotReadableException.class);
	}

	public void testMessageNotWriteableException() throws Exception {
		doTestJmsException(new javax.jms.MessageNotWriteableException(""), MessageNotWriteableException.class);
	}

	public void testResourceAllocationException() throws Exception {
		doTestJmsException(new javax.jms.ResourceAllocationException(""), ResourceAllocationException.class);
	}

	public void testTransactionInProgressException() throws Exception {
		doTestJmsException(new javax.jms.TransactionInProgressException(""), TransactionInProgressException.class);
	}

	public void testTransactionRolledBackException() throws Exception {
		doTestJmsException(new javax.jms.TransactionRolledBackException(""), TransactionRolledBackException.class);
	}

	public void testUncategorizedJmsException() throws Exception {
		doTestJmsException(new javax.jms.JMSException(""), UncategorizedJmsException.class);
	}

	protected void doTestJmsException(JMSException original, Class thrownExceptionClass) throws Exception {
		JmsTemplate template = createTemplate();
		template.setConnectionFactory(mockConnectionFactory);
		template.setMessageConverter(new SimpleMessageConverter());
		String s = "Hello world";

		MockControl messageProducerControl = MockControl.createControl(MessageProducer.class);
		MessageProducer mockMessageProducer = (MessageProducer) messageProducerControl.getMock();
		MockControl messageControl = MockControl.createControl(TextMessage.class);
		TextMessage mockMessage = (TextMessage) messageControl.getMock();

		sessionControl.reset();
		mockSession.createProducer(mockQueue);
		sessionControl.setReturnValue(mockMessageProducer);
		mockSession.createTextMessage("Hello world");
		sessionControl.setReturnValue(mockMessage);

		mockMessageProducer.send(mockMessage);
		messageProducerControl.setThrowable(original, 1);
		mockMessageProducer.close();
		messageProducerControl.setVoidCallable(1);

		mockSession.close();
		sessionControl.setVoidCallable(1);
		mockConnection.close();
		connectionControl.setVoidCallable(1);

		messageProducerControl.replay();
		sessionControl.replay();
		connectionControl.replay();

		try {
			template.convertAndSend(mockQueue, s);
			fail("Should have thrown JmsException");
		}
		catch (JmsException wrappedEx) {
			// expected
			assertEquals(thrownExceptionClass, wrappedEx.getClass());
			assertEquals(original, wrappedEx.getCause());
		}

		messageProducerControl.verify();
		sessionControl.verify();
		connectionControl.verify();
		connectionFactoryControl.verify();
	}

}
