/* * Copyright 2002-2007 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.jca.endpoint; import javax.resource.ResourceException; import javax.resource.spi.ActivationSpec; import javax.resource.spi.ResourceAdapter; import javax.resource.spi.endpoint.MessageEndpointFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.Lifecycle; /** * Generic bean that manages JCA 1.5 message endpoints within a Spring * application context, activating and deactivating the endpoint as part * of the application context's lifecycle. * * <p>This class is completely generic in that it may work with any * ResourceAdapter, any MessageEndpointFactory, and any ActivationSpec. * It can be configured in standard bean style, for example through * Spring's XML bean definition format, as follows: * * <pre class="code"> * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"> * <property name="resourceAdapter" ref="resourceAdapter"/> * <property name="messageEndpointFactory"> * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"> * <property name="messageListener" ref="messageListener"/> * </bean> * </property> * <property name="activationSpec"> * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec"> * <property name="destination" value="myQueue"/> * <property name="destinationType" value="javax.jms.Queue"/> * </bean> * </property> * </bean></pre> * * In this example, Spring's own {@link GenericMessageEndpointFactory} is used * to point to a standard message listener object that happens to be supported * by the specified target ResourceAdapter: in this case, a JMS * {@link javax.jms.MessageListener} object as supported by the ActiveMQ * message broker, defined as a Spring bean: * * <pre class="code"> * <bean id="messageListener" class="com.myorg.messaging.myMessageListener"> * ... * </bean></pre> * * The target ResourceAdapter may be configured as a local Spring bean as well * (the typical case) or obtained from JNDI (e.g. on WebLogic). For the * example above, a local ResourceAdapter bean could be defined as follows * (matching the "resourceAdapter" bean reference above): * * <pre class="code"> * <bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean"> * <property name="resourceAdapter"> * <bean class="org.apache.activemq.ra.ActiveMQResourceAdapter"> * <property name="serverUrl" value="tcp://localhost:61616"/> * </bean> * </property> * <property name="workManager"> * <bean class="org.springframework.jca.work.SimpleTaskWorkManager"/> * </property> * </bean></pre> * * For a different target resource, the configuration would simply point to a * different ResourceAdapter and a different ActivationSpec object (which are * both specific to the resource provider), and possibly a different message * listener (e.g. a CCI {@link javax.resource.cci.MessageListener} for a * resource adapter which is based on the JCA Common Client Interface). * * <p>The asynchronous execution strategy can be customized through the * "workManager" property on the ResourceAdapterFactoryBean (as shown above). * Check out {@link org.springframework.jca.work.SimpleTaskWorkManager}'s * javadoc for its configuration options; alternatively, any other * JCA-compliant WorkManager can be used (e.g. Geronimo's). * * <p>Transactional execution is a responsibility of the concrete message endpoint, * as built by the specified MessageEndpointFactory. {@link GenericMessageEndpointFactory} * supports XA transaction participation through its "transactionManager" property, * typically with a Spring {@link org.springframework.transaction.jta.JtaTransactionManager} * or a plain {@link javax.transaction.TransactionManager} implementation specified there. * * <pre class="code"> * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"> * <property name="resourceAdapter" ref="resourceAdapter"/> * <property name="messageEndpointFactory"> * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"> * <property name="messageListener" ref="messageListener"/> * <property name="transactionManager" ref="transactionManager"/> * </bean> * </property> * <property name="activationSpec"> * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec"> * <property name="destination" value="myQueue"/> * <property name="destinationType" value="javax.jms.Queue"/> * </bean> * </property> * </bean> * * <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/></pre> * * Alternatively, check out your resource provider's ActivationSpec object, * which should support local transactions through a provider-specific config flag, * e.g. ActiveMQActivationSpec's "useRAManagedTransaction" bean property. * * <pre class="code"> * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointManager"> * <property name="resourceAdapter" ref="resourceAdapter"/> * <property name="messageEndpointFactory"> * <bean class="org.springframework.jca.endpoint.GenericMessageEndpointFactory"> * <property name="messageListener" ref="messageListener"/> * </bean> * </property> * <property name="activationSpec"> * <bean class="org.apache.activemq.ra.ActiveMQActivationSpec"> * <property name="destination" value="myQueue"/> * <property name="destinationType" value="javax.jms.Queue"/> * <property name="useRAManagedTransaction" value="true"/> * </bean> * </property> * </bean></pre> * * @author Juergen Hoeller * @since 2.5 * @see javax.resource.spi.ResourceAdapter#endpointActivation * @see javax.resource.spi.ResourceAdapter#endpointDeactivation * @see javax.resource.spi.endpoint.MessageEndpointFactory * @see javax.resource.spi.ActivationSpec */ public class GenericMessageEndpointManager implements InitializingBean, Lifecycle, DisposableBean { private ResourceAdapter resourceAdapter; private MessageEndpointFactory messageEndpointFactory; private ActivationSpec activationSpec; private boolean autoStartup = true; private boolean running = false; private final Object lifecycleMonitor = new Object(); /** * Set the JCA ResourceAdapter to manage endpoints for. */ public void setResourceAdapter(ResourceAdapter resourceAdapter) { this.resourceAdapter = resourceAdapter; } /** * Return the JCA ResourceAdapter to manage endpoints for. */ public ResourceAdapter getResourceAdapter() { return this.resourceAdapter; } /** * Set the JCA MessageEndpointFactory to activate, pointing to a * MessageListener object that the endpoints will delegate to. * <p>A MessageEndpointFactory instance may be shared across multiple * endpoints (i.e. multiple GenericMessageEndpointManager instances), * with different {@link #setActivationSpec ActivationSpec} objects applied. * @see GenericMessageEndpointFactory#setMessageListener */ public void setMessageEndpointFactory(MessageEndpointFactory messageEndpointFactory) { this.messageEndpointFactory = messageEndpointFactory; } /** * Return the JCA MessageEndpointFactory to activate. */ public MessageEndpointFactory getMessageEndpointFactory() { return this.messageEndpointFactory; } /** * Set the JCA ActivationSpec to use for activating the endpoint. * <p>Note that this ActivationSpec instance should not be shared * across multiple ResourceAdapter instances. */ public void setActivationSpec(ActivationSpec activationSpec) { this.activationSpec = activationSpec; } /** * Return the JCA ActivationSpec to use for activating the endpoint. */ public ActivationSpec getActivationSpec() { return this.activationSpec; } /** * Set whether to auto-start the endpoint activation along with * this endpoint manager's initialization. * <p>Default is "true". Turn this flag off to defer the endpoint * activation until an explicit {#start()} call. */ public void setAutoStartup(boolean autoStartup) { this.autoStartup = autoStartup; } /** * Prepares the message endpoint, and automatically activates it * if the "autoStartup" flag is set to "true". */ public void afterPropertiesSet() throws ResourceException { if (getResourceAdapter() == null) { throw new IllegalArgumentException("Property 'resourceAdapter' is required"); } if (getMessageEndpointFactory() == null) { throw new IllegalArgumentException("Property 'messageEndpointFactory' is required"); } ActivationSpec activationSpec = getActivationSpec(); if (activationSpec == null) { throw new IllegalArgumentException("Property 'activationSpec' is required"); } if (activationSpec.getResourceAdapter() == null) { activationSpec.setResourceAdapter(getResourceAdapter()); } else if (activationSpec.getResourceAdapter() != getResourceAdapter()) { throw new IllegalArgumentException("ActivationSpec [" + activationSpec + "] is associated with a different ResourceAdapter: " + activationSpec.getResourceAdapter()); } if (this.autoStartup) { start(); } } /** * Activates the configured message endpoint. */ public void start() { synchronized (this.lifecycleMonitor) { if (!this.running) { try { getResourceAdapter().endpointActivation(getMessageEndpointFactory(), getActivationSpec()); } catch (ResourceException ex) { IllegalStateException wrapped = new IllegalStateException("Could not activate message endpoint"); wrapped.initCause(ex); throw wrapped; } this.running = true; } } } /** * Deactivates the configured message endpoint. */ public void stop() { synchronized (this.lifecycleMonitor) { if (this.running) { getResourceAdapter().endpointDeactivation(getMessageEndpointFactory(), getActivationSpec()); this.running = false; } } } /** * Return whether the configured message endpoint is currently active. */ public boolean isRunning() { synchronized (this.lifecycleMonitor) { return this.running; } } /** * Deactivates the message endpoint, preparing it for shutdown. */ public void destroy() { stop(); } }