package com.brightgenerous.injection.mybatis;
//@formatter:off
/*
* origin file
* groupId: org.mybatis
* artifactId: mybatis-guice
* version: 3.5
* org.mybatis.guice.transactional.TransactionalMethodInterceptor
*
* edit
* - package org.mybatis.guice.transactional;
* + //package org.mybatis.guice.transactional;
*
* + import org.mybatis.guice.transactional.Transactional;
*
* - public final class TransactionalMethodInterceptor implements MethodInterceptor {
* + //public final class TransactionalMethodInterceptor implements MethodInterceptor {
* + public class TransactionalMethodInterceptor implements MethodInterceptor {
*
* - Transactional transactional = interceptedMethod.getAnnotation(Transactional.class);
* + //Transactional transactional = interceptedMethod.getAnnotation(Transactional.class);
* + Transactional transactional = TransactionalImpl.getAnnotation(interceptedMethod, this);
*
* + private final boolean readOnly;
*
* + public TransactionalMethodInterceptor() {
* + this(false);
* + }
*
* + public TransactionalMethodInterceptor(boolean readOnly) {
* + this.readOnly = readOnly;
* + }
*
* + if (!readOnly) {
* + Connection conn = sqlSessionManager.getConnection();
* + if (conn.isReadOnly()) {
* + conn.setReadOnly(false);
* + }
* + }
*
* + protected void setting() {
* + }
*
*
* add codes
* + class TransactionalImpl implements Transactional {
* + ...
* + ...to eof
*/
/*
* Copyright 2010-2012 The MyBatis Team
*
* 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 org.mybatis.guice.transactional;
import static java.lang.String.*;
import static java.lang.Thread.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Arrays;
import javax.inject.Inject;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionManager;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.mybatis.guice.transactional.Isolation;
import org.mybatis.guice.transactional.Transactional;
/**
* Method interceptor for {@link Transactional} annotation.
*
* @version $Id$
*/
//public final class TransactionalMethodInterceptor implements MethodInterceptor {
public class TransactionalMethodInterceptor implements MethodInterceptor {
private static final Class<?>[] CAUSE_TYPES = new Class[]{ Throwable.class };
private static final Class<?>[] MESSAGE_CAUSE_TYPES = new Class[]{ String.class, Throwable.class };
/**
* This class logger.
*/
private final Log log = LogFactory.getLog(getClass());
/**
* The {@code SqlSessionManager} reference.
*/
@Inject
private SqlSessionManager sqlSessionManager;
private final boolean readOnly;
public TransactionalMethodInterceptor() {
this(false);
}
public TransactionalMethodInterceptor(boolean readOnly) {
this.readOnly = readOnly;
}
/**
* Sets the SqlSessionManager instance.
*
* @param sqlSessionManager the SqlSessionManager instance.
*/
public void setSqlSessionManager(SqlSessionManager sqlSessionManager) {
this.sqlSessionManager = sqlSessionManager;
}
/**
* {@inheritDoc}
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method interceptedMethod = invocation.getMethod();
//Transactional transactional = interceptedMethod.getAnnotation(Transactional.class);
Transactional transactional = TransactionalImpl.getAnnotation(interceptedMethod, this);
String debugPrefix = null;
if (log.isDebugEnabled()) {
debugPrefix = format("[Intercepted method: %s]", interceptedMethod.toGenericString());
}
boolean isSessionInherited = sqlSessionManager.isManagedSessionStarted();
if (isSessionInherited) {
if (log.isDebugEnabled()) {
log.debug(format("%s - SqlSession already set for thread: %s",
debugPrefix,
currentThread().getId()));
}
} else {
if (log.isDebugEnabled()) {
log.debug(format("%s - SqlSession not set for thread: %s, creating a new one",
debugPrefix,
currentThread().getId()));
}
sqlSessionManager.startManagedSession(transactional.executorType(), transactional.isolation().getTransactionIsolationLevel());
if (!readOnly) {
Connection conn = sqlSessionManager.getConnection();
if (conn.isReadOnly()) {
conn.setReadOnly(false);
}
}
}
Object object = null;
try {
object = invocation.proceed();
if (!isSessionInherited && !transactional.rollbackOnly()) {
sqlSessionManager.commit(transactional.force());
}
} catch (Throwable t) {
// rollback the transaction
sqlSessionManager.rollback(transactional.force());
// check the caught exception is declared in the invoked method
for (Class<?> exceptionClass : interceptedMethod.getExceptionTypes()) {
if (exceptionClass.isAssignableFrom(t.getClass())) {
throw t;
}
}
// check the caught exception is of same rethrow type
if (transactional.rethrowExceptionsAs().isAssignableFrom(t.getClass())) {
throw t;
}
// rethrow the exception as new exception
String errorMessage;
Object[] initargs;
Class<?>[] initargsType;
if (transactional.exceptionMessage().length() != 0) {
errorMessage = format(transactional.exceptionMessage(), invocation.getArguments());
initargs = new Object[]{ errorMessage, t };
initargsType = MESSAGE_CAUSE_TYPES;
} else {
initargs = new Object[]{ t };
initargsType = CAUSE_TYPES;
}
Constructor<? extends Throwable> exceptionConstructor = getMatchingConstructor(transactional.rethrowExceptionsAs(), initargsType);
Throwable rethrowEx = null;
if (exceptionConstructor != null) {
try {
rethrowEx = exceptionConstructor.newInstance(initargs);
} catch (Exception e) {
errorMessage = format("Impossible to re-throw '%s', it needs the constructor with %s argument(s).",
transactional.rethrowExceptionsAs().getName(),
Arrays.toString(initargsType));
log.error(errorMessage, e);
rethrowEx = new RuntimeException(errorMessage, e);
}
} else {
errorMessage = format("Impossible to re-throw '%s', it needs the constructor with %s or %s argument(s).",
transactional.rethrowExceptionsAs().getName(),
Arrays.toString(CAUSE_TYPES),
Arrays.toString(MESSAGE_CAUSE_TYPES));
log.error(errorMessage);
rethrowEx = new RuntimeException(errorMessage);
}
throw rethrowEx;
} finally {
// skip close when the session is inherited from another Transactional method
if (!isSessionInherited) {
if (transactional.rollbackOnly()) {
if (log.isDebugEnabled()) {
log.debug(debugPrefix
+ " - SqlSession of thread: "
+ currentThread().getId()
+ " was in rollbackOnly mode, rolling it back");
}
sqlSessionManager.rollback(true);
}
if (log.isDebugEnabled()) {
log.debug(format("%s - SqlSession of thread: %s terminated its life-cycle, closing it",
debugPrefix,
currentThread().getId()));
}
sqlSessionManager.close();
} else if (log.isDebugEnabled()) {
log.debug(format("%s - SqlSession of thread: %s is inherited, skipped close operation",
debugPrefix,
currentThread().getId()));
}
}
return object;
}
@SuppressWarnings("unchecked")
private static <E extends Throwable> Constructor<E> getMatchingConstructor(Class<E> type,
Class<?>[] argumentsType) {
Class<? super E> currentType = type;
while (Object.class != currentType) {
for (Constructor<?> constructor : currentType.getConstructors()) {
if (Arrays.equals(argumentsType, constructor.getParameterTypes())) {
return (Constructor<E>) constructor;
}
}
currentType = currentType.getSuperclass();
}
return null;
}
protected void setting() {
}
}
/*
* add codes
*/
//@formatter:on
@SuppressWarnings("all")
class TransactionalImpl implements Transactional {
static Transactional getAnnotation(Method method, TransactionalMethodInterceptor interceptor) {
Transactional setting;
try {
Method m = interceptor.getClass().getDeclaredMethod("setting");
setting = m.getAnnotation(Transactional.class);
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
return new TransactionalImpl(method.getAnnotation(Transactional.class), setting);
}
private static final Transactional def;
static {
try {
Method method = TransactionalImpl.class.getDeclaredMethod("annotated");
def = method.getAnnotation(Transactional.class);
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
@Transactional
void annotated() {
}
private final Transactional particular;
private final Transactional setting;
TransactionalImpl(Transactional particular, Transactional setting) {
this.particular = particular;
this.setting = setting;
}
@Override
public Class<? extends Annotation> annotationType() {
Class<? extends Annotation> ret = null;
if (particular != null) {
ret = particular.annotationType();
}
if ((ret == null) && (setting != null)) {
ret = setting.annotationType();
}
if (ret == null) {
ret = def.annotationType();
}
return null;
}
@Override
public ExecutorType executorType() {
ExecutorType ret = null;
if (particular != null) {
ret = particular.executorType();
}
if ((ret == null) && (setting != null)) {
ret = setting.executorType();
}
if (ret == null) {
ret = def.executorType();
}
return ret;
}
@Override
public Isolation isolation() {
Isolation ret = null;
if (particular != null) {
ret = particular.isolation();
}
if ((ret == null) && (setting != null)) {
ret = setting.isolation();
}
if (ret == null) {
ret = def.isolation();
}
return ret;
}
@Override
public TransactionIsolationLevel isolationLevel() {
TransactionIsolationLevel ret = null;
if (particular != null) {
ret = particular.isolationLevel();
}
if ((ret == null) && (setting != null)) {
ret = setting.isolationLevel();
}
if (ret == null) {
ret = def.isolationLevel();
}
return ret;
}
@Override
public boolean force() {
Boolean ret = null;
if (particular != null) {
ret = particular.force() ? Boolean.TRUE : Boolean.FALSE;
}
if ((ret == null) && (setting != null)) {
ret = setting.force() ? Boolean.TRUE : Boolean.FALSE;
}
if (ret == null) {
ret = def.force() ? Boolean.TRUE : Boolean.FALSE;
}
return ret.booleanValue();
}
@Override
public boolean autoCommit() {
Boolean ret = null;
if (particular != null) {
ret = particular.autoCommit() ? Boolean.TRUE : Boolean.FALSE;
}
if ((ret == null) && (setting != null)) {
ret = setting.autoCommit() ? Boolean.TRUE : Boolean.FALSE;
}
if (ret == null) {
ret = def.autoCommit() ? Boolean.TRUE : Boolean.FALSE;
}
return ret.booleanValue();
}
@Override
public Class<? extends Throwable> rethrowExceptionsAs() {
Class<? extends Throwable> ret = null;
if (particular != null) {
ret = particular.rethrowExceptionsAs();
}
if ((ret == null) && (setting != null)) {
ret = setting.rethrowExceptionsAs();
}
if (ret == null) {
ret = def.rethrowExceptionsAs();
}
return ret;
}
@Override
public String exceptionMessage() {
String ret = null;
if (particular != null) {
ret = particular.exceptionMessage();
}
if ((ret == null) && (setting != null)) {
ret = setting.exceptionMessage();
}
if (ret == null) {
ret = def.exceptionMessage();
}
return ret;
}
@Override
public boolean rollbackOnly() {
Boolean ret = null;
if (particular != null) {
ret = particular.rollbackOnly() ? Boolean.TRUE : Boolean.FALSE;
}
if ((ret == null) && (setting != null)) {
ret = setting.rollbackOnly() ? Boolean.TRUE : Boolean.FALSE;
}
if (ret == null) {
ret = def.rollbackOnly() ? Boolean.TRUE : Boolean.FALSE;
}
return ret.booleanValue();
}
}