package org.openspaces.esb.mule.transaction;

import java.rmi.RemoteException;

import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import net.jini.core.transaction.Transaction;
import net.jini.core.transaction.TransactionException;

import org.mule.transaction.TransactionCoordination;
import org.mule.transaction.XaTransaction;
import org.openspaces.core.TransactionDataAccessException;
import org.openspaces.core.transaction.TransactionProvider;
import org.springframework.transaction.TransactionDefinition;

import com.gigaspaces.client.transaction.DistributedTransactionManagerProvider;
import com.gigaspaces.internal.client.spaceproxy.ISpaceProxy;
import com.j_spaces.core.IJSpace;
import com.j_spaces.core.client.XAResourceImpl;

/**
 * @author kimchy (Shay Banon)
 */
public class MuleXATransactionProvider implements TransactionProvider {

    private DistributedTransactionManagerProvider distributedTransactionManagerProvider;
    
    private final Object distributedTransactionManagerProviderLock = new Object();
    
    public Transaction.Created getCurrentTransaction(Object transactionalContext, IJSpace space) {
        org.mule.api.transaction.Transaction tx = TransactionCoordination.getInstance().getTransaction();
        if (!(tx instanceof XaTransaction)) {
            return null;
        }
        XaTransaction xaTransaction = (XaTransaction) tx;
        if (xaTransaction.hasResource(space)) {
            // already bound the space, return
            return ((CustomXaResource) xaTransaction.getResource(space)).transaction;
        }
        if (distributedTransactionManagerProvider == null)
            
            if (distributedTransactionManagerProvider == null) {
                synchronized(distributedTransactionManagerProviderLock) {
                    if (distributedTransactionManagerProvider == null) {
                        try {
                            distributedTransactionManagerProvider = new DistributedTransactionManagerProvider();
                        } catch (TransactionException e) {
                            throw new TransactionDataAccessException("Failed to get local transaction manager for space [" + space + "]", e);
                        }
                    }
                }
            }
        CustomXaResource xaResourceSpace = new CustomXaResource(new XAResourceImpl(distributedTransactionManagerProvider.getTransactionManager(), space));

        // enlist the Space xa resource with the current JTA transaction
        // we rely on the fact that this call will start the XA transaction
        try {
            xaTransaction.bindResource(space, xaResourceSpace);
        } catch (Exception e) {
            throw new TransactionDataAccessException("Failed to enlist xa resource [" + xaResourceSpace + "] with space [" + space + "]", e);
        }

        // get the context transaction from the Space and nullify it. We will handle
        // the declarative transaction nature using Spring sync
        Transaction.Created transaction = ((ISpaceProxy) space).getContextTransaction();
        ((ISpaceProxy) space).replaceContextTransaction(null);

        xaResourceSpace.transaction = transaction;

        return transaction;
    }

    public int getCurrentTransactionIsolationLevel(Object transactionalContext) {
        return TransactionDefinition.ISOLATION_DEFAULT;
    }

    public boolean isEnabled() {
        return true;
    }
    
    public void destroy() throws RemoteException {
        synchronized(distributedTransactionManagerProviderLock)
        {
            if (distributedTransactionManagerProvider != null)
                distributedTransactionManagerProvider.destroy();
        }
    }

    private static class CustomXaResource implements XAResource {

        public final XAResource actual;

        public Transaction.Created transaction;

        private CustomXaResource(XAResource actual) {
            this.actual = actual;
        }

        public void commit(Xid xid, boolean b) throws XAException {
            actual.commit(xid, b);
        }

        public void end(Xid xid, int i) throws XAException {
            actual.end(xid, i);
        }

        public void forget(Xid xid) throws XAException {
            actual.forget(xid);
        }

        public int getTransactionTimeout() throws XAException {
            return actual.getTransactionTimeout();
        }

        public boolean isSameRM(XAResource xaResource) throws XAException {
            return actual.isSameRM(xaResource);
        }

        public int prepare(Xid xid) throws XAException {
            return actual.prepare(xid);
        }

        public Xid[] recover(int i) throws XAException {
            return actual.recover(i);
        }

        public void rollback(Xid xid) throws XAException {
            actual.rollback(xid);
        }

        public boolean setTransactionTimeout(int i) throws XAException {
            return actual.setTransactionTimeout(i);
        }

        public void start(Xid xid, int i) throws XAException {
            actual.start(xid, i);
        }
    }
}

@@ -1684,84 +1684,8 @@
 ull)
-%0A            %0A            if (distributedTransactionManagerProvider == null)
  %7B%0A 
@@ -1691,24 +1691,17 @@
         
-        
+%09
 synchron
@@ -1758,28 +1758,18 @@
         
-  
-          
+%09%09
 if (dist
@@ -1825,58 +1825,29 @@
     
-                try %7B%0A                            
+%09%09%09try %7B%0A        %09%09%09%09
 dist
@@ -1935,32 +1935,19 @@
         
-   
-             
+%09%09%09
 %7D catch 
@@ -1981,36 +1981,20 @@
         
-                    
+%09%09%09%09
 throw ne
@@ -2111,70 +2111,36 @@
     
-                %7D%0A                    %7D%0A                %7D%0A    
+%09%09%09%7D%0A        %09%09%7D%0A        %09%7D%0A
     

GS-10056 Removed redundant if.