com.google.inject.persist.jpa.KuneJpaLocalTxnInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for com.google.inject.persist.jpa.KuneJpaLocalTxnInterceptor.java

Source

/**
 * Copyright (C) 2010 Google, Inc.
 *
 * 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.google.inject.persist.jpa;

import java.lang.reflect.Method;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cc.kune.core.client.errors.DefaultException;
import cc.kune.core.server.persist.KuneTransactional;

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

// TODO: Auto-generated Javadoc
/**
 * The Class KuneJpaLocalTxnInterceptor.
 * 
 * @author Dhanji R. Prasanna (dhanji@gmail.com)
 */
public class KuneJpaLocalTxnInterceptor implements MethodInterceptor {

    /**
     * The Class Internal.
     * 
     * @author vjrj@ourproject.org (Vicente J. Ruiz Jurado)
     */
    @KuneTransactional
    private static class Internal {
    }

    public static final Log LOG = LogFactory.getLog(KuneJpaLocalTxnInterceptor.class);

    // Tracks if the unit of work was begun implicitly by this transaction.
    /** The did we start work. */
    private final ThreadLocal<Boolean> didWeStartWork = new ThreadLocal<Boolean>();

    /** The em provider. */
    @Inject
    private final JpaPersistService emProvider = null;

    /** The unit of work. */
    @Inject
    private final UnitOfWork unitOfWork = null;

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept
     * .MethodInvocation)
     */
    @Override
    public Object invoke(final MethodInvocation methodInvocation) throws Throwable, DefaultException {

        // Should we start a unit of work?
        if (!emProvider.isWorking()) {
            emProvider.begin();
            didWeStartWork.set(true);
            LOG.debug("Starting transaction");
        }

        final KuneTransactional transactional = readTransactionMetadata(methodInvocation);
        final EntityManager em = this.emProvider.get();

        // Allow 'joining' of transactions if there is an enclosing
        // @KuneTransactional method.
        if (em.getTransaction().isActive()) {
            LOG.debug("Joining a previous transaction");
            return methodInvocation.proceed();
        }

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

        Object result;
        try {
            result = methodInvocation.proceed();

        } catch (final Exception e) {
            // commit transaction only if rollback didnt occur
            LOG.debug("Exception in transaction", e);
            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 (null != didWeStartWork.get() && !txn.isActive()) {
                didWeStartWork.remove();
                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 {
            LOG.debug("Trying to commit transaction");
            txn.commit();
        } finally {
            LOG.debug("Transaction commited");
            // close the em if necessary
            if (null != didWeStartWork.get()) {
                didWeStartWork.remove();
                unitOfWork.end();
            }
        }

        // or return result
        return result;
    }

    // TODO(dhanji): Cache this method's results.
    /**
     * Read transaction metadata.
     * 
     * @param methodInvocation
     *          the method invocation
     * @return the kune transactional
     */
    private KuneTransactional readTransactionMetadata(final MethodInvocation methodInvocation) {
        KuneTransactional transactional;
        final Method method = methodInvocation.getMethod();
        final Class<?> targetClass = methodInvocation.getThis().getClass();

        transactional = method.getAnnotation(KuneTransactional.class);
        if (null == transactional) {
            // If none on method, try the class.
            transactional = targetClass.getAnnotation(KuneTransactional.class);
        }
        if (null == transactional) {
            // If there is no transactional annotation present, use the default
            transactional = Internal.class.getAnnotation(KuneTransactional.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
     * @return true, if successful
     */
    private boolean rollbackIfNecessary(final KuneTransactional transactional, final Exception e,
            final EntityTransaction txn) {
        boolean commit = true;

        // check rollback clauses
        for (final 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 (final 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;
    }
}