/*
* Copyright 2014 NAVER Corp.
*
* 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 com.navercorp.pinpoint.profiler.instrument.interceptor;
import com.navercorp.pinpoint.bootstrap.interceptor.*;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.IgnoreMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Woonduk Kang(emeroad)
*/
public class InterceptorDefinitionFactory {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final List<TypeHandler> detectHandlers;
public InterceptorDefinitionFactory() {
this.detectHandlers = register();
}
public InterceptorDefinition createInterceptorDefinition(Class<?> interceptorClazz) {
if (interceptorClazz == null) {
throw new NullPointerException("targetInterceptorClazz must not be null");
}
for (TypeHandler typeHandler : detectHandlers) {
final InterceptorDefinition interceptorDefinition = typeHandler.resolveType(interceptorClazz);
if (interceptorDefinition != null) {
return interceptorDefinition;
}
}
throw new RuntimeException("unsupported Interceptor Type. " + interceptorClazz.getName());
}
private List<TypeHandler> register() {
final List<TypeHandler> typeHandlerList = new ArrayList<TypeHandler>();
addTypeHandler(typeHandlerList, AroundInterceptor.class, InterceptorType.ARRAY_ARGS);
addTypeHandler(typeHandlerList, AroundInterceptor0.class, InterceptorType.BASIC);
addTypeHandler(typeHandlerList, AroundInterceptor1.class, InterceptorType.BASIC);
addTypeHandler(typeHandlerList, AroundInterceptor2.class, InterceptorType.BASIC);
addTypeHandler(typeHandlerList, AroundInterceptor3.class, InterceptorType.BASIC);
addTypeHandler(typeHandlerList, AroundInterceptor4.class, InterceptorType.BASIC);
addTypeHandler(typeHandlerList, AroundInterceptor5.class, InterceptorType.BASIC);
addTypeHandler(typeHandlerList, StaticAroundInterceptor.class, InterceptorType.STATIC);
addTypeHandler(typeHandlerList, ApiIdAwareAroundInterceptor.class, InterceptorType.API_ID_AWARE);
return typeHandlerList;
}
private void addTypeHandler(List<TypeHandler> typeHandlerList, Class<? extends Interceptor> interceptorClazz, InterceptorType arrayArgs) {
final TypeHandler typeHandler = createInterceptorTypeHandler(interceptorClazz, arrayArgs);
typeHandlerList.add(typeHandler);
}
private TypeHandler createInterceptorTypeHandler(Class<? extends Interceptor> interceptorClazz, InterceptorType interceptorType) {
if (interceptorClazz == null) {
throw new NullPointerException("targetInterceptorClazz must not be null");
}
if (interceptorType == null) {
throw new NullPointerException("interceptorType must not be null");
}
final Method[] declaredMethods = interceptorClazz.getDeclaredMethods();
if (declaredMethods.length != 2) {
throw new RuntimeException("invalid Type");
}
final String before = "before";
final Method beforeMethod = findMethodByName(declaredMethods, before);
final Class<?>[] beforeParamList = beforeMethod.getParameterTypes();
final String after = "after";
final Method afterMethod = findMethodByName(declaredMethods, after);
final Class<?>[] afterParamList = afterMethod.getParameterTypes();
return new TypeHandler(interceptorClazz, interceptorType, before, beforeParamList, after, afterParamList);
}
private Method findMethodByName(Method[] declaredMethods, String methodName) {
Method findMethod = null;
int count = 0;
for (Method method : declaredMethods) {
if (method.getName().equals(methodName)) {
count++;
findMethod = method;
}
}
if (findMethod == null) {
throw new RuntimeException(methodName + " not found");
}
if (count > 1 ) {
throw new RuntimeException("duplicated method exist. methodName:" + methodName);
}
return findMethod;
}
private class TypeHandler {
private final Class<? extends Interceptor> interceptorClazz;
private final InterceptorType interceptorType;
private final String before;
private final Class<?>[] beforeParamList;
private final String after;
private final Class<?>[] afterParamList;
public TypeHandler(Class<? extends Interceptor> interceptorClazz, InterceptorType interceptorType, String before, final Class<?>[] beforeParamList, final String after, final Class<?>[] afterParamList) {
if (interceptorClazz == null) {
throw new NullPointerException("targetInterceptorClazz must not be null");
}
if (interceptorType == null) {
throw new NullPointerException("interceptorType must not be null");
}
if (before == null) {
throw new NullPointerException("before must not be null");
}
if (beforeParamList == null) {
throw new NullPointerException("beforeParamList must not be null");
}
if (after == null) {
throw new NullPointerException("after must not be null");
}
if (afterParamList == null) {
throw new NullPointerException("afterParamList must not be null");
}
this.interceptorClazz = interceptorClazz;
this.interceptorType = interceptorType;
this.before = before;
this.beforeParamList = beforeParamList;
this.after = after;
this.afterParamList = afterParamList;
}
public InterceptorDefinition resolveType(Class<?> targetClazz) {
if(!this.interceptorClazz.isAssignableFrom(targetClazz)) {
return null;
}
@SuppressWarnings("unchecked")
final Class<? extends Interceptor> casting = (Class<? extends Interceptor>) targetClazz;
return createInterceptorDefinition(casting);
}
private InterceptorDefinition createInterceptorDefinition(Class<? extends Interceptor> targetInterceptorClazz) {
final Method beforeMethod = searchMethod(targetInterceptorClazz, before, beforeParamList);
if (beforeMethod == null) {
throw new RuntimeException(before + " method not found. " + Arrays.toString(beforeParamList));
}
final boolean beforeIgnoreMethod = beforeMethod.isAnnotationPresent(IgnoreMethod.class);
final Method afterMethod = searchMethod(targetInterceptorClazz, after, afterParamList);
if (afterMethod == null) {
throw new RuntimeException(after + " method not found. " + Arrays.toString(afterParamList));
}
final boolean afterIgnoreMethod = afterMethod.isAnnotationPresent(IgnoreMethod.class);
if (beforeIgnoreMethod == true && afterIgnoreMethod == true) {
return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.NON, null, null);
}
if (beforeIgnoreMethod == true) {
return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.AFTER, null, afterMethod);
}
if (afterIgnoreMethod == true) {
return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.BEFORE, beforeMethod, null);
}
return new DefaultInterceptorDefinition(interceptorClazz, targetInterceptorClazz, interceptorType, CaptureType.AROUND, beforeMethod, afterMethod);
}
private Method searchMethod(Class<?> interceptorClazz, String searchMethodName, Class<?>[] searchMethodParameter) {
if (searchMethodName == null) {
throw new NullPointerException("methodList must not be null");
}
// only DeclaredMethod search ?
// try {
// return targetInterceptorClazz.getDeclaredMethod(searchMethodName, searchMethodParameter);
// } catch (NoSuchMethodException ex) {
// logger.debug(searchMethodName + " DeclaredMethod not found. search parent class");
// }
// search all class
try {
return interceptorClazz.getMethod(searchMethodName, searchMethodParameter);
} catch (NoSuchMethodException ex) {
logger.debug(searchMethodName +" DeclaredMethod not found.");
}
return null;
}
}
}