/**
 * 
 * Copyright 2004 Hiram Chirino
 * 
 * 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.codehaus.activemq.ra;

import org.codehaus.activemq.ActiveMQXASession;
import org.codehaus.activemq.Closeable;
import org.codehaus.activemq.ActiveMQXAConnection;
import org.codehaus.activemq.message.ActiveMQMessage;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.jms.IllegalStateException;
import javax.jms.JMSException;
import javax.jms.TransactionInProgressException;
import javax.jms.MessageListener;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;
import javax.resource.spi.endpoint.MessageEndpoint;
import javax.resource.ResourceException;

/**
 * The ActiveMQRASession extends the ActiveMQXASession so 
 * that it is more flexible in terms of using the availble 
 * transaction context.  A normal ActiveMQXASession implements
 * the XASession interface stictly which means that it
 * can only do work in the context of an XA transaction. 
 *
 * @version $Revision: 1.1.2.1 $
 */
public class ActiveMQRASession extends ActiveMQXASession implements Closeable {
    
    private static final Log log = LogFactory.getLog(ActiveMQRASession.class);

	private boolean inLocalTransaction;
	private boolean inXATransaction;
	private boolean autoCommit=false;
	
	public ActiveMQRASession(ActiveMQXAConnection theConnection, int theAcknowlegeMode) throws JMSException {
		super(theConnection, theAcknowlegeMode);
	}
	
	public boolean getTransacted() throws JMSException {
        return inLocalTransaction || inXATransaction || !autoCommit;
    }
	
	protected boolean isXaTransacted() {
        return inXATransaction;
	}

	//////////////////////////////////////////////////////////////////////////
	//
	// Handle the local transactions
	//
	//////////////////////////////////////////////////////////////////////////
	
	
	/**
     * @throws JMSException if some internal error occurs
     */
    protected void doStartTransaction() throws JMSException {
    	if( !inXATransaction && !inLocalTransaction && !autoCommit) {
    	    // Start a local transaction.
    		begin();
    	}
    }
	
	/**
	 * Used to know when a local transaction has started.
	 */
	public void begin() throws JMSException {
		if( inXATransaction ) 
			throw new TransactionInProgressException("Cannot start local transction.  XA transaction is allready in progress.");
		if( inLocalTransaction ) 
			throw new TransactionInProgressException("Cannot start local transction.  Local transaction is allready in progress.");
		
		inLocalTransaction = true;
		startLocalTransaction();		
	}
	
	public boolean isInLocalTransaction() {
	    return inLocalTransaction;
	}
	
	public void rollback() throws JMSException {
		if( inXATransaction ) 
	        throw new TransactionInProgressException("Cannot rollback() if an XA transaction is allready in progress ");
		if( autoCommit ) 
            throw new javax.jms.IllegalStateException("Not a transacted session");
		if( !autoCommit && !inLocalTransaction) 
            return;
		if( !inLocalTransaction ) 
			throw new JMSException("Cannot rollback local transction.  Local transaction had not been started.");
		inLocalTransaction=false;
		rollbackLocalTransaction();
	}
	
	public void commit() throws JMSException {
		if( inXATransaction ) 
	        throw new TransactionInProgressException("Cannot commit() if an XA transaction is allready in progress ");
		if( autoCommit ) 
            throw new javax.jms.IllegalStateException("Not a transacted session");
		if( !autoCommit && !inLocalTransaction) 
            return;
		if( !inLocalTransaction ) 
			throw new IllegalStateException("Cannot commit local transction.  Local transaction had not been started.");
		inLocalTransaction=false;
		commitLocalTransaction();
	}

	//////////////////////////////////////////////////////////////////////////
	//
	// Handle the XA transactions
	//
	//////////////////////////////////////////////////////////////////////////

	public void start(Xid xid, int flags) throws XAException {
		if( inLocalTransaction ) 
            throw new XAException(XAException.XAER_PROTO);
		
		super.start(xid, flags);
		inXATransaction=true;
	}
	
	public void end(Xid xid, int flags) throws XAException {
		if( inLocalTransaction ) 
            throw new XAException(XAException.XAER_PROTO);

		super.end(xid, flags);
		inXATransaction=false;
	}
	
	/**
	 * @return Returns the autoCommit.
	 */
	public boolean isAutoCommit() {
		return autoCommit;
	}
	
	/**
	 * @param autoCommit The autoCommit to set.
	 */
	public void setAutoCommit(boolean autoCommit) {
		this.autoCommit = autoCommit;
	}

	/**
	 * Allows you to change the acknowlegeMode of an existing session.
	 * 
	 * @param acknowledgeMode
	 */
	public void setAcknowlegeMode(int acknowledgeMode) {
		this.acknowledgeMode = acknowledgeMode;
	}

    protected void preDeliveryHook(MessageListener listener, ActiveMQMessage message) {
        if (listener instanceof MessageEndpoint) {
            MessageEndpoint endpoint = (MessageEndpoint) listener;
            try {
                endpoint.beforeDelivery(ActiveMQBaseEndpointWorker.ON_MESSAGE_METHOD);
            } catch (NoSuchMethodException e) {
                log.error(e.toString(), e);
            } catch (ResourceException e) {
                log.warn(e.toString(), e); 
            }
        }        
    }

    protected void postDeliveryHook(MessageListener listener, ActiveMQMessage message) {
        if (listener instanceof MessageEndpoint) {
            MessageEndpoint endpoint = (MessageEndpoint) listener;
            try {
                endpoint.afterDelivery();
            } catch (ResourceException e) {
                log.warn(e.toString(), e);
            }
        }        
    }
}
