You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
8.2 KiB
164 lines
8.2 KiB
package com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.impl; |
|
|
|
import com.fr.third.alibaba.ttl.threadpool.TtlExecutors; |
|
import com.fr.third.alibaba.ttl.threadpool.agent.internal.logging.Logger; |
|
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.ClassInfo; |
|
import com.fr.third.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet; |
|
import com.fr.third.alibaba.ttl.TtlCallable; |
|
import com.fr.third.alibaba.ttl.TtlRunnable; |
|
import org.jetbrains.annotations.NotNull; |
|
import com.fr.third.javassist.*; |
|
|
|
import java.io.IOException; |
|
import java.lang.reflect.Modifier; |
|
import java.util.HashMap; |
|
import java.util.HashSet; |
|
import java.util.Map; |
|
import java.util.Set; |
|
import java.util.concurrent.Callable; |
|
|
|
/** |
|
* TTL {@link JavassistTransformlet} for {@link java.util.concurrent.Executor}. |
|
* |
|
* @author Jerry Lee (oldratlee at gmail dot com) |
|
* @author wuwen5 (wuwen.55 at aliyun dot com) |
|
* @see java.util.concurrent.Executor |
|
* @see java.util.concurrent.ExecutorService |
|
* @see java.util.concurrent.ThreadPoolExecutor |
|
* @see java.util.concurrent.ScheduledThreadPoolExecutor |
|
* @see java.util.concurrent.Executors |
|
* @since 2.5.1 |
|
*/ |
|
public class TtlExecutorTransformlet implements JavassistTransformlet { |
|
private static final Logger logger = Logger.getLogger(TtlExecutorTransformlet.class); |
|
|
|
private static Set<String> EXECUTOR_CLASS_NAMES = new HashSet<String>(); |
|
private static final Map<String, String> PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS = new HashMap<String, String>(); |
|
|
|
private static final String THREAD_POOL_EXECUTOR_CLASS_NAME = "java.util.concurrent.ThreadPoolExecutor"; |
|
private static final String RUNNABLE_CLASS_NAME = "java.lang.Runnable"; |
|
|
|
static { |
|
EXECUTOR_CLASS_NAMES.add(THREAD_POOL_EXECUTOR_CLASS_NAME); |
|
EXECUTOR_CLASS_NAMES.add("java.util.concurrent.ScheduledThreadPoolExecutor"); |
|
|
|
PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.put(RUNNABLE_CLASS_NAME, "com.alibaba.ttl.TtlRunnable"); |
|
PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.put("java.util.concurrent.Callable", "com.alibaba.ttl.TtlCallable"); |
|
} |
|
|
|
private static final String THREAD_FACTORY_CLASS_NAME = "java.util.concurrent.ThreadFactory"; |
|
|
|
private final boolean disableInheritableForThreadPool; |
|
|
|
public TtlExecutorTransformlet(boolean disableInheritableForThreadPool) { |
|
this.disableInheritableForThreadPool = disableInheritableForThreadPool; |
|
} |
|
|
|
@Override |
|
public void doTransform(@NotNull final ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException { |
|
if (EXECUTOR_CLASS_NAMES.contains(classInfo.getClassName())) { |
|
final CtClass clazz = classInfo.getCtClass(); |
|
|
|
for (CtMethod method : clazz.getDeclaredMethods()) { |
|
updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(method); |
|
} |
|
|
|
if (disableInheritableForThreadPool) updateConstructorDisableInheritable(clazz); |
|
|
|
classInfo.setModified(); |
|
} else { |
|
final CtClass clazz = classInfo.getCtClass(); |
|
|
|
if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || clazz.isAnnotation()) { |
|
return; |
|
} |
|
if (!clazz.subclassOf(clazz.getClassPool().get(THREAD_POOL_EXECUTOR_CLASS_NAME))) return; |
|
|
|
logger.info("Transforming class " + classInfo.getClassName()); |
|
|
|
final boolean modified = updateBeforeAndAfterExecuteMethodOfExecutorSubclass(clazz); |
|
if (modified) classInfo.setModified(); |
|
} |
|
} |
|
|
|
/** |
|
* @see TtlRunnable#get(Runnable, boolean, boolean) |
|
* @see TtlCallable#get(Callable, boolean, boolean) |
|
* @see Utils#setAutoWrapperAttachment(Object) |
|
*/ |
|
// [ERROR] Format string should use %n rather than \n |
|
private void updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(@NotNull final CtMethod method) throws NotFoundException, CannotCompileException { |
|
final int modifiers = method.getModifiers(); |
|
if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) return; |
|
|
|
CtClass[] parameterTypes = method.getParameterTypes(); |
|
StringBuilder insertCode = new StringBuilder(); |
|
for (int i = 0; i < parameterTypes.length; i++) { |
|
final String paramTypeName = parameterTypes[i].getName(); |
|
if (PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.containsKey(paramTypeName)) { |
|
String code = String.format( |
|
// decorate to TTL wrapper, |
|
// and then set AutoWrapper attachment/Tag |
|
"$%d = %s.get($%d, false, true);" |
|
+ "\ncom.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.setAutoWrapperAttachment($%<d);", |
|
i + 1, PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.get(paramTypeName), i + 1); |
|
logger.info("insert code before method " + Utils.signatureOfMethod(method) + " of class " + method.getDeclaringClass().getName() + ": " + code); |
|
insertCode.append(code); |
|
} |
|
} |
|
if (insertCode.length() > 0) method.insertBefore(insertCode.toString()); |
|
} |
|
|
|
/** |
|
* @see TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory) |
|
*/ |
|
private void updateConstructorDisableInheritable(@NotNull final CtClass clazz) throws NotFoundException, CannotCompileException { |
|
for (CtConstructor constructor : clazz.getDeclaredConstructors()) { |
|
final CtClass[] parameterTypes = constructor.getParameterTypes(); |
|
final StringBuilder insertCode = new StringBuilder(); |
|
for (int i = 0; i < parameterTypes.length; i++) { |
|
final String paramTypeName = parameterTypes[i].getName(); |
|
if (THREAD_FACTORY_CLASS_NAME.equals(paramTypeName)) { |
|
String code = String.format("$%d = com.alibaba.ttl.threadpool.TtlExecutors.getDisableInheritableThreadFactory($%<d);", i + 1); |
|
logger.info("insert code before method " + Utils.signatureOfMethod(constructor) + " of class " + constructor.getDeclaringClass().getName() + ": " + code); |
|
insertCode.append(code); |
|
} |
|
} |
|
if (insertCode.length() > 0) constructor.insertBefore(insertCode.toString()); |
|
} |
|
} |
|
|
|
/** |
|
* @see Utils#unwrapIfIsAutoWrapper(Runnable) |
|
*/ |
|
private boolean updateBeforeAndAfterExecuteMethodOfExecutorSubclass(@NotNull final CtClass clazz) throws NotFoundException, CannotCompileException { |
|
final CtClass runnableClass = clazz.getClassPool().get(RUNNABLE_CLASS_NAME); |
|
final CtClass threadClass = clazz.getClassPool().get("java.lang.Thread"); |
|
final CtClass throwableClass = clazz.getClassPool().get("java.lang.Throwable"); |
|
boolean modified = false; |
|
|
|
try { |
|
final CtMethod beforeExecute = clazz.getDeclaredMethod("beforeExecute", new CtClass[]{threadClass, runnableClass}); |
|
// unwrap runnable if IsAutoWrapper |
|
String code = "$2 = com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.unwrapIfIsAutoWrapper($2);"; |
|
logger.info("insert code before method " + Utils.signatureOfMethod(beforeExecute) + " of class " + beforeExecute.getDeclaringClass().getName() + ": " + code); |
|
beforeExecute.insertBefore(code); |
|
modified = true; |
|
} catch (NotFoundException e) { |
|
// clazz does not override beforeExecute method, do nothing. |
|
} |
|
|
|
try { |
|
final CtMethod afterExecute = clazz.getDeclaredMethod("afterExecute", new CtClass[]{runnableClass, throwableClass}); |
|
// unwrap runnable if IsAutoWrapper |
|
String code = "$1 = com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.unwrapIfIsAutoWrapper($1);"; |
|
logger.info("insert code before method " + Utils.signatureOfMethod(afterExecute) + " of class " + afterExecute.getDeclaringClass().getName() + ": " + code); |
|
afterExecute.insertBefore(code); |
|
modified = true; |
|
} catch (NotFoundException e) { |
|
// clazz does not override afterExecute method, do nothing. |
|
} |
|
|
|
return modified; |
|
} |
|
}
|
|
|