001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jms.config; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.springframework.beans.factory.BeanFactory; 023import org.springframework.beans.factory.BeanFactoryAware; 024import org.springframework.beans.factory.InitializingBean; 025import org.springframework.beans.factory.config.ConfigurableBeanFactory; 026import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory; 027import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory; 028import org.springframework.util.Assert; 029 030/** 031 * Helper bean for registering {@link JmsListenerEndpoint} with a {@link JmsListenerEndpointRegistry}. 032 * 033 * @author Stephane Nicoll 034 * @author Juergen Hoeller 035 * @since 4.1 036 * @see org.springframework.jms.annotation.JmsListenerConfigurer 037 */ 038public class JmsListenerEndpointRegistrar implements BeanFactoryAware, InitializingBean { 039 040 private JmsListenerEndpointRegistry endpointRegistry; 041 042 private MessageHandlerMethodFactory messageHandlerMethodFactory; 043 044 private JmsListenerContainerFactory<?> containerFactory; 045 046 private String containerFactoryBeanName; 047 048 private BeanFactory beanFactory; 049 050 private final List<JmsListenerEndpointDescriptor> endpointDescriptors = 051 new ArrayList<JmsListenerEndpointDescriptor>(); 052 053 private boolean startImmediately; 054 055 private Object mutex = endpointDescriptors; 056 057 058 /** 059 * Set the {@link JmsListenerEndpointRegistry} instance to use. 060 */ 061 public void setEndpointRegistry(JmsListenerEndpointRegistry endpointRegistry) { 062 this.endpointRegistry = endpointRegistry; 063 } 064 065 /** 066 * Return the {@link JmsListenerEndpointRegistry} instance for this 067 * registrar, may be {@code null}. 068 */ 069 public JmsListenerEndpointRegistry getEndpointRegistry() { 070 return this.endpointRegistry; 071 } 072 073 /** 074 * Set the {@link MessageHandlerMethodFactory} to use to configure the message 075 * listener responsible to serve an endpoint detected by this processor. 076 * <p>By default, {@link DefaultMessageHandlerMethodFactory} is used and it 077 * can be configured further to support additional method arguments 078 * or to customize conversion and validation support. See 079 * {@link DefaultMessageHandlerMethodFactory} javadoc for more details. 080 */ 081 public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) { 082 this.messageHandlerMethodFactory = messageHandlerMethodFactory; 083 } 084 085 /** 086 * Return the custom {@link MessageHandlerMethodFactory} to use, if any. 087 */ 088 public MessageHandlerMethodFactory getMessageHandlerMethodFactory() { 089 return this.messageHandlerMethodFactory; 090 } 091 092 /** 093 * Set the {@link JmsListenerContainerFactory} to use in case a {@link JmsListenerEndpoint} 094 * is registered with a {@code null} container factory. 095 * <p>Alternatively, the bean name of the {@link JmsListenerContainerFactory} to use 096 * can be specified for a lazy lookup, see {@link #setContainerFactoryBeanName}. 097 */ 098 public void setContainerFactory(JmsListenerContainerFactory<?> containerFactory) { 099 this.containerFactory = containerFactory; 100 } 101 102 /** 103 * Set the bean name of the {@link JmsListenerContainerFactory} to use in case 104 * a {@link JmsListenerEndpoint} is registered with a {@code null} container factory. 105 * Alternatively, the container factory instance can be registered directly: 106 * see {@link #setContainerFactory(JmsListenerContainerFactory)}. 107 * @see #setBeanFactory 108 */ 109 public void setContainerFactoryBeanName(String containerFactoryBeanName) { 110 this.containerFactoryBeanName = containerFactoryBeanName; 111 } 112 113 /** 114 * A {@link BeanFactory} only needs to be available in conjunction with 115 * {@link #setContainerFactoryBeanName}. 116 */ 117 @Override 118 public void setBeanFactory(BeanFactory beanFactory) { 119 this.beanFactory = beanFactory; 120 if (beanFactory instanceof ConfigurableBeanFactory) { 121 this.mutex = ((ConfigurableBeanFactory) beanFactory).getSingletonMutex(); 122 } 123 } 124 125 126 @Override 127 public void afterPropertiesSet() { 128 registerAllEndpoints(); 129 } 130 131 protected void registerAllEndpoints() { 132 synchronized (this.mutex) { 133 for (JmsListenerEndpointDescriptor descriptor : this.endpointDescriptors) { 134 this.endpointRegistry.registerListenerContainer( 135 descriptor.endpoint, resolveContainerFactory(descriptor)); 136 } 137 this.startImmediately = true; // trigger immediate startup 138 } 139 } 140 141 private JmsListenerContainerFactory<?> resolveContainerFactory(JmsListenerEndpointDescriptor descriptor) { 142 if (descriptor.containerFactory != null) { 143 return descriptor.containerFactory; 144 } 145 else if (this.containerFactory != null) { 146 return this.containerFactory; 147 } 148 else if (this.containerFactoryBeanName != null) { 149 Assert.state(this.beanFactory != null, "BeanFactory must be set to obtain container factory by bean name"); 150 // Consider changing this if live change of the factory is required... 151 this.containerFactory = this.beanFactory.getBean( 152 this.containerFactoryBeanName, JmsListenerContainerFactory.class); 153 return this.containerFactory; 154 } 155 else { 156 throw new IllegalStateException("Could not resolve the " + 157 JmsListenerContainerFactory.class.getSimpleName() + " to use for [" + 158 descriptor.endpoint + "] no factory was given and no default is set."); 159 } 160 } 161 162 /** 163 * Register a new {@link JmsListenerEndpoint} alongside the 164 * {@link JmsListenerContainerFactory} to use to create the underlying container. 165 * <p>The {@code factory} may be {@code null} if the default factory has to be 166 * used for that endpoint. 167 */ 168 public void registerEndpoint(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> factory) { 169 Assert.notNull(endpoint, "Endpoint must be set"); 170 Assert.hasText(endpoint.getId(), "Endpoint id must be set"); 171 172 // Factory may be null, we defer the resolution right before actually creating the container 173 JmsListenerEndpointDescriptor descriptor = new JmsListenerEndpointDescriptor(endpoint, factory); 174 175 synchronized (this.mutex) { 176 if (this.startImmediately) { // register and start immediately 177 this.endpointRegistry.registerListenerContainer(descriptor.endpoint, 178 resolveContainerFactory(descriptor), true); 179 } 180 else { 181 this.endpointDescriptors.add(descriptor); 182 } 183 } 184 } 185 186 /** 187 * Register a new {@link JmsListenerEndpoint} using the default 188 * {@link JmsListenerContainerFactory} to create the underlying container. 189 * @see #setContainerFactory(JmsListenerContainerFactory) 190 * @see #registerEndpoint(JmsListenerEndpoint, JmsListenerContainerFactory) 191 */ 192 public void registerEndpoint(JmsListenerEndpoint endpoint) { 193 registerEndpoint(endpoint, null); 194 } 195 196 197 private static class JmsListenerEndpointDescriptor { 198 199 public final JmsListenerEndpoint endpoint; 200 201 public final JmsListenerContainerFactory<?> containerFactory; 202 203 public JmsListenerEndpointDescriptor(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> containerFactory) { 204 this.endpoint = endpoint; 205 this.containerFactory = containerFactory; 206 } 207 } 208 209}