/*
 * 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.jms.listener.endpoint;

import javax.jms.MessageListener;
import javax.resource.ResourceException;

import org.springframework.jca.endpoint.GenericMessageEndpointManager;
import org.springframework.jms.support.destination.DestinationResolver;

/**
 * Extension of the generic JCA 1.5
 * {@link org.springframework.jca.endpoint.GenericMessageEndpointManager},
 * adding JMS-specific support for ActivationSpec configuration.
 *
 * <p>Allows for defining a common {@link JmsActivationSpecConfig} object
 * that gets converted into a provider-specific JCA 1.5 ActivationSpec
 * object for activating the endpoint.
 *
 * <p><b>NOTE:</b> This JCA-based endpoint manager supports standard JMS
 * {@link javax.jms.MessageListener} endpoints only. It does <i>not</i> support
 * Spring's {@link org.springframework.jms.listener.SessionAwareMessageListener}
 * variant, simply because the JCA endpoint management contract does not allow
 * for obtaining the current JMS {@link javax.jms.Session}.
 *
 * @author Juergen Hoeller
 * @since 2.5
 * @see javax.jms.MessageListener
 * @see #setActivationSpecConfig
 * @see JmsActivationSpecConfig
 * @see JmsActivationSpecFactory
 * @see JmsMessageEndpointFactory
 */
public class JmsMessageEndpointManager extends GenericMessageEndpointManager {

	private final JmsMessageEndpointFactory endpointFactory = new JmsMessageEndpointFactory();

	private boolean messageListenerSet = false;

	private JmsActivationSpecFactory activationSpecFactory = new DefaultJmsActivationSpecFactory();

	private JmsActivationSpecConfig activationSpecConfig;


	/**
	 * Set the JMS MessageListener for this endpoint.
	 * <p>This is a shortcut for configuring a dedicated JmsMessageEndpointFactory.
	 * @see JmsMessageEndpointFactory#setMessageListener
	 */
	public void setMessageListener(MessageListener messageListener) {
		this.endpointFactory.setMessageListener(messageListener);
		this.messageListenerSet = true;
	}

	/**
	 * Set the XA transaction manager to use for wrapping endpoint
	 * invocations, enlisting the endpoint resource in each such transaction.
	 * <p>The passed-in object may be a transaction manager which implements
	 * Spring's {@link org.springframework.transaction.jta.TransactionFactory}
	 * interface, or a plain {@link javax.transaction.TransactionManager}.
	 * <p>If no transaction manager is specified, the endpoint invocation
	 * will simply not be wrapped in an XA transaction. Consult your
	 * resource provider's ActivationSpec documentation for the local
	 * transaction options of your particular provider.
	 * <p>This is a shortcut for configuring a dedicated JmsMessageEndpointFactory.
	 * @see JmsMessageEndpointFactory#setTransactionManager
	 */
	public void setTransactionManager(Object transactionManager) {
		this.endpointFactory.setTransactionManager(transactionManager);
	}

	/**
	 * Set the factory for concrete JCA 1.5 ActivationSpec objects,
	 * creating JCA ActivationSpecs based on
	 * {@link #setActivationSpecConfig JmsActivationSpecConfig} objects.
	 * <p>This factory is dependent on the concrete JMS provider, e.g. on ActiveMQ.
	 * The default implementation simply guesses the ActivationSpec class name
	 * from the provider's class name (e.g. "ActiveMQResourceAdapter" ->
	 * "ActiveMQActivationSpec" in the same package), and populates the
	 * ActivationSpec properties as suggested by the JCA 1.5 specification
	 * (plus a couple of autodetected vendor-specific properties).
	 * @see DefaultJmsActivationSpecFactory
	 */
	public void setActivationSpecFactory(JmsActivationSpecFactory activationSpecFactory) {
		this.activationSpecFactory =
				(activationSpecFactory != null ? activationSpecFactory : new DefaultJmsActivationSpecFactory());
	}

	/**
	 * Set the DestinationResolver to use for resolving destination names
	 * into the JCA 1.5 ActivationSpec "destination" property.
	 * <p>If not specified, destination names will simply be passed in as Strings.
	 * If specified, destination names will be resolved into Destination objects first.
	 * <p>Note that a DestinationResolver is usually specified on the JmsActivationSpecFactory
	 * (see {@link StandardJmsActivationSpecFactory#setDestinationResolver}). This is simply
	 * a shortcut for parameterizing the default JmsActivationSpecFactory; it will replace
	 * any custom JmsActivationSpecFactory that might have been set before.
	 * @see StandardJmsActivationSpecFactory#setDestinationResolver
	 */
	public void setDestinationResolver(DestinationResolver destinationResolver) {
		DefaultJmsActivationSpecFactory factory = new DefaultJmsActivationSpecFactory();
		factory.setDestinationResolver(destinationResolver);
		this.activationSpecFactory = factory;
	}

	/**
	 * Specify the {@link JmsActivationSpecConfig} object that this endpoint manager
	 * should use for activating its listener.
	 * <p>This config object will be turned into a concrete JCA 1.5 ActivationSpec
	 * object through a {@link #setActivationSpecFactory JmsActivationSpecFactory}.
	 */
	public void setActivationSpecConfig(JmsActivationSpecConfig activationSpecConfig) {
		this.activationSpecConfig = activationSpecConfig;
	}


	public void afterPropertiesSet() throws ResourceException {
		if (this.messageListenerSet) {
			setMessageEndpointFactory(this.endpointFactory);
		}
		if (this.activationSpecConfig != null) {
			setActivationSpec(
					this.activationSpecFactory.createActivationSpec(getResourceAdapter(), this.activationSpecConfig));
		}
		super.afterPropertiesSet();
	}

}
