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