Package io.fathom.cloud.persist

Source Code of io.fathom.cloud.persist.ZookeeperLocalTxnInterceptor$Internal

package io.fathom.cloud.persist;

import java.lang.reflect.Method;

import javax.persistence.EntityTransaction;
import javax.persistence.OptimisticLockException;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.google.inject.persist.UnitOfWork;

/**
* Based on JpaLocalTxnInterceptor
*/
class ZookeeperLocalTxnInterceptor implements MethodInterceptor {
    private static final Logger log = LoggerFactory.getLogger(ZookeeperLocalTxnInterceptor.class);

    @Inject
    private final ZookeeperPersistService emProvider = null;

    @Inject
    private final UnitOfWork unitOfWork = null;

    @Transactional
    private static class Internal {
    }

    // TODO: In Guice, this is ThreadLocal. Not clear why!
    // Tracks if the unit of work was begun implicitly by this transaction.
    // private final ThreadLocal<Boolean> didWeStartWork = new
    // ThreadLocal<Boolean>();

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        int attempt = 0;

        int maxAttempts = maxAttempts();

        while (true) {
            attempt++;

            try {
                boolean didWeStartWork = false;

                // Should we start a unit of work?
                if (!emProvider.isWorking()) {
                    emProvider.begin();
                    didWeStartWork = true;
                }

                ZookeeperEntityManager em = this.emProvider.get();

                // Allow 'joining' of transactions if there is an enclosing
                // @Transactional method.
                if (em.getTransaction().isActive()) {
                    // Don't retry at this level; rely on the outer transaction
                    maxAttempts = 0;

                    return methodInvocation.proceed();
                }

                return invoke0(methodInvocation, em, didWeStartWork);
            } catch (OptimisticLockException e) {
                boolean retry = false;

                if (attempt < maxAttempts) {
                    retry = true;
                }

                if (!retry) {
                    // throw new CloudException(
                    // "Unable to update due to concurrent modification",
                    // e);

                    if (maxAttempts != 0) {
                        log.warn("Too many retries on OptimisticLockException", e);
                    }

                    throw e;
                } else {
                    log.warn("Retrying after OptimisticLockException");
                    continue;
                }
            }
        }
    }

    private Object invoke0(MethodInvocation methodInvocation, ZookeeperEntityManager em, boolean didWeStartWork)
            throws Throwable, Exception {
        Transactional transactional = readTransactionMetadata(methodInvocation);

        final EntityTransaction txn = em.getTransaction();
        txn.begin();

        Object result;
        try {
            result = methodInvocation.proceed();
        } catch (Exception e) {
            // commit transaction only if rollback didnt occur
            if (rollbackIfNecessary(transactional, e, txn)) {
                txn.commit();
            }

            // propagate whatever exception is thrown anyway
            throw e;
        } finally {
            // Close the em if necessary (guarded so this code doesn't
            // run
            // unless catch fired).
            if (didWeStartWork && !txn.isActive()) {
                didWeStartWork = false;
                unitOfWork.end();
            }
        }

        // everything was normal so commit the txn (do not move into try
        // block
        // above as it
        // interferes with the advised method's throwing semantics)
        try {
            txn.commit();
        } finally {
            // close the em if necessary
            if (didWeStartWork) {
                didWeStartWork = false;
                unitOfWork.end();
            }
        }

        // or return result
        return result;
    }

    private int maxAttempts() {
        return 5;
    }

    // TODO(dhanji): Cache this method's results.
    private Transactional readTransactionMetadata(MethodInvocation methodInvocation) {
        Transactional transactional;
        Method method = methodInvocation.getMethod();
        Class<?> targetClass = methodInvocation.getThis().getClass();

        transactional = method.getAnnotation(Transactional.class);
        if (null == transactional) {
            // If none on method, try the class.
            transactional = targetClass.getAnnotation(Transactional.class);
        }
        if (null == transactional) {
            // If there is no transactional annotation present, use the default
            transactional = Internal.class.getAnnotation(Transactional.class);
        }

        return transactional;
    }

    /**
     * Returns True if rollback DID NOT HAPPEN (i.e. if commit should continue).
     *
     * @param transactional
     *            The metadata annotaiton of the method
     * @param e
     *            The exception to test for rollback
     * @param txn
     *            A JPA Transaction to issue rollbacks on
     */
    private boolean rollbackIfNecessary(Transactional transactional, Exception e, EntityTransaction txn) {
        boolean commit = true;

        if (e instanceof OptimisticLockException) {
            txn.rollback();
            return false;
        }

        // check rollback clauses
        for (Class<? extends Exception> rollBackOn : transactional.rollbackOn()) {

            // if one matched, try to perform a rollback
            if (rollBackOn.isInstance(e)) {
                commit = false;

                // check ignore clauses (supercedes rollback clause)
                for (Class<? extends Exception> exceptOn : transactional.ignore()) {
                    // An exception to the rollback clause was found, DON'T
                    // rollback
                    // (i.e. commit and throw anyway)
                    if (exceptOn.isInstance(e)) {
                        commit = true;
                        break;
                    }
                }

                // rollback only if nothing matched the ignore check
                if (!commit) {
                    txn.rollback();
                }
                // otherwise continue to commit

                break;
            }
        }

        return commit;
    }
}
TOP

Related Classes of io.fathom.cloud.persist.ZookeeperLocalTxnInterceptor$Internal

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.