All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.bonita.pvm.internal.tx.StandardTransaction Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.ow2.bonita.pvm.internal.tx;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.transaction.Synchronization;

import org.ow2.bonita.pvm.env.Transaction;
import org.ow2.bonita.pvm.internal.log.Log;

/**
 * simple 2 phase commit transaction. no logging or recovery. non thread safe
 * (which is ok).
 * 
 * @author Tom Baeyens
 */
public class StandardTransaction implements Transaction, Serializable {

  private static final long serialVersionUID = 1L;
  private static Log log = Log.getLog(StandardTransaction.class.getName());

  enum State {
    CREATED, ACTIVE, ROLLBACKONLY, COMMITTED, ROLLEDBACK
  }

  protected List resources;
  protected List synchronizations;
  protected State state = State.CREATED;

  // methods for interceptor //////////////////////////////////////////////////

  public void begin() {
    log.debug("beginning " + this);
    state = State.ACTIVE;
  }

  public void complete() {
    if (state == State.ACTIVE) {
      commit();
    } else if (state == State.ROLLBACKONLY) {
      rollback();
    } else {
      throw new TransactionException("complete on transaction in state "
          + state);
    }
  }

  // public tx methods ////////////////////////////////////////////////////////

  public void setRollbackOnly() {
    if (state != State.ACTIVE) {
      throw new TransactionException("transaction was not active: " + state);
    }
    state = State.ROLLBACKONLY;
  }

  public boolean isRollbackOnly() {
    return ((state == State.ROLLBACKONLY) || (state == State.ROLLEDBACK));
  }

  // commit ///////////////////////////////////////////////////////////////////

  /** implements simplest two phase commit. */
  public void commit() {
    if (state != State.ACTIVE) {
      throw new TransactionException("commit on transaction in state " + state);
    }

    log.trace("committing " + this);

    try {
      beforeCompletion();

      if (resources != null) {
        // prepare
        // //////////////////////////////////////////////////////////////
        // the prepare loop will be skipped at the first exception
        for (StandardResource standardResource : resources) {
          log.trace("preparing resource " + standardResource);
          standardResource.prepare();
        }
      }

      // for any exception in the prepare phase, we'll rollback
    } catch (Exception exception) {
      try {
        log.debug("resource threw exception in prepare.  rolling back.");
        rollbackResources();
      } catch (Exception rollbackException) {
        log.error("rollback failed as well", rollbackException);
      }

      // rethrow
      if (exception instanceof RuntimeException) {
        throw (RuntimeException) exception;
      }
      throw new TransactionException("prepare failed", exception);
    }

    // here is the point of no return :-)

    // commit ///////////////////////////////////////////////////////////////
    Throwable commitException = null;
    if (resources != null) {
      // The commit loop will try to send the commit to every resource,
      // No matter what it takes. If exceptions come out of resource.commit's
      // they will be suppressed and the first exception will be rethrown after
      // all the resources are commited
      for (StandardResource standardResource : resources) {
        try {
          log.trace("committing resource " + standardResource);
          standardResource.commit();

          // Exceptions in the commit phase will not lead to rollback, since
          // some resources
          // might have committed and can't go back.
        } catch (Throwable t) {
          // TODO this should go to a special log for sys admin recovery
          log.error("commit failed for resource " + standardResource, t);
          if (commitException == null) {
            commitException = t;
          }
        }
      }
    }

    state = State.COMMITTED;
    afterCompletion();
    log.debug("committed " + this);

    if (commitException != null) {
      if (commitException instanceof RuntimeException) {
        throw (RuntimeException) commitException;
      } else if (commitException instanceof Error) {
        throw (Error) commitException;
      }
      throw new TransactionException("resource failed to commit",
          commitException);
    }
  }

  // rollback /////////////////////////////////////////////////////////////////

  public void rollback() {
    if ((state != State.ACTIVE) && (state != State.ROLLBACKONLY)) {
      throw new TransactionException("rollback on transaction in state "
          + state);
    }

    log.trace("rolling back " + this);

    beforeCompletion();
    rollbackResources();
  }

  void rollbackResources() {
    if (resources != null) {
      for (StandardResource resource : resources) {
        try {
          log.trace("rolling back resource " + resource);
          resource.rollback();
        } catch (Exception e) {
          log.error("rollback failed for resource " + resource);
        }
      }
    }

    state = State.ROLLEDBACK;

    afterCompletion();

    log.debug("rolled back");
  }

  // synchronizations /////////////////////////////////////////////////////////

  public void registerSynchronization(Synchronization synchronization) {
    if (synchronizations == null) {
      synchronizations = new ArrayList();
    }
    synchronizations.add(new StandardSynchronization(synchronization));
  }

  public void afterCompletion() {
    if (synchronizations != null) {
      for (StandardSynchronization synchronization : synchronizations) {
        synchronization.afterCompletion(state);
      }
    }
  }

  public void beforeCompletion() {
    if (synchronizations != null) {
      for (StandardSynchronization synchronization : synchronizations) {
        synchronization.beforeCompletion();
      }
    }
  }

  // resource enlisting ///////////////////////////////////////////////////////

  public void enlistResource(StandardResource standardResource) {
    if (resources == null) {
      resources = new ArrayList();
    }
    log.trace("enlisting resource " + standardResource
        + " to standard transaction");
    resources.add(standardResource);
  }

  List getResources() {
    return resources;
  }

  public String toString() {
    return "StandardTransaction[" + System.identityHashCode(this) + "]";
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy