Package org.hornetq.core.transaction.impl

Source Code of org.hornetq.core.transaction.impl.TransactionImpl

/*
60 * Copyright 2009 Red Hat, Inc.
* Red Hat licenses this file to you 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.hornetq.core.transaction.impl;

import java.util.ArrayList;
import java.util.List;

import javax.transaction.xa.Xid;

import org.hornetq.api.core.HornetQException;
import org.hornetq.core.journal.IOAsyncTask;
import org.hornetq.core.logging.Logger;
import org.hornetq.core.persistence.OperationContext;
import org.hornetq.core.persistence.StorageManager;
import org.hornetq.core.transaction.Transaction;
import org.hornetq.core.transaction.TransactionOperation;

/**
* A TransactionImpl
*
* @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
* @author <a href="mailto:andy.taylor@jboss.org>Andy Taylor</a>
*
*/
public class TransactionImpl implements Transaction
{
   private List<TransactionOperation> operations;

   private static final Logger log = Logger.getLogger(TransactionImpl.class);

   private static final int INITIAL_NUM_PROPERTIES = 10;

   private Object[] properties = new Object[TransactionImpl.INITIAL_NUM_PROPERTIES];

   private final StorageManager storageManager;

   private final Xid xid;

   private final long id;
  
   /**
    * if the appendCommit has to be done only after the current operations are completed
    */
   private boolean waitBeforeCommit = false;

   private volatile State state = State.ACTIVE;

   private HornetQException exception;

   private final Object timeoutLock = new Object();

   private final long createTime;

   private volatile boolean containsPersistent;

   private int timeoutSeconds = -1;

   public TransactionImpl(final StorageManager storageManager, final int timeoutSeconds)
   {
      this.storageManager = storageManager;

      xid = null;

      id = storageManager.generateUniqueID();

      createTime = System.currentTimeMillis();

      this.timeoutSeconds = timeoutSeconds;
   }

   /** Used for copying */
   private TransactionImpl(final TransactionImpl other)
   {
      this.storageManager = other.storageManager;

      this.xid = other.xid;

      this.id = other.id;

      this.createTime = other.createTime;

      this.timeoutSeconds = other.timeoutSeconds;
   }

   public TransactionImpl(final StorageManager storageManager)
   {
      this.storageManager = storageManager;

      xid = null;

      id = storageManager.generateUniqueID();

      createTime = System.currentTimeMillis();
   }

   public TransactionImpl(final Xid xid, final StorageManager storageManager, final int timeoutSeconds)
   {
      this.storageManager = storageManager;

      this.xid = xid;

      id = storageManager.generateUniqueID();

      createTime = System.currentTimeMillis();

      this.timeoutSeconds = timeoutSeconds;
   }

   public TransactionImpl(final long id, final Xid xid, final StorageManager storageManager)
   {
      this.storageManager = storageManager;

      this.xid = xid;

      this.id = id;

      createTime = System.currentTimeMillis();
   }

   // Transaction implementation
   // -----------------------------------------------------------

   public void setContainsPersistent()
   {
      containsPersistent = true;
   }
  
   public boolean isContainsPersistent()
   {
      return containsPersistent;
   }

   public void setTimeout(final int timeout)
   {
      this.timeoutSeconds = timeout;
   }

   public long getID()
   {
      return id;
   }

   public long getCreateTime()
   {
      return createTime;
   }
  
   public boolean hasTimedOut(final long currentTime,final int defaultTimeout)
   {
      if(timeoutSeconds == - 1)
      {
         return getState() != Transaction.State.PREPARED && currentTime > createTime + defaultTimeout * 1000;
      }
      else
      {
         return getState() != Transaction.State.PREPARED && currentTime > createTime + timeoutSeconds * 1000;
      }
   }

   public void prepare() throws Exception
   {
      synchronized (timeoutLock)
      {
         if (state == State.ROLLBACK_ONLY)
         {
            if (exception != null)
            {
               throw exception;
            }
            else
            {
               // Do nothing
               return;
            }
         }
         else if (state != State.ACTIVE)
         {
            throw new IllegalStateException("Transaction is in invalid state " + state);
         }

         if (xid == null)
         {
            throw new IllegalStateException("Cannot prepare non XA transaction");
         }

         beforePrepare();

         storageManager.prepare(id, xid);

         state = State.PREPARED;
         // We use the Callback even for non persistence
         // If we are using non-persistence with replication, the replication manager will have
         // to execute this runnable in the correct order
         storageManager.afterCompleteOperations(new IOAsyncTask()
         {

            public void onError(final int errorCode, final String errorMessage)
            {
               TransactionImpl.log.warn("IO Error completing the transaction, code = " + errorCode +
                                        ", message = " +
                                        errorMessage);
            }

            public void done()
            {
               afterPrepare();
            }
         });
      }
   }

   public void commit() throws Exception
   {
      commit(true);
   }

   public void commit(final boolean onePhase) throws Exception
   {
      synchronized (timeoutLock)
      {
         if (state == State.ROLLBACK_ONLY)
         {
            rollback();
           
            if (exception != null)
            {
               throw exception;
            }
            else
            {
               // Do nothing
               return;
            }
         }

         if (xid != null)
         {
            if (onePhase && state != State.ACTIVE || !onePhase && state != State.PREPARED)
            {
               throw new IllegalStateException("Transaction is in invalid state " + state);
            }
         }
         else
         {
            if (state != State.ACTIVE)
            {
               throw new IllegalStateException("Transaction is in invalid state " + state);
            }
         }
        
         beforeCommit();

         if (containsPersistent || xid != null && state == State.PREPARED)
         {

            if (waitBeforeCommit)
            {
               // we will wait all the pending operations to finish before we can add this
               asyncAppendCommit();
            }
            else
            {
               storageManager.commit(id);
            }

            state = State.COMMITTED;
         }

         // We use the Callback even for non persistence
         // If we are using non-persistence with replication, the replication manager will have
         // to execute this runnable in the correct order
         // This also will only use a different thread if there are any IO pendings.
         // If the IO finished early by the time we got here, we won't need an executor
         storageManager.afterCompleteOperations(new IOAsyncTask()
         {

            public void onError(final int errorCode, final String errorMessage)
            {
               TransactionImpl.log.warn("IO Error completing the transaction, code = " + errorCode +
                                        ", message = " +
                                        errorMessage);
            }

            public void done()
            {
               afterCommit();
            }
         });

      }
   }

   /**
    *
    */
   protected void asyncAppendCommit()
   {
      final OperationContext ctx = storageManager.getContext();
      storageManager.afterCompleteOperations(new IOAsyncTask()
      {
         public void onError(int errorCode, String errorMessage)
         {
         }
        
         public void done()
         {
            OperationContext originalCtx = storageManager.getContext();
            try
            {
               storageManager.setContext(ctx);
               storageManager.commit(id, false);
            }
            catch (Exception e)
            {
               onError(HornetQException.IO_ERROR, e.getMessage());
            }
            finally
            {
               storageManager.setContext(originalCtx);
            }
         }
      });
      storageManager.lineUpContext();
   }

   public void rollback() throws Exception
   {
      synchronized (timeoutLock)
      {
         if (xid != null)
         {
            if (state != State.PREPARED && state != State.ACTIVE && state != State.ROLLBACK_ONLY)
            {
               throw new IllegalStateException("Transaction is in invalid state " + state);
            }
         }
         else
         {
            if (state != State.ACTIVE && state != State.ROLLBACK_ONLY)
            {
               throw new IllegalStateException("Transaction is in invalid state " + state);
            }
         }

         beforeRollback();

         doRollback();

         // We use the Callback even for non persistence
         // If we are using non-persistence with replication, the replication manager will have
         // to execute this runnable in the correct order
         storageManager.afterCompleteOperations(new IOAsyncTask()
         {

            public void onError(final int errorCode, final String errorMessage)
            {
               TransactionImpl.log.warn("IO Error completing the transaction, code = " + errorCode +
                                        ", message = " +
                                        errorMessage);
            }

            public void done()
            {
               afterRollback();
               state = State.ROLLEDBACK;
            }
         });
      }
   }

   public void suspend()
   {
      if (state != State.ACTIVE)
      {
         throw new IllegalStateException("Can only suspend active transaction");
      }
      state = State.SUSPENDED;
   }

   public void resume()
   {
      if (state != State.SUSPENDED)
      {
         throw new IllegalStateException("Can only resume a suspended transaction");
      }
      state = State.ACTIVE;
   }

   public Transaction.State getState()
   {
      return state;
   }

   public void setState(final State state)
   {
      this.state = state;
   }
  
   public boolean isWaitBeforeCommit()
   {
      return waitBeforeCommit;
   }

   public void setWaitBeforeCommit(boolean waitBeforeCommit)
   {
      this.waitBeforeCommit = waitBeforeCommit;
   }

   public Xid getXid()
   {
      return xid;
   }

   public void markAsRollbackOnly(final HornetQException exception)
   {
      if (log.isDebugEnabled())
      {
         log.debug("Marking Transaction " + this.id + " as rollback only");
      }
      state = State.ROLLBACK_ONLY;

      this.exception = exception;
   }

   public synchronized void addOperation(final TransactionOperation operation)
   {
      checkCreateOperations();

      operations.add(operation);
   }

   public int getOperationsCount()
   {
      return operations.size();
   }

   public synchronized List<TransactionOperation> getAllOperations()
   {
      return new ArrayList<TransactionOperation>(operations);
   }
  
   public void putProperty(final int index, final Object property)
   {
      if (index >= properties.length)
      {
         Object[] newProperties = new Object[index];

         System.arraycopy(properties, 0, newProperties, 0, properties.length);

         properties = newProperties;
      }

      properties[index] = property;
   }

   public Object getProperty(final int index)
   {
      return properties[index];
   }

   // Private
   // -------------------------------------------------------------------

   private void doRollback() throws Exception
   {
      if (containsPersistent || xid != null && state == State.PREPARED)
      {
         storageManager.rollback(id);
      }
   }

   private void checkCreateOperations()
   {
      if (operations == null)
      {
         operations = new ArrayList<TransactionOperation>();
      }
   }

   public Transaction copy()
   {
      return new TransactionImpl(this);
   }

   public synchronized void afterCommit()
   {
      if (operations != null)
      {
         for (TransactionOperation operation : operations)
         {
            operation.afterCommit(this);
         }
      }
   }

   public synchronized void afterRollback()
   {
      if (operations != null)
      {
         for (TransactionOperation operation : operations)
         {
            operation.afterRollback(this);
         }
      }
   }

   public synchronized void beforeCommit() throws Exception
   {
      if (operations != null)
      {
         for (TransactionOperation operation : operations)
         {
            operation.beforeCommit(this);
         }
      }
   }

   public synchronized void beforePrepare() throws Exception
   {
      if (operations != null)
      {
         for (TransactionOperation operation : operations)
         {
            operation.beforePrepare(this);
         }
      }
   }

   public synchronized void beforeRollback() throws Exception
   {
      if (operations != null)
      {
         for (TransactionOperation operation : operations)
         {
            operation.beforeRollback(this);
         }
      }
   }

   public synchronized void afterPrepare()
   {
      if (operations != null)
      {
         for (TransactionOperation operation : operations)
         {
            operation.afterPrepare(this);
         }
      }
   }

}
TOP

Related Classes of org.hornetq.core.transaction.impl.TransactionImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.