TtlExecutorTransformlet.java 8.2 KB
Newer Older
1
package com.alibaba.ttl.threadpool.agent.internal.transformlet.impl;
2

oldratlee's avatar
oldratlee 已提交
3
import com.alibaba.ttl.threadpool.TtlExecutors;
4
import com.alibaba.ttl.threadpool.agent.internal.logging.Logger;
oldratlee's avatar
oldratlee 已提交
5
import com.alibaba.ttl.threadpool.agent.internal.transformlet.ClassInfo;
6
import com.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
7
import edu.umd.cs.findbugs.annotations.NonNull;
oldratlee's avatar
oldratlee 已提交
8
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
9
import javassist.*;
10 11 12

import java.io.IOException;
import java.lang.reflect.Modifier;
13
import java.util.HashMap;
14
import java.util.HashSet;
15
import java.util.Map;
16
import java.util.Set;
oldratlee's avatar
oldratlee 已提交
17
import java.util.concurrent.Callable;
18

19
import static com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.signatureOfMethod;
oldratlee's avatar
oldratlee 已提交
20

21 22 23 24 25 26 27 28 29 30 31 32 33
/**
 * 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 {
34
    private static final Logger logger = Logger.getLogger(TtlExecutorTransformlet.class);
35

oldratlee's avatar
oldratlee 已提交
36
    private static final Set<String> EXECUTOR_CLASS_NAMES = new HashSet<String>();
37
    private static final Map<String, String> PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS = new HashMap<String, String>();
38

oldratlee's avatar
oldratlee 已提交
39 40 41
    private static final String THREAD_POOL_EXECUTOR_CLASS_NAME = "java.util.concurrent.ThreadPoolExecutor";
    private static final String RUNNABLE_CLASS_NAME = "java.lang.Runnable";

42
    static {
oldratlee's avatar
oldratlee 已提交
43
        EXECUTOR_CLASS_NAMES.add(THREAD_POOL_EXECUTOR_CLASS_NAME);
44
        EXECUTOR_CLASS_NAMES.add("java.util.concurrent.ScheduledThreadPoolExecutor");
45

oldratlee's avatar
oldratlee 已提交
46
        PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.put(RUNNABLE_CLASS_NAME, "com.alibaba.ttl.TtlRunnable");
47
        PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.put("java.util.concurrent.Callable", "com.alibaba.ttl.TtlCallable");
48 49
    }

50 51
    private static final String THREAD_FACTORY_CLASS_NAME = "java.util.concurrent.ThreadFactory";

52
    private final boolean disableInheritableForThreadPool;
53

54 55
    public TtlExecutorTransformlet(boolean disableInheritableForThreadPool) {
        this.disableInheritableForThreadPool = disableInheritableForThreadPool;
56 57
    }

58
    @Override
59
    public void doTransform(@NonNull final ClassInfo classInfo) throws IOException, NotFoundException, CannotCompileException {
oldratlee's avatar
oldratlee 已提交
60
        final CtClass clazz = classInfo.getCtClass();
oldratlee's avatar
oldratlee 已提交
61
        if (EXECUTOR_CLASS_NAMES.contains(classInfo.getClassName())) {
oldratlee's avatar
oldratlee 已提交
62
            for (CtMethod method : clazz.getDeclaredMethods()) {
oldratlee's avatar
oldratlee 已提交
63
                updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(method);
oldratlee's avatar
oldratlee 已提交
64
            }
65

66
            if (disableInheritableForThreadPool) updateConstructorDisableInheritable(clazz);
67

oldratlee's avatar
oldratlee 已提交
68
            classInfo.setModified();
oldratlee's avatar
oldratlee 已提交
69 70
        } else {
            if (clazz.isPrimitive() || clazz.isArray() || clazz.isInterface() || clazz.isAnnotation()) {
oldratlee's avatar
oldratlee 已提交
71
                return;
oldratlee's avatar
oldratlee 已提交
72
            }
oldratlee's avatar
oldratlee 已提交
73
            if (!clazz.subclassOf(clazz.getClassPool().get(THREAD_POOL_EXECUTOR_CLASS_NAME))) return;
oldratlee's avatar
oldratlee 已提交
74

oldratlee's avatar
oldratlee 已提交
75
            logger.info("Transforming class " + classInfo.getClassName());
oldratlee's avatar
oldratlee 已提交
76

oldratlee's avatar
oldratlee 已提交
77 78
            final boolean modified = updateBeforeAndAfterExecuteMethodOfExecutorSubclass(clazz);
            if (modified) classInfo.setModified();
79 80 81
        }
    }

oldratlee's avatar
oldratlee 已提交
82 83 84
    /**
     * @see com.alibaba.ttl.TtlRunnable#get(Runnable, boolean, boolean)
     * @see com.alibaba.ttl.TtlCallable#get(Callable, boolean, boolean)
oldratlee's avatar
oldratlee 已提交
85
     * @see com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils#setAutoWrapperAttachment(Object)
oldratlee's avatar
oldratlee 已提交
86
     */
oldratlee's avatar
oldratlee 已提交
87
    @SuppressFBWarnings("VA_FORMAT_STRING_USES_NEWLINE") // [ERROR] Format string should use %n rather than \n
88
    private void updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(@NonNull final CtMethod method) throws NotFoundException, CannotCompileException {
89
        final int modifiers = method.getModifiers();
90
        if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) return;
91 92 93 94

        CtClass[] parameterTypes = method.getParameterTypes();
        StringBuilder insertCode = new StringBuilder();
        for (int i = 0; i < parameterTypes.length; i++) {
95 96
            final String paramTypeName = parameterTypes[i].getName();
            if (PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.containsKey(paramTypeName)) {
oldratlee's avatar
oldratlee 已提交
97 98 99 100
                String code = String.format(
                        // decorate to TTL wrapper,
                        // and then set AutoWrapper attachment/Tag
                        "$%d = %s.get($%d, false, true);"
oldratlee's avatar
oldratlee 已提交
101
                                + "\ncom.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.setAutoWrapperAttachment($%<d);",
oldratlee's avatar
oldratlee 已提交
102
                        i + 1, PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.get(paramTypeName), i + 1);
oldratlee's avatar
oldratlee 已提交
103
                logger.info("insert code before method " + signatureOfMethod(method) + " of class " + method.getDeclaringClass().getName() + ": " + code);
104 105 106
                insertCode.append(code);
            }
        }
107
        if (insertCode.length() > 0) method.insertBefore(insertCode.toString());
108
    }
109

oldratlee's avatar
oldratlee 已提交
110 111 112
    /**
     * @see TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)
     */
113
    private void updateConstructorDisableInheritable(@NonNull final CtClass clazz) throws NotFoundException, CannotCompileException {
114 115 116 117 118 119 120 121 122 123 124 125 126 127
        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 " + signatureOfMethod(constructor) + " of class " + constructor.getDeclaringClass().getName() + ": " + code);
                    insertCode.append(code);
                }
            }
            if (insertCode.length() > 0) constructor.insertBefore(insertCode.toString());
        }
    }
oldratlee's avatar
oldratlee 已提交
128 129 130 131

    /**
     * @see Utils#unwrapIfIsAutoWrapper(Runnable)
     */
132
    private boolean updateBeforeAndAfterExecuteMethodOfExecutorSubclass(@NonNull final CtClass clazz) throws NotFoundException, CannotCompileException {
oldratlee's avatar
oldratlee 已提交
133 134 135
        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");
oldratlee's avatar
oldratlee 已提交
136
        boolean modified = false;
oldratlee's avatar
oldratlee 已提交
137 138 139 140 141 142 143

        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 " + signatureOfMethod(beforeExecute) + " of class " + beforeExecute.getDeclaringClass().getName() + ": " + code);
            beforeExecute.insertBefore(code);
oldratlee's avatar
oldratlee 已提交
144
            modified = true;
oldratlee's avatar
oldratlee 已提交
145 146 147 148 149 150 151 152 153 154
        } 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 " + signatureOfMethod(afterExecute) + " of class " + afterExecute.getDeclaringClass().getName() + ": " + code);
            afterExecute.insertBefore(code);
oldratlee's avatar
oldratlee 已提交
155
            modified = true;
oldratlee's avatar
oldratlee 已提交
156 157 158 159
        } catch (NotFoundException e) {
            // clazz does not override afterExecute method, do nothing.
        }

oldratlee's avatar
oldratlee 已提交
160
        return modified;
oldratlee's avatar
oldratlee 已提交
161
    }
162
}