package com.googlecode.objectify.impl; import com.google.appengine.api.datastore.Transaction; import com.googlecode.objectify.Result; import com.googlecode.objectify.util.FutureHelper; import com.googlecode.objectify.util.SimpleFutureWrapper; import com.googlecode.objectify.util.cmd.TransactionWrapper; import lombok.Getter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Future; /** */ public class TransactionImpl extends TransactionWrapper { /** * Holds the session data and knows what to do with it. */ private final TransactorYes<?> transactor; /** * Operations which modify the session must be enlisted in the transaction and completed * before the transaction commits. This is so that the session reaches a consistent state * before it is propagated to the parent session. */ private List<Result<?>> enlisted = new ArrayList<>(); /** Listeners that will be executed _after_ a commit completes successfully */ private List<Runnable> listeners = new ArrayList<>(); /** * An arbitrary bag of stuff that can be associated with the current transaction context. Not used * by Objectify internally but useful sometimes for application programming. */ @Getter private Map<Object, Object> bag = new HashMap<>(); /** */ public TransactionImpl(Transaction raw, TransactorYes<?> transactor) { super(raw); this.transactor = transactor; } /** * Enlist any operations that modify the session. */ public void enlist(Result<?> result) { enlisted.add(result); } /** * Add a listener to be called after the transaction commits. */ public void listenForCommit(Runnable listener) { listeners.add(listener); } public void runCommitListeners() { for (Runnable listener : listeners) { listener.run(); } } /* (non-Javadoc) * @see com.googlecode.objectify.util.cmd.TransactionWrapper#commit() */ @Override public void commit() { FutureHelper.quietGet(commitAsync()); } /* (non-Javadoc) * @see com.googlecode.objectify.util.cmd.TransactionWrapper#commitAsync() */ @Override public Future<Void> commitAsync() { // Complete any enlisted operations so that the session becomes consistent. Note that some of the // enlisted load operations might result in further enlistment... so we have to do this in a loop // that protects against concurrent modification exceptions while (!enlisted.isEmpty()) { List<Result<?>> last = enlisted; enlisted = new ArrayList<>(); for (Result<?> result: last) result.now(); } return new SimpleFutureWrapper<Void, Void>(super.commitAsync()) { @Override protected Void wrap(Void nothing) throws Exception { transactor.committed(); return nothing; } }; } }