org.eclipse.ecr.core.api.TransactionalCoreSessionWrapper.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ecr.core.api.TransactionalCoreSessionWrapper.java

Source

/*
 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Florent Guillaume
 */

package org.eclipse.ecr.core.api;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.naming.NamingException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.Transaction;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ecr.core.api.CoreSession;
import org.eclipse.ecr.core.api.NoRollbackOnException;
import org.eclipse.ecr.runtime.transaction.TransactionHelper;

/**
 * Wrapper around a CoreSession that gives it transactional behavior.
 * <p>
 * Transactional behavior:
 * <ul>
 * <li>notifies the event service on transaction start/stop</li>
 * <li>throws RollbackException</li>
 * </ul>
 */
public class TransactionalCoreSessionWrapper implements InvocationHandler, Synchronization {

    private static final Log log = LogFactory.getLog(TransactionalCoreSessionWrapper.class);

    private static final Class<?>[] INTERFACES = new Class[] { CoreSession.class };

    private final CoreSession session;

    /**
     * Per-thread flag with transaction status:
     * <ul>
     * <li>{@code null}: outside transaction</li>
     * <li>{@code TRUE}: in a transaction</li>
     * </ul>
     */
    private final ThreadLocal<Boolean> threadBound = new ThreadLocal<Boolean>();

    protected TransactionalCoreSessionWrapper(CoreSession session) {
        this.session = session;
    }

    public static CoreSession wrap(CoreSession session) {
        try {
            TransactionHelper.lookupTransactionManager();
        } catch (NamingException e) {
            // no transactions, do not wrap
            return session;
        }
        ClassLoader cl = session.getClass().getClassLoader();
        return (CoreSession) Proxy.newProxyInstance(cl, INTERFACES, new TransactionalCoreSessionWrapper(session));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Boolean b = threadBound.get();
        if (b == null) {
            // first call in thread
            try {
                Transaction tx = TransactionHelper.lookupTransactionManager().getTransaction();
                if (tx != null && tx.getStatus() != Status.STATUS_MARKED_ROLLBACK) {
                    tx.registerSynchronization(this);
                    session.afterBegin();
                    threadBound.set(Boolean.TRUE);
                }
            } catch (NamingException e) {
                // no transaction manager, ignore
            } catch (Exception e) {
                log.error("Error on transaction synchronizer registration", e);
            }
        }
        try {
            return method.invoke(session, args);
        } catch (Throwable t) {
            if (TransactionHelper.isTransactionActive() && needsRollback(method, t)) {
                TransactionHelper.setTransactionRollbackOnly();
            }
            if (t instanceof InvocationTargetException) {
                Throwable tt = ((InvocationTargetException) t).getTargetException();
                if (tt != null) {
                    throw tt;
                }
            }
            throw t;
        }
    }

    protected boolean needsRollback(Method method, Throwable t) {
        for (Annotation annotation : method.getAnnotations()) {
            if (annotation.annotationType() == NoRollbackOnException.class) {
                return false;
            }
        }
        return true;
    }

    @Override
    public void beforeCompletion() {
        session.beforeCompletion();
    }

    @Override
    public void afterCompletion(int status) {
        threadBound.remove();
        boolean committed;
        if (status == Status.STATUS_COMMITTED) {
            committed = true;
        } else if (status == Status.STATUS_ROLLEDBACK) {
            committed = false;
        } else {
            log.error("Unexpected status after completion: " + status);
            return;
        }
        session.afterCompletion(committed);
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName() + '(' + session + ')';
    }

}