/* * 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. * 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.config; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.jms.ConnectionFactory; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.ComponentDefinition; import org.springframework.beans.factory.parsing.CompositeComponentDefinition; import org.springframework.beans.factory.parsing.EmptyReaderEventListener; import org.springframework.beans.factory.parsing.PassThroughSourceExtractor; import org.springframework.beans.factory.parsing.ReaderEventListener; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.Phased; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jca.endpoint.GenericMessageEndpointManager; import org.springframework.jms.listener.DefaultMessageListenerContainer; import org.springframework.jms.listener.adapter.MessageListenerAdapter; import org.springframework.jms.listener.endpoint.JmsMessageEndpointManager; import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.ErrorHandler; import org.springframework.util.backoff.BackOff; import org.springframework.util.backoff.FixedBackOff; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; /** * @author Mark Fisher * @author Juergen Hoeller * @author Christian Dupuis * @author Stephane Nicoll */ public class JmsNamespaceHandlerTests { private static final String DEFAULT_CONNECTION_FACTORY = "connectionFactory"; private static final String EXPLICIT_CONNECTION_FACTORY = "testConnectionFactory"; private ToolingTestApplicationContext context; @Before public void setUp() throws Exception { this.context = new ToolingTestApplicationContext("jmsNamespaceHandlerTests.xml", getClass()); } @After public void tearDown() throws Exception { this.context.close(); } @Test public void testBeansCreated() { Map<String, ?> containers = context.getBeansOfType(DefaultMessageListenerContainer.class); assertEquals("Context should contain 3 JMS listener containers", 3, containers.size()); containers = context.getBeansOfType(GenericMessageEndpointManager.class); assertEquals("Context should contain 3 JCA endpoint containers", 3, containers.size()); Map<String, JmsListenerContainerFactory> containerFactories = context.getBeansOfType(JmsListenerContainerFactory.class); assertEquals("Context should contain 3 JmsListenerContainerFactory instances", 3, containerFactories.size()); } @Test public void testContainerConfiguration() throws Exception { Map<String, DefaultMessageListenerContainer> containers = context.getBeansOfType(DefaultMessageListenerContainer.class); ConnectionFactory defaultConnectionFactory = context.getBean(DEFAULT_CONNECTION_FACTORY, ConnectionFactory.class); ConnectionFactory explicitConnectionFactory = context.getBean(EXPLICIT_CONNECTION_FACTORY, ConnectionFactory.class); int defaultConnectionFactoryCount = 0; int explicitConnectionFactoryCount = 0; for (DefaultMessageListenerContainer container : containers.values()) { if (container.getConnectionFactory().equals(defaultConnectionFactory)) { defaultConnectionFactoryCount++; } else if (container.getConnectionFactory().equals(explicitConnectionFactory)) { explicitConnectionFactoryCount++; } } assertEquals("1 container should have the default connectionFactory", 1, defaultConnectionFactoryCount); assertEquals("2 containers should have the explicit connectionFactory", 2, explicitConnectionFactoryCount); } @Test public void testJcaContainerConfiguration() throws Exception { Map<String, JmsMessageEndpointManager> containers = context.getBeansOfType(JmsMessageEndpointManager.class); assertTrue("listener3 not found", containers.containsKey("listener3")); JmsMessageEndpointManager listener3 = containers.get("listener3"); assertEquals("Wrong resource adapter", context.getBean("testResourceAdapter"), listener3.getResourceAdapter()); assertEquals("Wrong activation spec factory", context.getBean("testActivationSpecFactory"), new DirectFieldAccessor(listener3).getPropertyValue("activationSpecFactory")); Object endpointFactory = new DirectFieldAccessor(listener3).getPropertyValue("endpointFactory"); Object messageListener = new DirectFieldAccessor(endpointFactory).getPropertyValue("messageListener"); assertEquals("Wrong message listener", MessageListenerAdapter.class, messageListener.getClass()); MessageListenerAdapter adapter = (MessageListenerAdapter) messageListener; DirectFieldAccessor adapterFieldAccessor = new DirectFieldAccessor(adapter); assertEquals("Message converter not set properly", context.getBean("testMessageConverter"), adapterFieldAccessor.getPropertyValue("messageConverter")); assertEquals("Wrong delegate", context.getBean("testBean1"), adapterFieldAccessor.getPropertyValue("delegate")); assertEquals("Wrong method name", "setName", adapterFieldAccessor.getPropertyValue("defaultListenerMethod")); } @Test public void testJmsContainerFactoryConfiguration() { Map<String, DefaultJmsListenerContainerFactory> containers = context.getBeansOfType(DefaultJmsListenerContainerFactory.class); DefaultJmsListenerContainerFactory factory = containers.get("testJmsFactory"); assertNotNull("No factory registered with testJmsFactory id", factory); DefaultMessageListenerContainer container = factory.createListenerContainer(createDummyEndpoint()); assertEquals("explicit connection factory not set", context.getBean(EXPLICIT_CONNECTION_FACTORY), container.getConnectionFactory()); assertEquals("explicit destination resolver not set", context.getBean("testDestinationResolver"), container.getDestinationResolver()); assertEquals("explicit message converter not set", context.getBean("testMessageConverter"), container.getMessageConverter()); assertEquals("Wrong pub/sub", true, container.isPubSubDomain()); assertEquals("Wrong durable flag", true, container.isSubscriptionDurable()); assertEquals("wrong cache", DefaultMessageListenerContainer.CACHE_CONNECTION, container.getCacheLevel()); assertEquals("wrong concurrency", 3, container.getConcurrentConsumers()); assertEquals("wrong concurrency", 5, container.getMaxConcurrentConsumers()); assertEquals("wrong prefetch", 50, container.getMaxMessagesPerTask()); assertEquals("Wrong phase", 99, container.getPhase()); assertSame(context.getBean("testBackOff"), new DirectFieldAccessor(container).getPropertyValue("backOff")); } @Test public void testJcaContainerFactoryConfiguration() { Map<String, DefaultJcaListenerContainerFactory> containers = context.getBeansOfType(DefaultJcaListenerContainerFactory.class); DefaultJcaListenerContainerFactory factory = containers.get("testJcaFactory"); assertNotNull("No factory registered with testJcaFactory id", factory); JmsMessageEndpointManager container = factory.createListenerContainer(createDummyEndpoint()); assertEquals("explicit resource adapter not set", context.getBean("testResourceAdapter"),container.getResourceAdapter()); assertEquals("explicit message converter not set", context.getBean("testMessageConverter"), container.getActivationSpecConfig().getMessageConverter()); assertEquals("Wrong pub/sub", true, container.isPubSubDomain()); assertEquals("wrong concurrency", 5, container.getActivationSpecConfig().getMaxConcurrency()); assertEquals("Wrong prefetch", 50, container.getActivationSpecConfig().getPrefetchSize()); assertEquals("Wrong phase", 77, container.getPhase()); } @Test public void testListeners() throws Exception { TestBean testBean1 = context.getBean("testBean1", TestBean.class); TestBean testBean2 = context.getBean("testBean2", TestBean.class); TestMessageListener testBean3 = context.getBean("testBean3", TestMessageListener.class); assertNull(testBean1.getName()); assertNull(testBean2.getName()); assertNull(testBean3.message); TextMessage message1 = mock(TextMessage.class); given(message1.getText()).willReturn("Test1"); MessageListener listener1 = getListener("listener1"); listener1.onMessage(message1); assertEquals("Test1", testBean1.getName()); TextMessage message2 = mock(TextMessage.class); given(message2.getText()).willReturn("Test2"); MessageListener listener2 = getListener("listener2"); listener2.onMessage(message2); assertEquals("Test2", testBean2.getName()); TextMessage message3 = mock(TextMessage.class); MessageListener listener3 = getListener(DefaultMessageListenerContainer.class.getName() + "#0"); listener3.onMessage(message3); assertSame(message3, testBean3.message); } @Test public void testRecoveryInterval() { Object testBackOff = context.getBean("testBackOff"); BackOff backOff1 = getBackOff("listener1"); BackOff backOff2 = getBackOff("listener2"); long recoveryInterval3 = getRecoveryInterval(DefaultMessageListenerContainer.class.getName() + "#0"); assertSame(testBackOff, backOff1); assertSame(testBackOff, backOff2); assertEquals(DefaultMessageListenerContainer.DEFAULT_RECOVERY_INTERVAL, recoveryInterval3); } @Test public void testConcurrency() { // JMS DefaultMessageListenerContainer listener0 = this.context .getBean(DefaultMessageListenerContainer.class.getName() + "#0", DefaultMessageListenerContainer.class); DefaultMessageListenerContainer listener1 = this.context .getBean("listener1", DefaultMessageListenerContainer.class); DefaultMessageListenerContainer listener2 = this.context .getBean("listener2", DefaultMessageListenerContainer.class); assertEquals("Wrong concurrency on listener using placeholder", 2, listener0.getConcurrentConsumers()); assertEquals("Wrong concurrency on listener using placeholder", 3, listener0.getMaxConcurrentConsumers()); assertEquals("Wrong concurrency on listener1", 3, listener1.getConcurrentConsumers()); assertEquals("Wrong max concurrency on listener1", 5, listener1.getMaxConcurrentConsumers()); assertEquals("Wrong custom concurrency on listener2", 5, listener2.getConcurrentConsumers()); assertEquals("Wrong custom max concurrency on listener2", 10, listener2.getMaxConcurrentConsumers()); // JCA JmsMessageEndpointManager listener3 = this.context .getBean("listener3", JmsMessageEndpointManager.class); JmsMessageEndpointManager listener4 = this.context .getBean("listener4", JmsMessageEndpointManager.class); assertEquals("Wrong concurrency on listener3", 5, listener3.getActivationSpecConfig().getMaxConcurrency()); assertEquals("Wrong custom concurrency on listener4", 7, listener4.getActivationSpecConfig().getMaxConcurrency()); } @Test public void testResponseDestination() { // JMS DefaultMessageListenerContainer listener1 = this.context .getBean("listener1", DefaultMessageListenerContainer.class); DefaultMessageListenerContainer listener2 = this.context .getBean("listener2", DefaultMessageListenerContainer.class); assertEquals("Wrong destination type on listener1", true, listener1.isPubSubDomain()); assertEquals("Wrong destination type on listener2", true, listener2.isPubSubDomain()); assertEquals("Wrong response destination type on listener1", false, listener1.isReplyPubSubDomain()); assertEquals("Wrong response destination type on listener2", false, listener2.isReplyPubSubDomain()); // JCA JmsMessageEndpointManager listener3 = this.context .getBean("listener3", JmsMessageEndpointManager.class); JmsMessageEndpointManager listener4 = this.context .getBean("listener4", JmsMessageEndpointManager.class); assertEquals("Wrong destination type on listener3", true, listener3.isPubSubDomain()); assertEquals("Wrong destination type on listener4", true, listener4.isPubSubDomain()); assertEquals("Wrong response destination type on listener3", false, listener3.isReplyPubSubDomain()); assertEquals("Wrong response destination type on listener4", false, listener4.isReplyPubSubDomain()); } @Test public void testErrorHandlers() { ErrorHandler expected = this.context.getBean("testErrorHandler", ErrorHandler.class); ErrorHandler errorHandler1 = getErrorHandler("listener1"); ErrorHandler errorHandler2 = getErrorHandler("listener2"); ErrorHandler defaultErrorHandler = getErrorHandler(DefaultMessageListenerContainer.class.getName() + "#0"); assertSame(expected, errorHandler1); assertSame(expected, errorHandler2); assertNull(defaultErrorHandler); } @Test public void testPhases() { int phase1 = getPhase("listener1"); int phase2 = getPhase("listener2"); int phase3 = getPhase("listener3"); int phase4 = getPhase("listener4"); int defaultPhase = getPhase(DefaultMessageListenerContainer.class.getName() + "#0"); assertEquals(99, phase1); assertEquals(99, phase2); assertEquals(77, phase3); assertEquals(77, phase4); assertEquals(Integer.MAX_VALUE, defaultPhase); } @Test public void testComponentRegistration() { assertTrue("Parser should have registered a component named 'listener1'", context.containsComponentDefinition("listener1")); assertTrue("Parser should have registered a component named 'listener2'", context.containsComponentDefinition("listener2")); assertTrue("Parser should have registered a component named 'listener3'", context.containsComponentDefinition("listener3")); assertTrue("Parser should have registered a component named '" + DefaultMessageListenerContainer.class.getName() + "#0'", context.containsComponentDefinition(DefaultMessageListenerContainer.class.getName() + "#0")); assertTrue("Parser should have registered a component named '" + JmsMessageEndpointManager.class.getName() + "#0'", context.containsComponentDefinition(JmsMessageEndpointManager.class.getName() + "#0")); assertTrue("Parser should have registered a component named 'testJmsFactory", context.containsComponentDefinition("testJmsFactory")); assertTrue("Parser should have registered a component named 'testJcaFactory", context.containsComponentDefinition("testJcaFactory")); assertTrue("Parser should have registered a component named 'testJcaFactory", context.containsComponentDefinition("onlyJmsFactory")); } @Test public void testSourceExtraction() { Iterator<ComponentDefinition> iterator = context.getRegisteredComponents(); while (iterator.hasNext()) { ComponentDefinition compDef = iterator.next(); assertNotNull("CompositeComponentDefinition '" + compDef.getName() + "' has no source attachment", compDef.getSource()); validateComponentDefinition(compDef); } } private void validateComponentDefinition(ComponentDefinition compDef) { BeanDefinition[] beanDefs = compDef.getBeanDefinitions(); for (BeanDefinition beanDef : beanDefs) { assertNotNull("BeanDefinition has no source attachment", beanDef.getSource()); } } private MessageListener getListener(String containerBeanName) { DefaultMessageListenerContainer container = this.context.getBean(containerBeanName, DefaultMessageListenerContainer.class); return (MessageListener) container.getMessageListener(); } private ErrorHandler getErrorHandler(String containerBeanName) { DefaultMessageListenerContainer container = this.context.getBean(containerBeanName, DefaultMessageListenerContainer.class); return (ErrorHandler) new DirectFieldAccessor(container).getPropertyValue("errorHandler"); } private BackOff getBackOff(String containerBeanName) { DefaultMessageListenerContainer container = this.context.getBean(containerBeanName, DefaultMessageListenerContainer.class); return (BackOff) new DirectFieldAccessor(container).getPropertyValue("backOff"); } private long getRecoveryInterval(String containerBeanName) { BackOff backOff = getBackOff(containerBeanName); assertEquals(FixedBackOff.class, backOff.getClass()); return ((FixedBackOff)backOff).getInterval(); } private int getPhase(String containerBeanName) { Object container = this.context.getBean(containerBeanName); if (!(container instanceof Phased)) { throw new IllegalStateException("Container '" + containerBeanName + "' does not implement Phased."); } return ((Phased) container).getPhase(); } private JmsListenerEndpoint createDummyEndpoint() { SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint(); endpoint.setMessageListener(new MessageListenerAdapter()); endpoint.setDestination("testQueue"); return endpoint; } public static class TestMessageListener implements MessageListener { public Message message; @Override public void onMessage(Message message) { this.message = message; } } /** * Internal extension that registers a {@link ReaderEventListener} to store * registered {@link ComponentDefinition}s. */ private static class ToolingTestApplicationContext extends ClassPathXmlApplicationContext { private Set<ComponentDefinition> registeredComponents; public ToolingTestApplicationContext(String path, Class<?> clazz) { super(path, clazz); } @Override protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { this.registeredComponents = new HashSet<>(); beanDefinitionReader.setEventListener(new StoringReaderEventListener(this.registeredComponents)); beanDefinitionReader.setSourceExtractor(new PassThroughSourceExtractor()); } public boolean containsComponentDefinition(String name) { for (ComponentDefinition cd : this.registeredComponents) { if (cd instanceof CompositeComponentDefinition) { ComponentDefinition[] innerCds = ((CompositeComponentDefinition) cd).getNestedComponents(); for (ComponentDefinition innerCd : innerCds) { if (innerCd.getName().equals(name)) { return true; } } } else { if (cd.getName().equals(name)) { return true; } } } return false; } public Iterator<ComponentDefinition> getRegisteredComponents() { return this.registeredComponents.iterator(); } } private static class StoringReaderEventListener extends EmptyReaderEventListener { protected final Set<ComponentDefinition> registeredComponents; public StoringReaderEventListener(Set<ComponentDefinition> registeredComponents) { this.registeredComponents = registeredComponents; } @Override public void componentRegistered(ComponentDefinition componentDefinition) { this.registeredComponents.add(componentDefinition); } } static class TestErrorHandler implements ErrorHandler { @Override public void handleError(Throwable t) { } } }