/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.rdfbean.guice;
import java.lang.reflect.Method;
import java.util.Map;
import javax.annotation.concurrent.ThreadSafe;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.transaction.annotation.Transactional;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.mysema.rdfbean.model.RDFBeanTransaction;
import com.mysema.rdfbean.object.FlushMode;
import com.mysema.rdfbean.object.Session;
import com.mysema.rdfbean.object.SessionFactory;
import com.mysema.rdfbean.object.SimpleSessionContext;
import com.mysema.rdfbean.object.TxException;
/**
* TransactionalInterceptor provides a MethodInterceptor implementation for
* transactional method interception
*
* @author tiwe
* @version $Id$
*
*/
@ThreadSafe
class TransactionalInterceptor implements MethodInterceptor {
private final Provider<Map<Method, Transactional>> configuration;
private SimpleSessionContext sessionContext;
public TransactionalInterceptor(Provider<Map<Method, Transactional>> configuration) {
this.configuration = configuration;
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable { // NOSONAR
Transactional annotation = configuration.get().get(methodInvocation.getMethod());
boolean inSession = false;
boolean inTx = false;
if (sessionContext.getCurrentSession() != null) {
inSession = true;
Session session = sessionContext.getCurrentSession();
inTx = session.getTransaction() != null && session.getTransaction().isActive();
}
boolean intercepted = isIntercepted(annotation, inTx);
if (!intercepted) {
return methodInvocation.proceed();
}
Session session = sessionContext.getOrCreateSession();
FlushMode savedFlushMode = session.getFlushMode();
try {
RDFBeanTransaction txn = doBegin(session, annotation);
Object result;
try {
result = methodInvocation.proceed();
} catch (Exception e) {
if (txn.isRollbackOnly() || isRollbackNecessary(annotation, e, txn)) {
doRollback(txn);
} else {
doCommit(session, txn);
}
throw e;
}
if (!txn.isRollbackOnly()) {
doCommit(session, txn);
} else {
doRollback(txn);
}
return result;
} finally {
session.setFlushMode(savedFlushMode);
sessionContext.releaseSession();
if (!inSession) {
session.close();
}
}
}
private boolean isIntercepted(Transactional annotation, boolean inTx) {
switch (annotation.propagation()) {
case REQUIRED:
case REQUIRES_NEW:
case NESTED:
if (inTx) {
return false;
}
break;
case MANDATORY:
if (inTx) {
return false;
} else {
throw new TxException("Tx propagation " + annotation.propagation() + " without transaction");
}
case NOT_SUPPORTED:
case NEVER:
if (inTx) {
throw new TxException("Tx propagation " + annotation.propagation() + " in transaction");
} else {
return false;
}
}
return true;
}
private RDFBeanTransaction doBegin(Session session, Transactional transactional) {
RDFBeanTransaction txn = session.beginTransaction(
transactional.readOnly(),
transactional.timeout(),
transactional.isolation().value());
session.setFlushMode(FlushMode.COMMIT);
return txn;
}
private void doCommit(Session session, RDFBeanTransaction txn) throws Exception {
Exception commitException = null;
try {
session.flush();
txn.commit();
} catch (RuntimeException re) {
doRollback(txn);
commitException = re;
}
if (commitException != null) {
throw commitException;
}
}
private void doRollback(RDFBeanTransaction txn) {
txn.rollback();
}
private boolean isRollbackNecessary(Transactional transactional, Exception e, RDFBeanTransaction txn) {
boolean rollBack = false;
for (Class<? extends Throwable> rollBackOn : transactional.rollbackFor()) {
if (rollBackOn.isInstance(e)) {
rollBack = true;
for (Class<? extends Throwable> exceptOn : transactional.noRollbackFor()) {
if (exceptOn.isInstance(e)) {
rollBack = false;
break;
}
}
break;
}
}
return rollBack;
}
@Inject
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionContext = new SimpleSessionContext(sessionFactory);
sessionFactory.setSessionContext(sessionContext);
}
}