diff options
Diffstat (limited to 'src/main/java/com/amazon/carbonado/spi')
4 files changed, 3 insertions, 940 deletions
diff --git a/src/main/java/com/amazon/carbonado/spi/AbstractRepository.java b/src/main/java/com/amazon/carbonado/spi/AbstractRepository.java index e73a8c9..688134b 100644 --- a/src/main/java/com/amazon/carbonado/spi/AbstractRepository.java +++ b/src/main/java/com/amazon/carbonado/spi/AbstractRepository.java @@ -41,6 +41,9 @@ import com.amazon.carbonado.sequence.SequenceCapability; import com.amazon.carbonado.sequence.SequenceValueProducer;
import com.amazon.carbonado.sequence.SequenceValueProducerPool;
+import com.amazon.carbonado.txn.TransactionManager;
+import com.amazon.carbonado.txn.TransactionScope;
+
/**
* Implements basic functionality required by a core Repository.
*
diff --git a/src/main/java/com/amazon/carbonado/spi/TransactionManager.java b/src/main/java/com/amazon/carbonado/spi/TransactionManager.java deleted file mode 100644 index 48f52af..0000000 --- a/src/main/java/com/amazon/carbonado/spi/TransactionManager.java +++ /dev/null @@ -1,212 +0,0 @@ -/*
- * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
- * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
- * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
- *
- * Licensed 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 com.amazon.carbonado.spi;
-
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import org.cojen.util.WeakIdentityMap;
-
-import com.amazon.carbonado.IsolationLevel;
-import com.amazon.carbonado.PersistException;
-import com.amazon.carbonado.RepositoryException;
-import com.amazon.carbonado.Transaction;
-
-/**
- * Generic transaction manager for repositories.
- *
- * @param <Txn> Transaction implementation
- * @author Brian S O'Neill
- */
-public abstract class TransactionManager<Txn> {
- private static final int OPEN = 0, CLOSED = 1, SUSPENDED = 2;
-
- private final ThreadLocal<TransactionScope<Txn>> mLocalScope;
- private final Map<TransactionScope<Txn>, ?> mAllScopes;
-
- private int mState;
-
- public TransactionManager() {
- mLocalScope = new ThreadLocal<TransactionScope<Txn>>();
- mAllScopes = new WeakIdentityMap();
- }
-
- /**
- * Returns the thread-local TransactionScope, creating it if needed.
- */
- public TransactionScope<Txn> localScope() {
- TransactionScope<Txn> scope = mLocalScope.get();
- if (scope == null) {
- int state;
- synchronized (this) {
- state = mState;
- scope = new TransactionScope<Txn>(this, state != OPEN);
- mAllScopes.put(scope, null);
- }
- mLocalScope.set(scope);
- if (state == SUSPENDED) {
- // Immediately suspend new scope.
- scope.getLock().lock();
- }
- }
- return scope;
- }
-
- /**
- * Detaches the thread-local TransactionScope from the current thread. It
- * can be {@link TransactionScope#attach attached} later, and to any thread
- * 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) {
- scope.markDetached();
- mLocalScope.remove();
- }
- return scope;
- }
-
- // Called by TransactionScope.
- boolean removeLocalScope(TransactionScope<Txn> scope) {
- TransactionScope<Txn> existing = mLocalScope.get();
- if (existing == scope) {
- mLocalScope.remove();
- return true;
- }
- return false;
- }
-
- // Called by TransactionScope.
- boolean setLocalScope(TransactionScope<Txn> scope, boolean detached) {
- TransactionScope<Txn> existing = mLocalScope.get();
- if ((existing == null && detached) || existing == scope) {
- mLocalScope.set(scope);
- return true;
- }
- return false;
- }
-
- /**
- * Closes all transaction scopes. Should be called only when repository is
- * closed.
- *
- * @param suspend when true, indefinitely suspend all threads interacting
- * with transactions
- */
- public synchronized void close(boolean suspend) throws RepositoryException {
- if (mState == SUSPENDED) {
- // If suspended, attempting to close again will likely deadlock.
- return;
- }
-
- if (suspend) {
- for (TransactionScope<?> scope : mAllScopes.keySet()) {
- // Lock scope but don't release it. This prevents other threads
- // from beginning work during shutdown, which will likely fail
- // along the way.
- scope.getLock().lock();
- }
- }
-
- mState = suspend ? SUSPENDED : CLOSED;
-
- for (TransactionScope<?> scope : mAllScopes.keySet()) {
- scope.close();
- }
- }
-
- /**
- * Returns supported isolation level, which may be higher. If isolation
- * level cannot go higher (or lower than parent) then return null.
- *
- * @param parent optional parent transaction
- * @param level desired isolation level (may be null)
- */
- protected abstract IsolationLevel selectIsolationLevel(Transaction parent,
- IsolationLevel level);
-
- /**
- * Return true if transactions support "for update" mode.
- *
- * @since 1.2
- */
- protected abstract boolean supportsForUpdate();
-
- /**
- * Creates an internal transaction representation, with the optional parent
- * transaction. If parent is not null and real nested transactions are not
- * supported, simply return parent transaction for supporting fake nested
- * transactions.
- *
- * @param parent optional parent transaction
- * @param level required isolation level
- * @return new transaction, parent transaction, or possibly null if required
- * isolation level is none
- */
- protected abstract Txn createTxn(Txn parent, IsolationLevel level) throws Exception;
-
- /**
- * Creates an internal transaction representation, with the optional parent
- * transaction. If parent is not null and real nested transactions are not
- * supported, simply return parent transaction for supporting fake nested
- * transactions.
- *
- * <p>The default implementation of this method just calls the regular
- * createTxn method, ignoring the timeout parameter.
- *
- * @param parent optional parent transaction
- * @param level required isolation level
- * @param timeout desired timeout for lock acquisition, never negative
- * @param unit timeout unit, never null
- * @return new transaction, parent transaction, or possibly null if required
- * isolation level is none
- */
- protected Txn createTxn(Txn parent, IsolationLevel level,
- int timeout, TimeUnit unit)
- throws Exception
- {
- return createTxn(parent, level);
- }
-
- /**
- * Called when a transaction is about to be reused. The default
- * implementation of this method does nothing. Override if any preparation
- * is required to ready a transaction for reuse.
- *
- * @param txn transaction to reuse, never null
- * @since 1.1.3
- */
- protected void reuseTxn(Txn txn) throws Exception {
- }
-
- /**
- * Commits and closes the given internal transaction.
- *
- * @return true if transaction object is still valid
- */
- protected abstract boolean commitTxn(Txn txn) throws PersistException;
-
- /**
- * Aborts and closes the given internal transaction.
- */
- protected abstract void abortTxn(Txn txn) throws PersistException;
-}
diff --git a/src/main/java/com/amazon/carbonado/spi/TransactionPair.java b/src/main/java/com/amazon/carbonado/spi/TransactionPair.java deleted file mode 100644 index ff07bc2..0000000 --- a/src/main/java/com/amazon/carbonado/spi/TransactionPair.java +++ /dev/null @@ -1,109 +0,0 @@ -/*
- * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
- * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
- * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
- *
- * Licensed 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 com.amazon.carbonado.spi;
-
-import java.util.concurrent.TimeUnit;
-
-import com.amazon.carbonado.IsolationLevel;
-import com.amazon.carbonado.PersistException;
-import com.amazon.carbonado.Transaction;
-
-/**
- * Pairs two transaction together into one. The transaction cannot be atomic,
- * however. Inconsistencies can result if the primary transaction succeeds in
- * committing, but the secondary fails. Therefore, the designated primary
- * transaction should be the one that is more likely to fail. For example, the
- * primary transaction might rely on the network, but the secondary operates
- * locally.
- *
- * @author Don Schneider
- * @author Brian S O'Neill
- */
-public class TransactionPair implements Transaction {
- private final Transaction mPrimaryTransaction;
- private final Transaction mSecondaryTransaction;
-
- /**
- * @param primaryTransaction is committed first, exited last
- * @param secondaryTransaction is exited first, commited last
- */
- public TransactionPair(Transaction primaryTransaction, Transaction secondaryTransaction) {
- mPrimaryTransaction = primaryTransaction;
- mSecondaryTransaction = secondaryTransaction;
- }
-
- public void commit() throws PersistException {
- mPrimaryTransaction.commit();
- try {
- mSecondaryTransaction.commit();
- } catch (Exception e) {
- throw new PersistException
- ("Failure to commit secondary transaction has likely caused an inconsistency", e);
- }
- }
-
- public void exit() throws PersistException {
- try {
- mSecondaryTransaction.exit();
- } finally {
- // Do this second so if there is an exception, the user sees the
- // primary exception, which is presumably more important.
- mPrimaryTransaction.exit();
- }
- }
-
- public void setForUpdate(boolean forUpdate) {
- mPrimaryTransaction.setForUpdate(forUpdate);
- mSecondaryTransaction.setForUpdate(forUpdate);
- }
-
- public boolean isForUpdate() {
- return mPrimaryTransaction.isForUpdate() && mSecondaryTransaction.isForUpdate();
- }
-
- public void setDesiredLockTimeout(int timeout, TimeUnit unit) {
- mPrimaryTransaction.setDesiredLockTimeout(timeout, unit);
- mSecondaryTransaction.setDesiredLockTimeout(timeout, unit);
- }
-
- public IsolationLevel getIsolationLevel() {
- 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 deleted file mode 100644 index bce7804..0000000 --- a/src/main/java/com/amazon/carbonado/spi/TransactionScope.java +++ /dev/null @@ -1,619 +0,0 @@ -/*
- * Copyright 2008 Amazon Technologies, Inc. or its affiliates.
- * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
- * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
- *
- * Licensed 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 com.amazon.carbonado.spi;
-
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import com.amazon.carbonado.Cursor;
-import com.amazon.carbonado.FetchException;
-import com.amazon.carbonado.IsolationLevel;
-import com.amazon.carbonado.PersistException;
-import com.amazon.carbonado.RepositoryException;
-import com.amazon.carbonado.Storable;
-import com.amazon.carbonado.Transaction;
-
-/**
- * Container of thread local, scoped transactions.
- *
- * @param <Txn> Transaction implementation
- * @author Brian S O'Neill
- * @since 1.2
- * @see TransactionManager
- */
-public class TransactionScope<Txn> {
- final TransactionManager<Txn> mTxnMgr;
- final Lock mLock;
-
- TransactionImpl<Txn> mActive;
-
- // Tracks all registered cursors by storage type.
- private Map<Class<?>, CursorList<TransactionImpl<Txn>>> mCursors;
-
- private boolean mClosed;
- private boolean mDetached;
-
- TransactionScope(TransactionManager<Txn> txnMgr, boolean closed) {
- mTxnMgr = txnMgr;
- mLock = new ReentrantLock(true);
- }
-
- /**
- * Enters a new transaction scope which becomes the active transaction.
- *
- * @param level desired isolation level (may be null)
- * @throws UnsupportedOperationException if isolation level higher than
- * supported by repository
- */
- public Transaction enter(IsolationLevel level) {
- mLock.lock();
- try {
- TransactionImpl<Txn> parent = mActive;
- IsolationLevel actualLevel = mTxnMgr.selectIsolationLevel(parent, level);
- if (actualLevel == null) {
- if (parent == null) {
- throw new UnsupportedOperationException
- ("Desired isolation level not supported: " + level);
- } else {
- throw new UnsupportedOperationException
- ("Desired isolation level not supported: " + level
- + "; parent isolation level: " + parent.getIsolationLevel());
- }
- }
-
- return mActive = new TransactionImpl<Txn>(this, parent, false, actualLevel);
- } finally {
- mLock.unlock();
- }
- }
-
- /**
- * Enters a new top-level transaction scope which becomes the active
- * transaction.
- *
- * @param level desired isolation level (may be null)
- * @throws UnsupportedOperationException if isolation level higher than
- * supported by repository
- */
- public Transaction enterTop(IsolationLevel level) {
- mLock.lock();
- try {
- IsolationLevel actualLevel = mTxnMgr.selectIsolationLevel(null, level);
- if (actualLevel == null) {
- throw new UnsupportedOperationException
- ("Desired isolation level not supported: " + level);
- }
-
- return mActive = new TransactionImpl<Txn>(this, mActive, true, actualLevel);
- } finally {
- mLock.unlock();
- }
- }
-
- /**
- * Registers the given cursor against the active transaction, allowing it
- * to be closed on transaction exit or transaction manager close. If there
- * is no active transaction in scope, the cursor is registered as not part
- * of a transaction. Cursors should register when created.
- */
- public <S extends Storable> void register(Class<S> type, Cursor<S> cursor) {
- mLock.lock();
- try {
- checkClosed();
- if (mCursors == null) {
- mCursors = new IdentityHashMap<Class<?>, CursorList<TransactionImpl<Txn>>>();
- }
-
- CursorList<TransactionImpl<Txn>> cursorList = mCursors.get(type);
- if (cursorList == null) {
- cursorList = new CursorList<TransactionImpl<Txn>>();
- mCursors.put(type, cursorList);
- }
-
- cursorList.register(cursor, mActive);
-
- if (mActive != null) {
- mActive.register(cursor);
- }
- } finally {
- mLock.unlock();
- }
- }
-
- /**
- * Unregisters a previously registered cursor. Cursors should unregister
- * when closed.
- */
- public <S extends Storable> void unregister(Class<S> type, Cursor<S> cursor) {
- mLock.lock();
- try {
- if (mCursors != null) {
- CursorList<TransactionImpl<Txn>> cursorList = mCursors.get(type);
- if (cursorList != null) {
- TransactionImpl<Txn> txnImpl = cursorList.unregister(cursor);
- if (txnImpl != null) {
- txnImpl.unregister(cursor);
- }
- }
- }
- } finally {
- mLock.unlock();
- }
- }
-
- /**
- * Returns lock used by TransactionScope. While holding lock, operations
- * are suspended.
- */
- public Lock getLock() {
- return mLock;
- }
-
- /**
- * Returns the implementation for the active transaction, or null if there
- * is no active transaction.
- *
- * @throws Exception thrown by createTxn or reuseTxn
- */
- public Txn getTxn() throws Exception {
- mLock.lock();
- try {
- checkClosed();
- return mActive == null ? null : mActive.getTxn();
- } finally {
- mLock.unlock();
- }
- }
-
- /**
- * Returns true if an active transaction exists and it is for update.
- */
- public boolean isForUpdate() {
- mLock.lock();
- try {
- return (mClosed || mActive == null) ? false : mActive.isForUpdate();
- } finally {
- mLock.unlock();
- }
- }
-
- /**
- * Returns the isolation level of the active transaction, or null if there
- * is no active transaction.
- */
- public IsolationLevel getIsolationLevel() {
- mLock.lock();
- try {
- return (mClosed || mActive == null) ? null : mActive.getIsolationLevel();
- } finally {
- mLock.unlock();
- }
- }
-
- /**
- * Attach this scope to the current thread, if it has been {@link
- * TransactionManager#detachLocalScope detached}.
- *
- * @throws IllegalStateException if current thread has a different
- * transaction already attached
- */
- public void attach() {
- mLock.lock();
- try {
- if (mTxnMgr.setLocalScope(this, mDetached)) {
- mDetached = false;
- } else if (!mDetached) {
- throw new IllegalStateException("Transaction scope is not detached");
- } else {
- throw new IllegalStateException
- ("Current thread has a different transaction already attached");
- }
- } finally {
- mLock.unlock();
- }
- }
-
- // Called by TransactionImpl.
- void detach() {
- mLock.lock();
- try {
- if (mDetached || mTxnMgr.removeLocalScope(this)) {
- mDetached = true;
- } else {
- throw new IllegalStateException("Transaction is attached to a different thread");
- }
- } 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
- * when repository is closed.
- */
- void close() throws RepositoryException {
- mLock.lock();
- try {
- if (!mClosed) {
- while (mActive != null) {
- mActive.exit();
- }
- if (mCursors != null) {
- for (CursorList<TransactionImpl<Txn>> cursorList : mCursors.values()) {
- cursorList.closeCursors();
- }
- }
- }
- } finally {
- mClosed = true;
- mLock.unlock();
- }
- }
-
- /**
- * Caller must hold mLock.
- */
- private void checkClosed() {
- if (mClosed) {
- throw new IllegalStateException("Repository is closed");
- }
- }
-
- private static class TransactionImpl<Txn> implements Transaction {
- private final TransactionScope<Txn> mScope;
- private final TransactionImpl<Txn> mParent;
- private final boolean mTop;
- private final IsolationLevel mLevel;
-
- private boolean mForUpdate;
- private int mDesiredLockTimeout;
- private TimeUnit mTimeoutUnit;
-
- private TransactionImpl<Txn> mChild;
- private boolean mExited;
- private Txn mTxn;
-
- // Tracks all registered cursors.
- private CursorList<?> mCursorList;
-
- TransactionImpl(TransactionScope<Txn> scope,
- TransactionImpl<Txn> parent,
- boolean top,
- IsolationLevel level) {
- mScope = scope;
- mParent = parent;
- mTop = top;
- mLevel = level;
- if (!top && parent != null) {
- parent.mChild = this;
- mDesiredLockTimeout = parent.mDesiredLockTimeout;
- mTimeoutUnit = parent.mTimeoutUnit;
- }
- }
-
- public void commit() throws PersistException {
- TransactionScope<Txn> scope = mScope;
- scope.mLock.lock();
- try {
- if (!mExited) {
- if (mChild != null) {
- mChild.commit();
- }
-
- closeCursors();
-
- if (mTxn != null) {
- if (mParent == null || mParent.mTxn != mTxn) {
- try {
- if (!scope.mTxnMgr.commitTxn(mTxn)) {
- mTxn = null;
- }
- } catch (Throwable e) {
- mTxn = null;
- throw ExceptionTransformer.getInstance().toPersistException(e);
- }
- } else {
- // Indicate fake nested transaction committed.
- mTxn = null;
- }
- }
- }
- } finally {
- scope.mLock.unlock();
- }
- }
-
- public void exit() throws PersistException {
- TransactionScope<Txn> scope = mScope;
- scope.mLock.lock();
- try {
- if (!mExited) {
- if (mChild != null) {
- mChild.exit();
- }
-
- closeCursors();
-
- if (mTxn != null) {
- try {
- if (mParent == null || mParent.mTxn != mTxn) {
- try {
- scope.mTxnMgr.abortTxn(mTxn);
- } catch (Throwable e) {
- throw ExceptionTransformer.getInstance().toPersistException(e);
- }
- }
- } finally {
- mTxn = null;
- }
- }
-
- scope.mActive = mParent;
-
- mExited = true;
- }
- } finally {
- scope.mLock.unlock();
- }
- }
-
- public void setForUpdate(boolean forUpdate) {
- mForUpdate = forUpdate && mScope.mTxnMgr.supportsForUpdate();
- }
-
- public boolean isForUpdate() {
- return mForUpdate;
- }
-
- public void setDesiredLockTimeout(int timeout, TimeUnit unit) {
- TransactionScope<Txn> scope = mScope;
- scope.mLock.lock();
- try {
- if (timeout < 0) {
- mDesiredLockTimeout = 0;
- mTimeoutUnit = null;
- } else {
- mDesiredLockTimeout = timeout;
- mTimeoutUnit = unit;
- }
- } finally {
- scope.mLock.unlock();
- }
- }
-
- public IsolationLevel getIsolationLevel() {
- 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) {
- mCursorList = new CursorList<Object>();
- }
- mCursorList.register(cursor, null);
- }
-
- // Caller must hold mLock.
- <S extends Storable> void unregister(Cursor<S> cursor) {
- if (mCursorList != null) {
- mCursorList.unregister(cursor);
- }
- }
-
- // Caller must hold mLock.
- Txn getTxn() throws Exception {
- TransactionScope<Txn> scope = mScope;
- if (mTxn != null) {
- scope.mTxnMgr.reuseTxn(mTxn);
- } else {
- Txn parentTxn;
- if (mParent == null || mTop) {
- parentTxn = null;
- } else if ((parentTxn = mParent.mTxn) == null) {
- // No point in creating nested transaction if parent
- // has never been used. Create parent transaction
- // and use it in child transaction, just like a fake
- // nested transaction.
- if ((parentTxn = mParent.getTxn()) != null) {
- return mTxn = parentTxn;
- }
- // Isolation level of parent is none, so proceed to create
- // a real transaction.
- }
- if (mTimeoutUnit == null) {
- mTxn = scope.mTxnMgr.createTxn(parentTxn, mLevel);
- } else {
- mTxn = scope.mTxnMgr.createTxn(parentTxn, mLevel,
- mDesiredLockTimeout, mTimeoutUnit);
- }
- }
- return mTxn;
- }
-
- // Caller must hold mLock.
- private void closeCursors() throws PersistException {
- if (mCursorList != null) {
- mCursorList.closeCursors();
- }
- }
- }
-
- /**
- * Simple fast list/map for holding a small amount of cursors.
- */
- static class CursorList<V> {
- private int mSize;
- private Cursor<?>[] mCursors;
- private V[] mValues;
-
- CursorList() {
- mCursors = new Cursor[8];
- }
-
- /**
- * @param value optional value to associate
- */
- @SuppressWarnings("unchecked")
- void register(Cursor<?> cursor, V value) {
- int size = mSize;
- Cursor<?>[] cursors = mCursors;
-
- if (size == cursors.length) {
- int newLength = size << 1;
-
- Cursor<?>[] newCursors = new Cursor[newLength];
- System.arraycopy(cursors, 0, newCursors, 0, size);
- mCursors = cursors = newCursors;
-
- if (mValues != null) {
- V[] newValues = (V[]) new Object[newLength];
- System.arraycopy(mValues, 0, newValues, 0, size);
- mValues = newValues;
- }
- }
-
- cursors[size] = cursor;
-
- if (value != null) {
- V[] values = mValues;
- if (values == null) {
- mValues = values = (V[]) new Object[cursors.length];
- }
- values[size] = value;
- }
-
- mSize = size + 1;
- }
-
- V unregister(Cursor<?> cursor) {
- // Assuming that cursors are opened and closed in LIFO order
- // (stack order), search backwards to optimize.
- Cursor<?>[] cursors = mCursors;
- int size = mSize;
- int i = size;
- search: {
- while (--i >= 0) {
- if (cursors[i] == cursor) {
- break search;
- }
- }
- // Not found.
- return null;
- }
-
- V[] values = mValues;
- V value;
-
- if (values == null) {
- value = null;
- if (i == size - 1) {
- // Clear reference so that it can be garbage collected.
- cursors[i] = null;
- } else {
- // Shift array elements down.
- System.arraycopy(cursors, i + 1, cursors, i, size - i - 1);
- }
- } else {
- value = values[i];
- if (i == size - 1) {
- // Clear references so that they can be garbage collected.
- cursors[i] = null;
- values[i] = null;
- } else {
- // Shift array elements down.
- System.arraycopy(cursors, i + 1, cursors, i, size - i - 1);
- System.arraycopy(values, i + 1, values, i, size - i - 1);
- }
- }
-
- mSize = size - 1;
- return value;
- }
-
- int size() {
- return mSize;
- }
-
- Cursor<?> getCursor(int index) {
- return mCursors[index];
- }
-
- V getValue(int index) {
- V[] values = mValues;
- return values == null ? null : values[index];
- }
-
- /**
- * Closes all cursors and resets the size of this list to 0.
- */
- void closeCursors() throws PersistException {
- // Note: Iteration must be in reverse order. Calling close on the
- // cursor should cause it to unregister from this list. This will
- // cause only a modification to the end of the list, which is no
- // longer needed by this method.
- try {
- Cursor<?>[] cursors = mCursors;
- V[] values = mValues;
- int i = mSize;
- if (values == null) {
- while (--i >= 0) {
- Cursor<?> cursor = cursors[i];
- if (cursor != null) {
- cursor.close();
- cursors[i] = null;
- }
- }
- } else {
- while (--i >= 0) {
- Cursor<?> cursor = cursors[i];
- if (cursor != null) {
- cursor.close();
- cursors[i] = null;
- values[i] = null;
- }
- }
- }
- } catch (FetchException e) {
- throw e.toPersistException();
- }
- mSize = 0;
- }
- }
-}
|