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.listener.endpoint; 018 019import javax.jms.MessageListener; 020import javax.resource.ResourceException; 021 022import org.springframework.beans.factory.BeanNameAware; 023import org.springframework.jca.endpoint.GenericMessageEndpointManager; 024import org.springframework.jms.listener.MessageListenerContainer; 025import org.springframework.jms.support.QosSettings; 026import org.springframework.jms.support.converter.MessageConverter; 027import org.springframework.jms.support.destination.DestinationResolver; 028import org.springframework.lang.Nullable; 029 030/** 031 * Extension of the generic JCA 1.5 032 * {@link org.springframework.jca.endpoint.GenericMessageEndpointManager}, 033 * adding JMS-specific support for ActivationSpec configuration. 034 * 035 * <p>Allows for defining a common {@link JmsActivationSpecConfig} object 036 * that gets converted into a provider-specific JCA 1.5 ActivationSpec 037 * object for activating the endpoint. 038 * 039 * <p><b>NOTE:</b> This JCA-based endpoint manager supports standard JMS 040 * {@link javax.jms.MessageListener} endpoints only. It does <i>not</i> support 041 * Spring's {@link org.springframework.jms.listener.SessionAwareMessageListener} 042 * variant, simply because the JCA endpoint management contract does not allow 043 * for obtaining the current JMS {@link javax.jms.Session}. 044 * 045 * @author Juergen Hoeller 046 * @author Stephane Nicoll 047 * @since 2.5 048 * @see javax.jms.MessageListener 049 * @see #setActivationSpecConfig 050 * @see JmsActivationSpecConfig 051 * @see JmsActivationSpecFactory 052 * @see JmsMessageEndpointFactory 053 */ 054public class JmsMessageEndpointManager extends GenericMessageEndpointManager 055 implements BeanNameAware, MessageListenerContainer { 056 057 private final JmsMessageEndpointFactory endpointFactory = new JmsMessageEndpointFactory(); 058 059 private boolean messageListenerSet = false; 060 061 private JmsActivationSpecFactory activationSpecFactory = new DefaultJmsActivationSpecFactory(); 062 063 @Nullable 064 private JmsActivationSpecConfig activationSpecConfig; 065 066 067 /** 068 * Set the JMS MessageListener for this endpoint. 069 * <p>This is a shortcut for configuring a dedicated JmsMessageEndpointFactory. 070 * @see JmsMessageEndpointFactory#setMessageListener 071 */ 072 public void setMessageListener(MessageListener messageListener) { 073 this.endpointFactory.setMessageListener(messageListener); 074 this.messageListenerSet = true; 075 } 076 077 /** 078 * Return the JMS MessageListener for this endpoint. 079 */ 080 public MessageListener getMessageListener() { 081 return this.endpointFactory.getMessageListener(); 082 } 083 084 /** 085 * Set the XA transaction manager to use for wrapping endpoint 086 * invocations, enlisting the endpoint resource in each such transaction. 087 * <p>The passed-in object may be a transaction manager which implements 088 * Spring's {@link org.springframework.transaction.jta.TransactionFactory} 089 * interface, or a plain {@link javax.transaction.TransactionManager}. 090 * <p>If no transaction manager is specified, the endpoint invocation 091 * will simply not be wrapped in an XA transaction. Consult your 092 * resource provider's ActivationSpec documentation for the local 093 * transaction options of your particular provider. 094 * <p>This is a shortcut for configuring a dedicated JmsMessageEndpointFactory. 095 * @see JmsMessageEndpointFactory#setTransactionManager 096 */ 097 public void setTransactionManager(Object transactionManager) { 098 this.endpointFactory.setTransactionManager(transactionManager); 099 } 100 101 /** 102 * Set the factory for concrete JCA 1.5 ActivationSpec objects, 103 * creating JCA ActivationSpecs based on 104 * {@link #setActivationSpecConfig JmsActivationSpecConfig} objects. 105 * <p>This factory is dependent on the concrete JMS provider, e.g. on ActiveMQ. 106 * The default implementation simply guesses the ActivationSpec class name 107 * from the provider's class name (e.g. "ActiveMQResourceAdapter" -> 108 * "ActiveMQActivationSpec" in the same package), and populates the 109 * ActivationSpec properties as suggested by the JCA 1.5 specification 110 * (plus a couple of autodetected vendor-specific properties). 111 * @see DefaultJmsActivationSpecFactory 112 */ 113 public void setActivationSpecFactory(@Nullable JmsActivationSpecFactory activationSpecFactory) { 114 this.activationSpecFactory = 115 (activationSpecFactory != null ? activationSpecFactory : new DefaultJmsActivationSpecFactory()); 116 } 117 118 /** 119 * Set the DestinationResolver to use for resolving destination names 120 * into the JCA 1.5 ActivationSpec "destination" property. 121 * <p>If not specified, destination names will simply be passed in as Strings. 122 * If specified, destination names will be resolved into Destination objects first. 123 * <p>Note that a DestinationResolver is usually specified on the JmsActivationSpecFactory 124 * (see {@link StandardJmsActivationSpecFactory#setDestinationResolver}). This is simply 125 * a shortcut for parameterizing the default JmsActivationSpecFactory; it will replace 126 * any custom JmsActivationSpecFactory that might have been set before. 127 * @see StandardJmsActivationSpecFactory#setDestinationResolver 128 */ 129 public void setDestinationResolver(DestinationResolver destinationResolver) { 130 DefaultJmsActivationSpecFactory factory = new DefaultJmsActivationSpecFactory(); 131 factory.setDestinationResolver(destinationResolver); 132 this.activationSpecFactory = factory; 133 } 134 135 /** 136 * Specify the {@link JmsActivationSpecConfig} object that this endpoint manager 137 * should use for activating its listener. 138 * <p>This config object will be turned into a concrete JCA 1.5 ActivationSpec 139 * object through a {@link #setActivationSpecFactory JmsActivationSpecFactory}. 140 */ 141 public void setActivationSpecConfig(@Nullable JmsActivationSpecConfig activationSpecConfig) { 142 this.activationSpecConfig = activationSpecConfig; 143 } 144 145 /** 146 * Return the {@link JmsActivationSpecConfig} object that this endpoint manager 147 * should use for activating its listener. Return {@code null} if none is set. 148 */ 149 @Nullable 150 public JmsActivationSpecConfig getActivationSpecConfig() { 151 return this.activationSpecConfig; 152 } 153 154 /** 155 * Set the name of this message endpoint. Populated with the bean name 156 * automatically when defined within Spring's bean factory. 157 */ 158 @Override 159 public void setBeanName(String beanName) { 160 this.endpointFactory.setBeanName(beanName); 161 } 162 163 164 @Override 165 public void afterPropertiesSet() throws ResourceException { 166 if (getResourceAdapter() == null) { 167 throw new IllegalArgumentException("Property 'resourceAdapter' is required"); 168 } 169 if (this.messageListenerSet) { 170 setMessageEndpointFactory(this.endpointFactory); 171 } 172 if (this.activationSpecConfig != null) { 173 setActivationSpec( 174 this.activationSpecFactory.createActivationSpec(getResourceAdapter(), this.activationSpecConfig)); 175 } 176 177 super.afterPropertiesSet(); 178 } 179 180 181 @Override 182 public void setupMessageListener(Object messageListener) { 183 if (messageListener instanceof MessageListener) { 184 setMessageListener((MessageListener) messageListener); 185 } 186 else { 187 throw new IllegalArgumentException("Unsupported message listener '" + 188 messageListener.getClass().getName() + "': only '" + MessageListener.class.getName() + 189 "' type is supported"); 190 } 191 } 192 193 @Override 194 @Nullable 195 public MessageConverter getMessageConverter() { 196 JmsActivationSpecConfig config = getActivationSpecConfig(); 197 if (config != null) { 198 return config.getMessageConverter(); 199 } 200 return null; 201 } 202 203 @Override 204 @Nullable 205 public DestinationResolver getDestinationResolver() { 206 if (this.activationSpecFactory instanceof StandardJmsActivationSpecFactory) { 207 return ((StandardJmsActivationSpecFactory) this.activationSpecFactory).getDestinationResolver(); 208 } 209 return null; 210 } 211 212 @Override 213 public boolean isPubSubDomain() { 214 JmsActivationSpecConfig config = getActivationSpecConfig(); 215 if (config != null) { 216 return config.isPubSubDomain(); 217 } 218 throw new IllegalStateException("Could not determine pubSubDomain - no activation spec config is set"); 219 } 220 221 @Override 222 public boolean isReplyPubSubDomain() { 223 JmsActivationSpecConfig config = getActivationSpecConfig(); 224 if (config != null) { 225 return config.isReplyPubSubDomain(); 226 } 227 throw new IllegalStateException("Could not determine reply pubSubDomain - no activation spec config is set"); 228 } 229 230 @Override 231 @Nullable 232 public QosSettings getReplyQosSettings() { 233 JmsActivationSpecConfig config = getActivationSpecConfig(); 234 if (config != null) { 235 return config.getReplyQosSettings(); 236 } 237 throw new IllegalStateException("Could not determine reply qosSettings - no activation spec config is set"); 238 } 239}