package com.googlecode.objectify.impl;
import com.google.common.base.Preconditions;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.TxnType;
import com.googlecode.objectify.Work;
import java.util.ConcurrentModificationException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Transactor which represents the absence of a transaction.
*
* @author Jeff Schnitzer <jeff@infohazard.org>
*/
/**
* @author jeff
*
* @param <O>
*/
public class TransactorNo<O extends Objectify> extends Transactor<O>
{
/** */
private static final Logger log = Logger.getLogger(TransactorNo.class.getName());
/**
*/
public TransactorNo(Objectify ofy) {
super(ofy);
}
/**
*/
public TransactorNo(Objectify ofy, Session session) {
super(ofy, session);
}
/* (non-Javadoc)
* @see com.googlecode.objectify.Objectify#getTxn()
*/
@Override
public TransactionImpl getTransaction() {
// This version doesn't have a transaction, always null.
return null;
}
/* (non-Javadoc)
* @see com.googlecode.objectify.impl.cmd.Transactor#transactionless()
*/
@Override
public ObjectifyImpl<O> transactionless(ObjectifyImpl<O> parent) {
return parent;
}
/* (non-Javadoc)
* @see com.googlecode.objectify.impl.cmd.Transactor#execute(com.googlecode.objectify.TxnType, com.googlecode.objectify.Work)
*/
@Override
public <R> R execute(ObjectifyImpl<O> parent, TxnType txnType, Work<R> work) {
switch (txnType) {
case MANDATORY:
throw new IllegalStateException("MANDATORY transaction but no transaction present");
case NOT_SUPPORTED:
case NEVER:
case SUPPORTS:
return work.run();
case REQUIRED:
case REQUIRES_NEW:
return transact(parent, work);
default:
throw new IllegalStateException("Impossible, some unknown txn type");
}
}
/* (non-Javadoc)
* @see com.googlecode.objectify.impl.Transactor#transact(com.googlecode.objectify.impl.ObjectifyImpl, com.googlecode.objectify.Work)
*/
@Override
public <R> R transact(ObjectifyImpl<O> parent, Work<R> work) {
return this.transactNew(parent, Integer.MAX_VALUE, work);
}
/* (non-Javadoc)
* @see com.googlecode.objectify.impl.Transactor#transactNew(com.googlecode.objectify.impl.ObjectifyImpl, int, com.googlecode.objectify.Work)
*/
@Override
public <R> R transactNew(ObjectifyImpl<O> parent, int limitTries, Work<R> work) {
Preconditions.checkArgument(limitTries >= 1);
while (true) {
try {
return transactOnce(parent, work);
} catch (ConcurrentModificationException ex) {
if (--limitTries > 0) {
if (log.isLoggable(Level.WARNING))
log.warning("Optimistic concurrency failure for " + work + " (retrying): " + ex);
if (log.isLoggable(Level.FINEST))
log.log(Level.FINEST, "Details of optimistic concurrency failure", ex);
} else {
throw ex;
}
}
}
}
/**
* One attempt at executing a transaction
*/
private <R> R transactOnce(ObjectifyImpl<O> parent, Work<R> work) {
ObjectifyImpl<O> txnOfy = startTransaction(parent);
ObjectifyService.push(txnOfy);
boolean committedSuccessfully = false;
try {
R result = work.run();
txnOfy.flush();
txnOfy.getTransaction().commit();
committedSuccessfully = true;
return result;
}
finally
{
if (txnOfy.getTransaction().isActive()) {
try {
txnOfy.getTransaction().rollback();
} catch (RuntimeException ex) {
log.log(Level.SEVERE, "Rollback failed, suppressing error", ex);
}
}
ObjectifyService.pop();
if (committedSuccessfully) {
txnOfy.getTransaction().runCommitListeners();
}
}
}
/**
* Create a new transactional session by cloning this instance and resetting the transactor component.
*/
ObjectifyImpl<O> startTransaction(ObjectifyImpl<O> parent) {
ObjectifyImpl<O> cloned = parent.clone();
cloned.transactor = new TransactorYes<>(cloned, this);
return cloned;
}
}