TtlAgent.java 10.2 KB
Newer Older
1
package com.alibaba.ttl.threadpool.agent;
2 3


oldratlee's avatar
oldratlee 已提交
4
import com.alibaba.ttl.threadpool.agent.internal.logging.Logger;
oldratlee's avatar
oldratlee 已提交
5
import com.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
6 7
import com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlExecutorTransformlet;
import com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlForkJoinTransformlet;
oldratlee's avatar
oldratlee 已提交
8
import com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlTimerTaskTransformlet;
9

oldratlee's avatar
oldratlee 已提交
10 11
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
12 13
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
14 15 16 17
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
18
import java.util.logging.Level;
19

20
/**
21
 * TTL Java Agent.
oldratlee's avatar
oldratlee 已提交
22
 * <p>
oldratlee's avatar
oldratlee 已提交
23 24
 * The configuration/arguments for agent see the javadoc of {@link #premain(String, Instrumentation)}
 * <p>
oldratlee's avatar
oldratlee 已提交
25 26
 * <b><i>NOTE:</i></b><br>
 * Since {@code v2.6.0}, TTL agent jar will auto add self to {@code boot classpath}.
oldratlee's avatar
oldratlee 已提交
27
 * But you <b>should <i>NOT</i></b> modify the downloaded TTL jar file name in the maven repo(eg: {@code transmittable-thread-local-2.x.x.jar}).<br>
oldratlee's avatar
oldratlee 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
 * if you modified the downloaded TTL agent jar file name(eg: {@code ttl-foo-name-changed.jar}),
 * you must add TTL agent jar to {@code boot classpath} manually
 * by java option {@code -Xbootclasspath/a:path/to/ttl-foo-name-changed.jar}.
 * <p>
 * The implementation of auto adding self agent jar to {@code boot classpath} use
 * the {@code Boot-Class-Path} property of manifest file({@code META-INF/MANIFEST.MF}) in the TTL Java Agent Jar:
 * <blockquote>
 * <dl>
 * <dt>Boot-Class-Path</dt>
 * <dd>
 * A list of paths to be searched by the bootstrap class loader. Paths represent directories or libraries (commonly referred to as JAR or zip libraries on many platforms).
 * These paths are searched by the bootstrap class loader after the platform specific mechanisms of locating a class have failed. Paths are searched in the order listed.
 * </dd>
 * </dl>
 * </blockquote>
 * <p>
 * More info about {@code Boot-Class-Path} see
oldratlee's avatar
oldratlee 已提交
45
 * <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>.
46
 *
oldratlee's avatar
oldratlee 已提交
47
 * @author Jerry Lee (oldratlee at gmail dot com)
oldratlee's avatar
oldratlee 已提交
48
 * @see Instrumentation
oldratlee's avatar
oldratlee 已提交
49
 * @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
oldratlee's avatar
oldratlee 已提交
50
 * @see <a href="https://docs.oracle.com/javase/10/docs/specs/jar/jar.html#jar-manifest">JAR File Specification - JAR Manifest</a>
oldratlee's avatar
oldratlee 已提交
51
 * @see <a href="https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html">Working with Manifest Files - The Java™ TutorialsHide</a>
oldratlee's avatar
oldratlee 已提交
52
 * @since 0.9.0
53
 */
S
smisger 已提交
54
public final class TtlAgent {
oldratlee's avatar
oldratlee 已提交
55 56
    /**
     * Entrance method of TTL Java Agent.
oldratlee's avatar
oldratlee 已提交
57 58 59 60
     *
     * <h2>TTL Agent configuration</h2>
     * Configure TTL agent via agent arguments, format is {@code k1:v1,k2:v2}. Below is available configuration keys.
     *
61 62 63
     * <h3>Disable inheritable for thread pool</h3>
     * <p>
     * Enable "disable inheritable" for thread pool, config by key {@code ttl.agent.disable.inheritable.for.thread.pool}.
oldratlee's avatar
oldratlee 已提交
64
     * When no configuration for this key, default does <b>not</b> enabled. Since version {@code 2.10.1}.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
     *
     * <ul>
     * <li>rewrite the {@link java.util.concurrent.ThreadFactory} constructor parameter
     * of {@link java.util.concurrent.ThreadPoolExecutor}
     * to {@link com.alibaba.ttl.threadpool.DisableInheritableThreadFactory}
     * by util method {@link com.alibaba.ttl.threadpool.TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)}.
     * </li>
     * <li>rewrite the {@link java.util.concurrent.ForkJoinPool} constructor parameter
     * of {@link java.util.concurrent.ForkJoinPool}
     * to {@link com.alibaba.ttl.threadpool.DisableInheritableForkJoinWorkerThreadFactory}
     * by util method {@link com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDisableInheritableForkJoinWorkerThreadFactory(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory)}.
     * </li>
     * </ul>
     * <p>
     * Configuration example:<br>
     * {@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.disable.inheritable.for.thread.pool:true}
     *
oldratlee's avatar
oldratlee 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
     * <h3>The log configuration</h3>
     * The log of TTL Java Agent is config by key {@code ttl.agent.logger}. Since version {@code 2.6.0}.
     *
     * <ul>
     * <li>{@code ttl.agent.logger : STDERR}<br>
     * only log to {@code stderr} when error.
     * This is <b>default</b>, when no/unrecognized configuration for key {@code ttl.agent.logger}.</li>
     * <li>{@code ttl.agent.logger : STDOUT}<br>
     * Log to {@code stdout}, more info than {@code ttl.agent.logger:STDERR}; This is needed when developing.</li>
     * </ul>
     * <p>
     * configuration example:
     * <ul>
     * <li>{@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar}</li>
     * <li>{@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.logger:STDOUT}</li>
     * </ul>
     *
oldratlee's avatar
oldratlee 已提交
99
     * <h3>Enable TimerTask class decoration</h3>
100
     * Enable TimerTask class decoration is config by key {@code ttl.agent.enable.timer.task}.
oldratlee's avatar
oldratlee 已提交
101
     * When no configuration for this key, default does <b>not</b> enabled. Since version {@code 2.7.0}.
oldratlee's avatar
oldratlee 已提交
102
     * <p>
103 104
     * Configuration example:<br>
     * {@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.enable.timer.task:true}
oldratlee's avatar
oldratlee 已提交
105 106
     *
     * <h3>Multi key configuration example</h3>
107
     * {@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.logger:STDOUT,ttl.agent.disable.inheritable.for.thread.pool:true}
oldratlee's avatar
oldratlee 已提交
108
     *
oldratlee's avatar
oldratlee 已提交
109
     * @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
oldratlee's avatar
oldratlee 已提交
110 111 112 113 114
     * @see Logger
     * @see Logger#TTL_AGENT_LOGGER_KEY
     * @see Logger#STDERR
     * @see Logger#STDOUT
     */
oldratlee's avatar
oldratlee 已提交
115
    public static void premain(String agentArgs, @Nonnull Instrumentation inst) {
116
        kvs = splitCommaColonStringToKV(agentArgs);
oldratlee's avatar
oldratlee 已提交
117 118

        Logger.setLoggerImplType(getLogImplTypeFromAgentArgs(kvs));
119
        final Logger logger = Logger.getLogger(TtlAgent.class);
120

121
        try {
122
            logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst);
123
            final boolean disableInheritable = isDisableInheritableForThreadPool();
124

125 126 127 128
            final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();
            transformletList.add(new TtlExecutorTransformlet(disableInheritable));
            transformletList.add(new TtlForkJoinTransformlet(disableInheritable));
            if (isEnableTimerTask()) transformletList.add(new TtlTimerTaskTransformlet());
oldratlee's avatar
oldratlee 已提交
129 130

            final ClassFileTransformer transformer = new TtlTransformer(transformletList);
131 132
            inst.addTransformer(transformer, true);
            logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success");
133

134
            logger.info("[TtlAgent.premain] end");
oldratlee's avatar
oldratlee 已提交
135 136

            ttlAgentLoaded = true;
137 138
        } catch (Exception e) {
            String msg = "Fail to load TtlAgent , cause: " + e.toString();
139
            logger.log(Level.SEVERE, msg, e);
140 141 142
            throw new IllegalStateException(msg, e);
        }
    }
143

oldratlee's avatar
oldratlee 已提交
144
    private static String getLogImplTypeFromAgentArgs(@Nonnull final Map<String, String> kvs) {
oldratlee's avatar
oldratlee 已提交
145 146 147
        return kvs.get(Logger.TTL_AGENT_LOGGER_KEY);
    }

148 149 150 151 152 153 154 155 156 157 158 159 160
    private static volatile Map<String, String> kvs;

    private static volatile boolean ttlAgentLoaded = false;

    /**
     * Whether TTL agent is loaded.
     *
     * @since 2.9.0
     */
    public static boolean isTtlAgentLoaded() {
        return ttlAgentLoaded;
    }

oldratlee's avatar
oldratlee 已提交
161 162
    private static final String TTL_AGENT_ENABLE_TIMER_TASK_KEY = "ttl.agent.enable.timer.task";

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    private static final String TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL = "ttl.agent.disable.inheritable.for.thread.pool";

    /**
     * Whether disable inheritable for thread pool is enhanced by ttl agent, check {@link #isTtlAgentLoaded()} first.
     *
     * @see com.alibaba.ttl.threadpool.DisableInheritableThreadFactory
     * @see com.alibaba.ttl.threadpool.TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory)
     * @see com.alibaba.ttl.threadpool.TtlExecutors#getDefaultDisableInheritableThreadFactory()
     * @see com.alibaba.ttl.threadpool.DisableInheritableForkJoinWorkerThreadFactory
     * @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDisableInheritableForkJoinWorkerThreadFactory(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory)
     * @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory()
     * @since 2.10.1
     */
    public static boolean isDisableInheritableForThreadPool() {
        return isOptionSet(kvs, TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL);
    }

    /**
     * Whether timer task is enhanced by ttl agent, check {@link #isTtlAgentLoaded()} first.
     *
     * @since 2.10.1
     */
    public static boolean isEnableTimerTask() {
        return isOptionSet(kvs, TTL_AGENT_ENABLE_TIMER_TASK_KEY);
    }

    private static boolean isOptionSet(@Nullable final Map<String, String> kvs, @Nonnull String key) {
        if (null == kvs) return false;

        final boolean hasEnableKey = kvs.containsKey(key);
oldratlee's avatar
oldratlee 已提交
193 194
        if (!hasEnableKey) return false;

195
        return !"false".equalsIgnoreCase(kvs.get(key));
196 197 198
    }

    /**
199
     * Split to {@code json} like String({@code "k1:v1,k2:v2"}) to KV map({@code "k1"->"v1", "k2"->"v2"}).
200
     */
oldratlee's avatar
oldratlee 已提交
201 202
    @Nonnull
    static Map<String, String> splitCommaColonStringToKV(@Nullable String commaColonString) {
203 204 205 206 207 208 209 210 211
        Map<String, String> ret = new HashMap<String, String>();
        if (commaColonString == null || commaColonString.trim().length() == 0) return ret;

        final String[] splitKvArray = commaColonString.trim().split("\\s*,\\s*");
        for (String kvString : splitKvArray) {
            final String[] kv = kvString.trim().split("\\s*:\\s*");
            if (kv.length == 0) continue;

            if (kv.length == 1) ret.put(kv[0], "");
212
            else ret.put(kv[0], kv[1]);
213 214 215 216
        }

        return ret;
    }
oldratlee's avatar
oldratlee 已提交
217 218 219 220

    private TtlAgent() {
        throw new InstantiationError("Must not instantiate this class");
    }
221
}