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