diff options
Diffstat (limited to 'src')
5 files changed, 114 insertions, 39 deletions
| diff --git a/src/main/java/com/amazon/carbonado/Transaction.java b/src/main/java/com/amazon/carbonado/Transaction.java index 5c8203f..1c2c06d 100644 --- a/src/main/java/com/amazon/carbonado/Transaction.java +++ b/src/main/java/com/amazon/carbonado/Transaction.java @@ -117,4 +117,30 @@ public interface Transaction {       * Returns the isolation level of this transaction.
       */
      IsolationLevel getIsolationLevel();
 +
 +    /**
 +     * Detaches this transaction from the current thread. It can be attached
 +     * later, and to any thread which currently has no thread-local
 +     * transaction.
 +     *
 +     * <p>Detaching a transaction also detaches any parent and nested child
 +     * transactions. Attaching any of them achieves the same result as
 +     * attaching this transaction.
 +     *
 +     * @throws IllegalStateException if transaction is attached to a different
 +     * thread
 +     * @since 1.2
 +     */
 +    void detach();
 +
 +    /**
 +     * Attaches this transaction to the current thread, if it has been
 +     * detached. Attaching a transaction also attaches any parent and nested
 +     * child transactions.
 +     *
 +     * @throws IllegalStateException if current thread has a different
 +     * transaction already attached
 +     * @since 1.2
 +     */
 +    void attach();
  }
 diff --git a/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java b/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java index fd88f35..dd2a4f5 100644 --- a/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java +++ b/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java @@ -83,6 +83,20 @@ class LoggingTransaction implements Transaction {          return mTxn.getIsolationLevel();
      }
 +    public void detach() {
 +        if (mLog.isEnabled()) {
 +            mLog.write("Transaction.detach() on " + idChain());
 +        }
 +        mTxn.detach();
 +    }
 +
 +    public void attach() {
 +        if (mLog.isEnabled()) {
 +            mLog.write("Transaction.attach() on " + idChain());
 +        }
 +        mTxn.attach();
 +    }
 +
      private String idChain() {
          if (mParent == null) {
              return String.valueOf(mID);
 diff --git a/src/main/java/com/amazon/carbonado/spi/TransactionManager.java b/src/main/java/com/amazon/carbonado/spi/TransactionManager.java index 4a28d24..a2b1375 100644 --- a/src/main/java/com/amazon/carbonado/spi/TransactionManager.java +++ b/src/main/java/com/amazon/carbonado/spi/TransactionManager.java @@ -74,32 +74,36 @@ public abstract class TransactionManager<Txn> {       * which does not currently have a TransactionScope.
       *
       * @return detached thread-local TransactionScope or null if none
 +     * @since 1.2
       */
 -    /*
      public TransactionScope<Txn> detachLocalScope() {
          TransactionScope<Txn> scope = mLocalScope.get();
          if (scope != null) {
 -            if (!scope.detach()) {
 -                scope = null;
 -            } else {
 -                mLocalScope.remove();
 -            }
 +            scope.markDetached();
 +            mLocalScope.remove();
          }
          return scope;
      }
 -    */
      // Called by TransactionScope.
 -    /*
 -    void attach(TransactionScope<Txn> scope) {
 +    boolean removeLocalScope(TransactionScope<Txn> scope) {
 +        TransactionScope<Txn> existing = mLocalScope.get();
 +        if (existing == null || existing == scope) {
 +            mLocalScope.remove();
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    // Called by TransactionScope.
 +    boolean setLocalScope(TransactionScope<Txn> scope) {
          TransactionScope<Txn> existing = mLocalScope.get();
          if (existing == null || existing == scope) {
              mLocalScope.set(scope);
 -        } else {
 -            throw new IllegalStateException("Current thread already has a transaction scope");
 +            return true;
          }
 +        return false;
      }
 -    */
      /**
       * Closes all transaction scopes. Should be called only when repository is
 diff --git a/src/main/java/com/amazon/carbonado/spi/TransactionPair.java b/src/main/java/com/amazon/carbonado/spi/TransactionPair.java index d97aef4..ff07bc2 100644 --- a/src/main/java/com/amazon/carbonado/spi/TransactionPair.java +++ b/src/main/java/com/amazon/carbonado/spi/TransactionPair.java @@ -86,4 +86,24 @@ public class TransactionPair implements Transaction {          return mPrimaryTransaction.getIsolationLevel()
              .lowestCommon(mSecondaryTransaction.getIsolationLevel());
      }
 +
 +    public void detach() {
 +        mPrimaryTransaction.detach();
 +        try {
 +            mSecondaryTransaction.detach();
 +        } catch (IllegalStateException e) {
 +            mPrimaryTransaction.attach();
 +            throw e;
 +        }
 +    }
 +
 +    public void attach() {
 +        mPrimaryTransaction.attach();
 +        try {
 +            mSecondaryTransaction.attach();
 +        } catch (IllegalStateException e) {
 +            mPrimaryTransaction.detach();
 +            throw e;
 +        }
 +    }
  }
 diff --git a/src/main/java/com/amazon/carbonado/spi/TransactionScope.java b/src/main/java/com/amazon/carbonado/spi/TransactionScope.java index 0ac2cee..82a20eb 100644 --- a/src/main/java/com/amazon/carbonado/spi/TransactionScope.java +++ b/src/main/java/com/amazon/carbonado/spi/TransactionScope.java @@ -41,8 +41,6 @@ import com.amazon.carbonado.Transaction;   * @see TransactionManager
   */
  public class TransactionScope<Txn> {
 -    private static final int OPEN = 0, DETACHED = 1, CLOSED = 2;
 -
      final TransactionManager<Txn> mTxnMgr;
      final Lock mLock;
 @@ -51,12 +49,12 @@ public class TransactionScope<Txn> {      // Tracks all registered cursors by storage type.
      private Map<Class<?>, CursorList<TransactionImpl<Txn>>> mCursors;
 -    private int mState;
 +    private boolean mClosed;
 +    private boolean mDetached;
      TransactionScope(TransactionManager<Txn> txnMgr, boolean closed) {
          mTxnMgr = txnMgr;
          mLock = new ReentrantLock(true);
 -        mState = closed ? CLOSED : OPEN;
      }
      /**
 @@ -192,7 +190,7 @@ public class TransactionScope<Txn> {      public boolean isForUpdate() {
          mLock.lock();
          try {
 -            return (mState == CLOSED || mActive == null) ? false : mActive.isForUpdate();
 +            return (mClosed || mActive == null) ? false : mActive.isForUpdate();
          } finally {
              mLock.unlock();
          }
 @@ -205,7 +203,7 @@ public class TransactionScope<Txn> {      public IsolationLevel getIsolationLevel() {
          mLock.lock();
          try {
 -            return (mState == CLOSED || mActive == null) ? null : mActive.getIsolationLevel();
 +            return (mClosed || mActive == null) ? null : mActive.getIsolationLevel();
          } finally {
              mLock.unlock();
          }
 @@ -215,41 +213,46 @@ public class TransactionScope<Txn> {       * Attach this scope to the current thread, if it has been {@link
       * TransactionManager#detachLocalScope detached}.
       *
 -     * @throws IllegalStateException if scope is not detached or if current
 -     * thread already has a transaction scope
 +     * @throws IllegalStateException if current thread has a different
 +     * transaction already attached
       */
 -    /*
      public void attach() {
          mLock.lock();
          try {
 -            if (mState != DETACHED) {
 -                throw new IllegalStateException("Transaction scope is not detached");
 +            if (mTxnMgr.setLocalScope(this)) {
 +                mDetached = false;
 +            } else {
 +                throw new IllegalStateException
 +                    ("Current thread has a different transaction already attached");
              }
 -            mState = OPEN;
          } finally {
              mLock.unlock();
          }
 -        mTxnMgr.attach(this);
      }
 -    */
 -    /**
 -     * @return false if closed
 -     */
 -    /*
 -    boolean detach() {
 +    // Called by TransactionImpl.
 +    void detach() {
          mLock.lock();
          try {
 -            if (mState == CLOSED) {
 -                return false;
 +            if (mTxnMgr.removeLocalScope(this)) {
 +                mDetached = true;
 +            } else {
 +                throw new IllegalStateException("Transaction is attached to a different thread");
              }
 -            mState = DETACHED;
 -            return true;
          } finally {
              mLock.unlock();
          }
      }
 -    */
 +
 +    // Called by TransactionManager.
 +    void markDetached() {
 +        mLock.lock();
 +        try {
 +            mDetached = true;
 +        } finally {
 +            mLock.unlock();
 +        }
 +    }
      /**
       * Exits all transactions and closes all cursors. Should be called only
 @@ -258,7 +261,7 @@ public class TransactionScope<Txn> {      void close() throws RepositoryException {
          mLock.lock();
          try {
 -            if (mState != CLOSED) {
 +            if (!mClosed) {
                  while (mActive != null) {
                      mActive.exit();
                  }
 @@ -269,7 +272,7 @@ public class TransactionScope<Txn> {                  }
              }
          } finally {
 -            mState = CLOSED;
 +            mClosed = true;
              mLock.unlock();
          }
      }
 @@ -278,7 +281,7 @@ public class TransactionScope<Txn> {       * Caller must hold mLock.
       */
      private void checkClosed() {
 -        if (mState == CLOSED) {
 +        if (mClosed) {
              throw new IllegalStateException("Repository is closed");
          }
      }
 @@ -409,6 +412,14 @@ public class TransactionScope<Txn> {              return mLevel;
          }
 +        public void detach() {
 +            mScope.detach();
 +        }
 +
 +        public void attach() {
 +            mScope.attach();
 +        }
 +
          // Caller must hold mLock.
          <S extends Storable> void register(Cursor<S> cursor) {
              if (mCursorList == null) {
 | 
