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.jca.endpoint; 018 019import javax.resource.ResourceException; 020import javax.resource.spi.ActivationSpec; 021import javax.resource.spi.ResourceAdapter; 022import javax.resource.spi.endpoint.MessageEndpointFactory; 023 024import org.springframework.beans.factory.DisposableBean; 025import org.springframework.beans.factory.InitializingBean; 026import org.springframework.context.SmartLifecycle; 027 028/** 029 * Generic bean that manages JCA 1.5 message endpoints within a Spring 030 * application context, activating and deactivating the endpoint as part 031 * of the application context's lifecycle. 032 * 033 * <p>This class is completely generic in that it may work with any 034 * ResourceAdapter, any MessageEndpointFactory, and any ActivationSpec. 035 * It can be configured in standard bean style, for example through 036 * Spring's XML bean definition format, as follows: 037 * 038 * <pre class="code"> 039 * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"> 040 * <property name="resourceAdapter" ref="resourceAdapter"/> 041 * <property name="messageEndpointFactory"> 042 * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"> 043 * <property name="messageListener" ref="messageListener"/> 044 * </bean> 045 * </property> 046 * <property name="activationSpec"> 047 * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec"> 048 * <property name="destination" value="myQueue"/> 049 * <property name="destinationType" value="javax.jms.Queue"/> 050 * </bean> 051 * </property> 052 * </bean></pre> 053 * 054 * In this example, Spring's own {@link GenericMessageEndpointFactory} is used 055 * to point to a standard message listener object that happens to be supported 056 * by the specified target ResourceAdapter: in this case, a JMS 057 * {@link javax.jms.MessageListener} object as supported by the ActiveMQ 058 * message broker, defined as a Spring bean: 059 * 060 * <pre class="code"> 061 * <bean id="messageListener" class="com.myorg.messaging.myMessageListener"> 062 * ... 063 * </bean></pre> 064 * 065 * The target ResourceAdapter may be configured as a local Spring bean as well 066 * (the typical case) or obtained from JNDI (e.g. on WebLogic). For the 067 * example above, a local ResourceAdapter bean could be defined as follows 068 * (matching the "resourceAdapter" bean reference above): 069 * 070 * <pre class="code"> 071 * <bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean"> 072 * <property name="resourceAdapter"> 073 * <bean class="org.apache.activemq.ra.ActiveMQResourceAdapter"> 074 * <property name="serverUrl" value="tcp://localhost:61616"/> 075 * </bean> 076 * </property> 077 * <property name="workManager"> 078 * <bean class="org.springframework.jca.work.SimpleTaskWorkManager"/> 079 * </property> 080 * </bean></pre> 081 * 082 * For a different target resource, the configuration would simply point to a 083 * different ResourceAdapter and a different ActivationSpec object (which are 084 * both specific to the resource provider), and possibly a different message 085 * listener (e.g. a CCI {@link javax.resource.cci.MessageListener} for a 086 * resource adapter which is based on the JCA Common Client Interface). 087 * 088 * <p>The asynchronous execution strategy can be customized through the 089 * "workManager" property on the ResourceAdapterFactoryBean (as shown above). 090 * Check out {@link org.springframework.jca.work.SimpleTaskWorkManager}'s 091 * javadoc for its configuration options; alternatively, any other 092 * JCA-compliant WorkManager can be used (e.g. Geronimo's). 093 * 094 * <p>Transactional execution is a responsibility of the concrete message endpoint, 095 * as built by the specified MessageEndpointFactory. {@link GenericMessageEndpointFactory} 096 * supports XA transaction participation through its "transactionManager" property, 097 * typically with a Spring {@link org.springframework.transaction.jta.JtaTransactionManager} 098 * or a plain {@link javax.transaction.TransactionManager} implementation specified there. 099 * 100 * <pre class="code"> 101 * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"> 102 * <property name="resourceAdapter" ref="resourceAdapter"/> 103 * <property name="messageEndpointFactory"> 104 * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"> 105 * <property name="messageListener" ref="messageListener"/> 106 * <property name="transactionManager" ref="transactionManager"/> 107 * </bean> 108 * </property> 109 * <property name="activationSpec"> 110 * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec"> 111 * <property name="destination" value="myQueue"/> 112 * <property name="destinationType" value="javax.jms.Queue"/> 113 * </bean> 114 * </property> 115 * </bean> 116 * 117 * <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/></pre> 118 * 119 * Alternatively, check out your resource provider's ActivationSpec object, 120 * which should support local transactions through a provider-specific config flag, 121 * e.g. ActiveMQActivationSpec's "useRAManagedTransaction" bean property. 122 * 123 * <pre class="code"> 124 * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"> 125 * <property name="resourceAdapter" ref="resourceAdapter"/> 126 * <property name="messageEndpointFactory"> 127 * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"> 128 * <property name="messageListener" ref="messageListener"/> 129 * </bean> 130 * </property> 131 * <property name="activationSpec"> 132 * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec"> 133 * <property name="destination" value="myQueue"/> 134 * <property name="destinationType" value="javax.jms.Queue"/> 135 * <property name="useRAManagedTransaction" value="true"/> 136 * </bean> 137 * </property> 138 * </bean></pre> 139 * 140 * @author Juergen Hoeller 141 * @since 2.5 142 * @see javax.resource.spi.ResourceAdapter#endpointActivation 143 * @see javax.resource.spi.ResourceAdapter#endpointDeactivation 144 * @see javax.resource.spi.endpoint.MessageEndpointFactory 145 * @see javax.resource.spi.ActivationSpec 146 */ 147public class GenericMessageEndpointManager implements SmartLifecycle, InitializingBean, DisposableBean { 148 149 private ResourceAdapter resourceAdapter; 150 151 private MessageEndpointFactory messageEndpointFactory; 152 153 private ActivationSpec activationSpec; 154 155 private boolean autoStartup = true; 156 157 private int phase = Integer.MAX_VALUE; 158 159 private volatile boolean running = false; 160 161 private final Object lifecycleMonitor = new Object(); 162 163 164 /** 165 * Set the JCA ResourceAdapter to manage endpoints for. 166 */ 167 public void setResourceAdapter(ResourceAdapter resourceAdapter) { 168 this.resourceAdapter = resourceAdapter; 169 } 170 171 /** 172 * Return the JCA ResourceAdapter to manage endpoints for. 173 */ 174 public ResourceAdapter getResourceAdapter() { 175 return this.resourceAdapter; 176 } 177 178 /** 179 * Set the JCA MessageEndpointFactory to activate, pointing to a 180 * MessageListener object that the endpoints will delegate to. 181 * <p>A MessageEndpointFactory instance may be shared across multiple 182 * endpoints (i.e. multiple GenericMessageEndpointManager instances), 183 * with different {@link #setActivationSpec ActivationSpec} objects applied. 184 * @see GenericMessageEndpointFactory#setMessageListener 185 */ 186 public void setMessageEndpointFactory(MessageEndpointFactory messageEndpointFactory) { 187 this.messageEndpointFactory = messageEndpointFactory; 188 } 189 190 /** 191 * Return the JCA MessageEndpointFactory to activate. 192 */ 193 public MessageEndpointFactory getMessageEndpointFactory() { 194 return this.messageEndpointFactory; 195 } 196 197 /** 198 * Set the JCA ActivationSpec to use for activating the endpoint. 199 * <p>Note that this ActivationSpec instance should not be shared 200 * across multiple ResourceAdapter instances. 201 */ 202 public void setActivationSpec(ActivationSpec activationSpec) { 203 this.activationSpec = activationSpec; 204 } 205 206 /** 207 * Return the JCA ActivationSpec to use for activating the endpoint. 208 */ 209 public ActivationSpec getActivationSpec() { 210 return this.activationSpec; 211 } 212 213 /** 214 * Set whether to auto-start the endpoint activation after this endpoint 215 * manager has been initialized and the context has been refreshed. 216 * <p>Default is "true". Turn this flag off to defer the endpoint 217 * activation until an explicit {@link #start()} call. 218 */ 219 public void setAutoStartup(boolean autoStartup) { 220 this.autoStartup = autoStartup; 221 } 222 223 /** 224 * Return the value for the 'autoStartup' property. If "true", this 225 * endpoint manager will start upon a ContextRefreshedEvent. 226 */ 227 @Override 228 public boolean isAutoStartup() { 229 return this.autoStartup; 230 } 231 232 /** 233 * Specify the phase in which this endpoint manager should be started 234 * and stopped. The startup order proceeds from lowest to highest, and 235 * the shutdown order is the reverse of that. By default this value is 236 * Integer.MAX_VALUE meaning that this endpoint manager starts as late 237 * as possible and stops as soon as possible. 238 */ 239 public void setPhase(int phase) { 240 this.phase = phase; 241 } 242 243 /** 244 * Return the phase in which this endpoint manager will be started and stopped. 245 */ 246 @Override 247 public int getPhase() { 248 return this.phase; 249 } 250 251 /** 252 * Prepares the message endpoint, and automatically activates it 253 * if the "autoStartup" flag is set to "true". 254 */ 255 @Override 256 public void afterPropertiesSet() throws ResourceException { 257 if (getResourceAdapter() == null) { 258 throw new IllegalArgumentException("Property 'resourceAdapter' is required"); 259 } 260 if (getMessageEndpointFactory() == null) { 261 throw new IllegalArgumentException("Property 'messageEndpointFactory' is required"); 262 } 263 ActivationSpec activationSpec = getActivationSpec(); 264 if (activationSpec == null) { 265 throw new IllegalArgumentException("Property 'activationSpec' is required"); 266 } 267 268 if (activationSpec.getResourceAdapter() == null) { 269 activationSpec.setResourceAdapter(getResourceAdapter()); 270 } 271 else if (activationSpec.getResourceAdapter() != getResourceAdapter()) { 272 throw new IllegalArgumentException("ActivationSpec [" + activationSpec + 273 "] is associated with a different ResourceAdapter: " + activationSpec.getResourceAdapter()); 274 } 275 } 276 277 /** 278 * Activates the configured message endpoint. 279 */ 280 @Override 281 public void start() { 282 synchronized (this.lifecycleMonitor) { 283 if (!this.running) { 284 try { 285 getResourceAdapter().endpointActivation(getMessageEndpointFactory(), getActivationSpec()); 286 } 287 catch (ResourceException ex) { 288 throw new IllegalStateException("Could not activate message endpoint", ex); 289 } 290 this.running = true; 291 } 292 } 293 } 294 295 /** 296 * Deactivates the configured message endpoint. 297 */ 298 @Override 299 public void stop() { 300 synchronized (this.lifecycleMonitor) { 301 if (this.running) { 302 getResourceAdapter().endpointDeactivation(getMessageEndpointFactory(), getActivationSpec()); 303 this.running = false; 304 } 305 } 306 } 307 308 @Override 309 public void stop(Runnable callback) { 310 synchronized (this.lifecycleMonitor) { 311 stop(); 312 callback.run(); 313 } 314 } 315 316 /** 317 * Return whether the configured message endpoint is currently active. 318 */ 319 @Override 320 public boolean isRunning() { 321 return this.running; 322 } 323 324 /** 325 * Deactivates the message endpoint, preparing it for shutdown. 326 */ 327 @Override 328 public void destroy() { 329 stop(); 330 } 331 332}