Package com.sun.sgs.test.util

Source Code of com.sun.sgs.test.util.DummyTransaction

/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* --
*/

package com.sun.sgs.test.util;

import com.sun.sgs.app.TransactionNotActiveException;
import com.sun.sgs.app.TransactionTimeoutException;
import com.sun.sgs.impl.service.transaction.TransactionCoordinator;
import com.sun.sgs.impl.sharedutil.LoggerWrapper;
import com.sun.sgs.service.Transaction;
import com.sun.sgs.service.TransactionListener;
import com.sun.sgs.service.TransactionParticipant;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Provides a simple implementation of Transaction, for testing.
*
* If using this class with DummyTransactionProxy, make sure to call
* DummyTransactionProxy.setCurrentTransaction with the DummyTransaction, so
* that the proxy's transaction is cleared when the transaction is terminated.
*/
public class DummyTransaction implements Transaction {

    /** The logger for this class. */
    private static final LoggerWrapper logger =
  new LoggerWrapper(Logger.getLogger(DummyTransaction.class.getName()));

    /** The default timeout. */
    private static long DEFAULT_TIMEOUT =
  Long.getLong(TransactionCoordinator.TXN_TIMEOUT_PROPERTY, 100);

    /** The possible transaction states. */
    public enum State {
  ACTIVE, PREPARING, PREPARED, COMMITTING, COMMITTED, ABORTING, ABORTED
    }

    /** Whether to use prepareAndCommit. */
    public enum UsePrepareAndCommit { YES, NO, ARBITRARY }

    /** The ID for the next transaction. */
    private static AtomicLong nextId = new AtomicLong(1);

    /**
     * Whether commit on this transaction should use prepareAndCommit instead
     * of prepare followed by commit.
     */
    private final boolean usePrepareAndCommit;

    /** The ID for this transaction. */
    private final long id = nextId.getAndIncrement();

    /** The creation time of this transaction. */
    private final long creationTime;

    /** The length of time this transaction is allowed to run. */
    private final long timeout;

    /** The state of this transaction. */
    private State state = State.ACTIVE;

    /**
     * The exception that caused the transaction to be aborted, or null if no
     * cause was provided or if no abort occurred.
     */
    private Throwable abortCause = null;

    /** The transaction proxy associated with this transaction, if any. */
    DummyTransactionProxy proxy;

    /** The transaction participants for this transaction. */
    public final Set<TransactionParticipant> participants =
  new HashSet<TransactionParticipant>();

    /** The registered {@code TransactionListener}s. */
    private final Set<TransactionListener> listeners =
  new HashSet<TransactionListener>();

    /** Creates an instance of this class that always uses prepareAndCommit. */
    public DummyTransaction() {
  this(UsePrepareAndCommit.YES, DEFAULT_TIMEOUT);
    }

    /**
     * Creates an instance of this class that always uses prepareAndCommit and
     * uses the specified timeout.
     */
    public DummyTransaction(long timeout) {
  this(UsePrepareAndCommit.YES, timeout);
    }

    /**
     * Creates an instance of this class which uses prepareAndCommit based on
     * the argument.
     */
    public DummyTransaction(UsePrepareAndCommit usePrepareAndCommit) {
  this(usePrepareAndCommit, DEFAULT_TIMEOUT);
    }

    /**
     * Creates an instance of this class that uses prepareAndCommit based on
     * the argument and uses the specified timeout.
     */
    public DummyTransaction(UsePrepareAndCommit usePrepareAndCommit,
          long timeout)
    {
  switch (usePrepareAndCommit) {
  case YES:
      this.usePrepareAndCommit = true;
      break;
  case NO:
      this.usePrepareAndCommit = false;
      break;
  case ARBITRARY:
      this.usePrepareAndCommit = (id % 2 == 0);
      break;
  default:
      throw new AssertionError();
  }
  this.timeout = timeout;
  logger.log(Level.FINER, "create {0}", this);
        DummyProfileCoordinator.startTask();
        this.creationTime = System.currentTimeMillis();
    }

    /* -- Implement Transaction -- */

    public byte[] getId() {
  return new byte[] {
      (byte) (id >>> 56), (byte) (id >>> 48), (byte) (id >>> 40),
      (byte) (id >>> 32), (byte) (id >>> 24), (byte) (id >>> 16),
      (byte) (id >>> 8), (byte) id };
    }

    public long getCreationTime() { return creationTime; }

    public long getTimeout() { return timeout; }

    public void checkTimeout() {
  if (state == State.ABORTED ||
      state == State.COMMITTED)
  {
      throw new TransactionNotActiveException(
    "Transaction is not active: " + state);
  } else if (state == State.ABORTING ||
       state == State.COMMITTING)
  {
      return;
  }
  long runningTime = System.currentTimeMillis() - creationTime;
  if (runningTime > timeout) {
      TransactionTimeoutException exception =
    new TransactionTimeoutException(
        "Transaction timed out after " + runningTime + " ms");
      abort(exception);
      throw exception;
  }
    }

    public synchronized void join(TransactionParticipant participant) {
  if (logger.isLoggable(Level.FINEST)) {
      logger.log(
    Level.FINER, "join {0} participant:{1}", this, participant);
  }
  if (participant == null) {
      throw new NullPointerException("Participant must not be null");
  } else if (state == State.ABORTED) {
      throw new TransactionNotActiveException(
    "Transaction not active", abortCause);
  } else if (state != State.ACTIVE) {
      throw new IllegalStateException(
    "Transaction not active: " + state);
  }
  participants.add(participant);
    }

    public synchronized void abort(Throwable cause) {
  if (cause == null)
      throw new NullPointerException("Cause cannot be null");
  if (logger.isLoggable(Level.FINER)) {
      logger.log(Level.FINER, "abort {0} cause:{1}", this, cause);
  }
  if (state == State.ABORTING) {
      return;
  } else if (state == State.ABORTED) {
      throw new TransactionNotActiveException(
    "Transaction is not active", abortCause);
  } else if (state != State.ACTIVE &&
       state != State.PREPARING &&
       state != State.PREPARED)
  {
      throw new IllegalStateException(
    "Transaction is not active: " + state);
  }
  state = State.ABORTING;
  abortCause = cause;
  if (proxy != null) {
      proxy.setCurrentTransaction(null);
      proxy = null;
  }
  for (TransactionParticipant participant : participants) {
      try {
    participant.abort(this);
      } catch (RuntimeException e) {
    logger.logThrow(Level.WARNING, e, "Abort failed");
      }
  }
  state = State.ABORTED;
  notifyListenersAfter(false);
        DummyProfileCoordinator.endTask(false);
    }

    public synchronized boolean isAborted() {
  return state == State.ABORTED || state == State.ABORTING;
    }

    public synchronized Throwable getAbortCause() {
  return abortCause;
    }

    /** {@inheritDoc} */
    public void registerListener(TransactionListener listener) {
  if (listener == null) {
      throw new NullPointerException("The listener must not be null");
  } else if (state != State.ACTIVE) {
      throw new TransactionNotActiveException(
    "Transaction is not active");
  }
  listeners.add(listener);
    }

    /* -- Other methods -- */

    public synchronized boolean prepare() throws Exception {
  logger.log(Level.FINER, "prepare {0}", this);
  if (state != State.ACTIVE) {
      throw new IllegalStateException("Transaction not active");
  }
  notifyListenersBefore();
  state = State.PREPARING;
  if (proxy != null) {
      proxy.setCurrentTransaction(null);
      proxy = null;
  }
  boolean result = true;
  for (Iterator<TransactionParticipant> iter = participants.iterator();
      iter.hasNext(); )
  {
      TransactionParticipant participant = iter.next();
      boolean readOnly;
      try {
    readOnly = participant.prepare(this);
      } catch (Exception e) {
    if (state != State.ABORTED) {
        abort(e);
    }
    throw e;
      }
      if (readOnly) {
    iter.remove();
      } else {
    result = false;
      }
  }
  state = State.PREPARED;
  return result;
    }

    public synchronized void commit() throws Exception {
  logger.log(Level.FINER, "commit {0}", this);
  if (state == State.PREPARED) {
      state = State.COMMITTING;
      for (TransactionParticipant participant : participants) {
    try {
        participant.commit(this);
    } catch (RuntimeException e) {
        logger.logThrow(Level.WARNING, e, "Commit failed");
    }
      }
  } else if (state == State.ABORTED) {
      throw new TransactionNotActiveException(
    "Transaction not active", abortCause);
  } else if (state != State.ACTIVE) {
      throw new IllegalStateException(
    "Transaction not active: " + state);
  } else if (usePrepareAndCommit && participants.size() == 1) {
      notifyListenersBefore();
      state = State.PREPARING;
      if (proxy != null) {
    proxy.setCurrentTransaction(null);
    proxy = null;
      }
      TransactionParticipant participant =
    participants.iterator().next();
      try {
    participant.prepareAndCommit(this);
      } catch (Exception e) {
    if (state != State.ABORTED) {
        abort(e);
    }
    throw e;
      }
  } else {
      prepare();
      state = State.COMMITTING;
      for (TransactionParticipant participant : participants) {
    try {
        participant.commit(this);
    } catch (RuntimeException e) {
        logger.logThrow(Level.WARNING, e, "Commit failed");
    }
      }
  }
  state = State.COMMITTED;
  notifyListenersAfter(true);
        DummyProfileCoordinator.endTask(true);
    }

    /** Returns the current state. */
    public synchronized State getState() {
  return state;
    }

    public String toString() {
  return "DummyTransaction[tid:" + id + "]";
    }

    /** Notify any listeners before preparing the transaction. */
    private void notifyListenersBefore() {
  /*
   * Copy the listeners to avoid problems if a beforeCompletion method
   * registers another listener.
   */
  for (TransactionListener listener :
     listeners.toArray(new TransactionListener[listeners.size()]))
  {
      try {
    listener.beforeCompletion();
      } catch (RuntimeException e) {
    if (state != State.ABORTED) {
        abort(e);
    }
    throw e;
      }
  }
    }

    /** Notify any listeners after completing the transaction. */
    private void notifyListenersAfter(boolean commited) {
  for (TransactionListener listener : listeners) {
      try {
    listener.afterCompletion(commited);
      } catch (RuntimeException e) {
    logger.logThrow(Level.WARNING, e,
        "TransactionListener.afterCompletion failed");
      }
  }
    }
}
TOP

Related Classes of com.sun.sgs.test.util.DummyTransaction

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.