package org.skywalking.apm.agent.core.plugin.interceptor.enhance;
import net.bytebuddy.implementation.bind.annotation.*;
import org.skywalking.apm.agent.core.plugin.interceptor.loader.InterceptorInstanceLoader;
import org.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext;
import org.skywalking.apm.logging.ILog;
import org.skywalking.apm.logging.LogManager;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* The actual byte-buddy's interceptor to intercept class instance methods.
* In this class, it provide a bridge between byte-buddy and sky-walking plugin.
*
* @author wusheng
*/
public class ClassInstanceMethodsInterceptor {
private static final ILog logger = LogManager.getLogger(ClassInstanceMethodsInterceptor.class);
/**
* A class full name, and instanceof {@link InstanceMethodsAroundInterceptor}
* This name should only stay in {@link String}, the real {@link Class} type will trigger classloader failure.
* If you want to know more, please check on books about Classloader or Classloader appointment mechanism.
*/
private String instanceMethodsAroundInterceptorClassName;
/**
* Set the name of {@link ClassInstanceMethodsInterceptor#instanceMethodsAroundInterceptorClassName}
*
* @param instanceMethodsAroundInterceptorClassName class full name.
*/
public ClassInstanceMethodsInterceptor(String instanceMethodsAroundInterceptorClassName) {
this.instanceMethodsAroundInterceptorClassName = instanceMethodsAroundInterceptorClassName;
}
/**
* Intercept the target instance method.
*
* @param obj target class instance.
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @param instanceContext the added field of enhanced class.
* @return the return value of target instance method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Origin Method method,
@SuperCall Callable<?> zuper,
@FieldValue(ClassEnhancePluginDefine.CONTEXT_ATTR_NAME) EnhancedClassInstanceContext instanceContext) throws Throwable {
InstanceMethodsAroundInterceptor interceptor = InterceptorInstanceLoader
.load(instanceMethodsAroundInterceptorClassName, obj.getClass().getClassLoader());
InstanceMethodInvokeContext interceptorContext = new InstanceMethodInvokeContext(obj, method.getName(), allArguments, method.getParameterTypes());
MethodInterceptResult result = new MethodInterceptResult();
try {
interceptor.beforeMethod(instanceContext, interceptorContext, result);
} catch (Throwable t) {
logger.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
}
Object ret = null;
try {
if (!result.isContinue()) {
ret = result._ret();
} else {
ret = zuper.call();
}
} catch (Throwable t) {
try {
interceptor.handleMethodException(t, instanceContext, interceptorContext);
} catch (Throwable t2) {
logger.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
}
throw t;
} finally {
try {
ret = interceptor.afterMethod(instanceContext, interceptorContext, ret);
} catch (Throwable t) {
logger.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
}
}
return ret;
}
}