package com.alibaba.ttl.threadpool.agent;
import com.alibaba.ttl.threadpool.agent.internal.logging.Logger;
import com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlExecutorTransformlet;
import com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlForkJoinTransformlet;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* TTL Java Agent.
*
* NOTE:
* Since {@code v2.6.0}, TTL agent jar will auto add self to {@code boot classpath}.
* But you should NOT modify the downloaded TTL jar file name.
* 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}.
*
* 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:
*
*
* - Boot-Class-Path
* -
* 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.
*
*
*
*
* More info about {@code Boot-Class-Path} see
* The mechanism for instrumentation.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see Instrumentation
* @see The mechanism for instrumentation
* @see JAR File Specification - JAR Manifest
* @see Working with Manifest Files - The Java™ TutorialsHide
* @since 0.9.0
*/
public final class TtlAgent {
private TtlAgent() {
throw new InstantiationError("Must not instantiate this class");
}
/**
* Entrance method of TTL Java Agent.
*
* The log of TTL Java Agent is config by agent argument, using key {@code ttl.agent.logger}.
*
*
* - {@code ttl.agent.logger : STDERR}
* only log to {@code stderr} when error.
* This is default, when no/unrecognized configuration for key {@code ttl.agent.logger}.
* - {@code ttl.agent.logger : STDOUT}
* log to {@code stdout}, more info than {@code ttl.agent.logger : STDERR} is needed when developing.
*
*
* configuration example:
*
* - {@code -javaagent:/path/to/transmittable-thread-local-2.6.0.jar}
* - {@code -javaagent:/path/to/transmittable-thread-local-2.6.0.jar=ttl.agent.logger:STDOUT}
*
*
* @see The mechanism for instrumentation
* @see Logger
* @see Logger#TTL_AGENT_LOGGER_KEY
* @see Logger#STDERR
* @see Logger#STDOUT
*/
public static void premain(String agentArgs, Instrumentation inst) {
Logger.setLoggerImplType(getLogImplTypeFromAgentArgs(agentArgs));
final Logger logger = Logger.getLogger(TtlAgent.class);
try {
logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst);
@SuppressWarnings("unchecked")
ClassFileTransformer transformer = new TtlTransformer(TtlExecutorTransformlet.class, TtlForkJoinTransformlet.class);
inst.addTransformer(transformer, true);
logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success");
logger.info("[TtlAgent.premain] end");
} catch (Exception e) {
String msg = "Fail to load TtlAgent , cause: " + e.toString();
logger.log(Level.SEVERE, msg, e);
throw new IllegalStateException(msg, e);
}
}
private static String getLogImplTypeFromAgentArgs(String agentArgs) {
final Map kv = splitCommaColonStringToKV(agentArgs);
return kv.get(Logger.TTL_AGENT_LOGGER_KEY);
}
/**
* Split to {@code json} like String({@code "k1:v1,k2:v2"}) to KV map({"k1"->"v1", "k2" -> "v2"}).
*/
static Map splitCommaColonStringToKV(String commaColonString) {
Map ret = new HashMap();
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], "");
else {
ret.put(kv[0], kv[1]);
}
}
return ret;
}
}