/*
* Copyright (c) 2009-2016, b3log.org & hacpai.com
*
* 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.b3log.latke.ioc.bean;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import org.b3log.latke.intercept.annotation.AfterMethod;
import org.b3log.latke.intercept.annotation.BeforeMethod;
import org.b3log.latke.ioc.LatkeBeanManager;
import org.b3log.latke.logging.Level;
import org.b3log.latke.logging.Logger;
import org.b3log.latke.repository.Transaction;
import org.b3log.latke.repository.annotation.Transactional;
import org.b3log.latke.repository.impl.UserRepository;
/**
* Javassist method handler.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.1.3, Mar 26, 2015
*/
public final class JavassistMethodHandler implements MethodHandler {
/**
* Logger.
*/
private static final Logger LOGGER = Logger.getLogger(JavassistMethodHandler.class.getName());
/**
* Bean manager.
*/
private LatkeBeanManager beanManager;
/**
* Method filter.
*/
private MethodFilter methodFilter = new MethodFilter() {
@Override
public boolean isHandled(final Method method) {
final String name = method.getName();
return !"beginTransaction".equals(name) && !"hasTransactionBegun".equals(name);
}
};
/**
* Constructs a method handler with the specified bean manager.
*
* @param beanManager the specified bean manager
*/
public JavassistMethodHandler(final LatkeBeanManager beanManager) {
this.beanManager = beanManager;
}
@Override
public Object invoke(final Object proxy, final Method method, final Method proceed, final Object[] params) throws Throwable {
LOGGER.trace("Processing invocation: " + method.toString());
final Class<?> declaringClass = method.getDeclaringClass();
final String invokingMehtodName = declaringClass.getName() + '#' + method.getName();
// 1. @BeforeMethod handle
handleInterceptor(invokingMehtodName, params, BeforeMethod.class);
// 2. Invocation with transaction handle
final UserRepository userRepository = beanManager.getReference(UserRepository.class);
final boolean withTransactionalAnno = method.isAnnotationPresent(Transactional.class);
final boolean alreadyInTransaction = userRepository.hasTransactionBegun();
final boolean needHandleTrans = withTransactionalAnno && !alreadyInTransaction;
// Transaction Propagation: REQUIRED (Support a current transaction, create a new one if none exists)
Transaction transaction = null;
if (needHandleTrans) {
transaction = userRepository.beginTransaction();
}
Object ret = null;
try {
ret = proceed.invoke(proxy, params);
if (needHandleTrans) {
transaction.commit();
}
} catch (final InvocationTargetException e) {
//final String errMsg = "Invoke [" + method.toString() + "] failed";
//LOGGER.log(Level.WARN, errMsg);
if (needHandleTrans) {
if (null != transaction && transaction.isActive()) {
transaction.rollback();
}
}
throw e.getTargetException();
}
// 3. @AfterMethod handle
handleInterceptor(invokingMehtodName, params, AfterMethod.class);
return ret;
}
/**
* Interceptor handle with the specified invoking method name, invoking method parameters and intercept annotation
* class.
*
* @param invokingMehtodName the specified invoking method name
* @param params the specified invoking method parameters
* @param interceptAnnClass the specified intercept annotation class
*/
private void handleInterceptor(final String invokingMehtodName, final Object[] params,
final Class<? extends Annotation> interceptAnnClass) {
final Set<Interceptor> interceptors = InterceptorHolder.getInterceptors(invokingMehtodName, interceptAnnClass);
for (final Interceptor interceptor : interceptors) {
final Method interceptMethod = interceptor.getInterceptMethod();
final Class<?> interceptMethodClass = interceptMethod.getDeclaringClass();
try {
final Object reference = beanManager.getReference(interceptMethodClass);
interceptMethod.invoke(reference, params);
} catch (final Exception e) {
final String errMsg = "Interception[" + interceptor.toString() + "] execute failed";
LOGGER.log(Level.ERROR, errMsg, e);
throw new RuntimeException(errMsg);
}
}
}
/**
* Gets the method filter.
*
* @return method filter
*/
public MethodFilter getMethodFilter() {
return methodFilter;
}
}