Não é possível instrumentar o httpclient do apache usando javaagent para o aplicativo uber jar de inicialização por primavera
Estou tentando escrever um javaagent com o Bytebuddy para interceptar solicitações http do cliente apache e quero usar esse agente para o aplicativo de inicialização por primavera. O agente funciona bem quando inicio meu aplicativo de inicialização de teste da Idea (execute o método principal diretamente). No entanto, quando empacoto o aplicativo em um jar uber de inicialização por mola e o executo usandojava -javaagent:myagent.jar -jar myapplication.jar
, lança a seguinte exceção.
onError:org.apache.http.impl.client.AbstractHttpClient
java.lang.NoClassDefFoundError: org/apache/http/HttpHost
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.getDeclaredMethods(Class.java:1975)
at net.bytebuddy.description.method.MethodList$ForLoadedType.<init>(MethodList.java:106)
at net.bytebuddy.description.type.TypeDescription$ForLoadedType.getDeclaredMethods(TypeDescription.java:985)
at net.bytebuddy.implementation.MethodDelegation$MethodContainer$ForExplicitMethods.ofStatic(MethodDelegation.java:1037)
at net.bytebuddy.implementation.MethodDelegation.to(MethodDelegation.java:247)
at net.bytebuddy.implementation.MethodDelegation.to(MethodDelegation.java:226)
at com.yiji.dtrace.agent.httpclient4.interceptor.HttpClient4Interceptors$1.transform(HttpClient4Interceptors.java:48)
at net.bytebuddy.agent.builder.AgentBuilder$Transformer$Compound.transform(AgentBuilder.java:457)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:2791)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:3081)
at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at org.springframework.boot.loader.LaunchedURLClassLoader.doLoadClass(LaunchedURLClassLoader.java:170)
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:142)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at org.springframework.boot.loader.LaunchedURLClassLoader.doLoadClass(LaunchedURLClassLoader.java:170)
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:142)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at org.springframework.boot.loader.LaunchedURLClassLoader.doLoadClass(LaunchedURLClassLoader.java:170)
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:142)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.yjf.common.net.HttpUtil.<init>(HttpUtil.java:118)
at com.yjf.common.net.HttpUtil.<init>(HttpUtil.java:81)
at com.yjf.common.net.HttpUtil.<clinit>(HttpUtil.java:78)
at com.daidai.dtrace.agent.test.Main.main(Main.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:53)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: org.apache.http.HttpHost
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 60 more
e aqui está o código relacionado ao meu agente.
public class DTraceAgent {
public static TypeDescription abstractHttpClientDescription() {
return new TypeDescription.Latent("org.apache.http.impl.client.AbstractHttpClient",
Modifier.PUBLIC|Modifier.ABSTRACT,
TypeDescription.OBJECT,
Arrays.asList(httpClientDescription()));
}
public static TypeDescription httpHostDescription() {
return new TypeDescription.Latent("org.apache.http.HttpHost",
Modifier.PUBLIC|Modifier.FINAL,
TypeDescription.OBJECT,
Arrays.asList(new TypeDescription.ForLoadedType(Cloneable.class),
new TypeDescription.ForLoadedType(Serializable.class)));
}
public static TypeDescription httpContextDescription() {
return new TypeDescription.Latent("org.apache.http.protocol.HttpContext",
getInterfaceModifiers(),
TypeDescription.OBJECT,
null);
}
public static TypeDescription httpRequestDescription() {
return new TypeDescription.Latent("org.apache.http.HttpRequest",
getInterfaceModifiers(),
httpMessageDescription(),
null);
}
public static void premain(String arguments, Instrumentation instrumentation) {
new AgentBuilder.Default()
//.withBinaryLocator(binaryLocatorFor(instrumentation))
.withListener(DebugListener.getListener())
.type(is(abstractHttpClientDescription()))
.transform(new AgentBuilder.Transformer() {
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription) {
return builder.method(named("execute")
.and(takesArguments(httpHostDescription(), httpRequestDescription(), httpContextDescription()))
.and(returns(named("org.apache.http.HttpResponse"))))
.intercept(MethodDelegation.to(HttpClientInterceptor4dot3Plus.class));
}
}).installOn(instrumentation);
}
}
public class HttpClientInterceptor4dot3Plus {
public static CloseableHttpResponse doExecute(
@SuperCall Callable<CloseableHttpResponse> client, @Argument(1)HttpRequest request
) throws Exception {
StringBuilder builder = new StringBuilder(1024);
if (request != null && request.getRequestLine() != null) {
RequestLine requestLine = request.getRequestLine();
builder.append(requestLine.getMethod()).append(" ").append(requestLine.getUri());
}
try (TraceScope scope = Trace.startSpanForEntry(builder.toString())) {
Trace.spanType(Span.SPAN_TYPE_HTTP);
try {
return client.call();
} catch (Exception e) {
Trace.except,ion(e);
throw e;
}
}
}
}
public class DebugListener {
public static AgentBuilder.Listener getListener() {
return new AgentBuilder.Listener() {
@Override
public void onTransformation(TypeDescription typeDescription, DynamicType dynamicType) {
System.err.println("onTransformation:" + typeDescription.getCanonicalName());
try {
dynamicType.saveIn(new File("generated_classes"));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onIgnored(TypeDescription typeDescription) {
//System.err.println("onIgored:" + typeDescription.getCanonicalName());
}
@Override
public void onError(String typeName, Throwable throwable) {
System.err.println("onError:" + typeName);
throwable.printStackTrace();
}
@Override
public void onComplete(String typeName) {
//System.err.println("onComplete:" + typeName);
}
};
}
}
Eu acho que esse problema é causado pela maneira como o boot do uber boot de mola inicia uma aplicação. A inicialização do Spring fornece um carregador de classes dedicado chamado LaunchedURLClassLoader para carregar classes relacionadas a aplicativos do uber jar, enquanto o javaagent jar é carregado pelo carregador de classes padrão do sistema (se meu entendimento estiver correto). Portanto, a apache httpclient lib (incluída no uber jar) não fica visível para o carregador de classe do sistema.
Tentei fornecer um BinaryLocator ao AgentBuilder, mas não funcionou. Talvez o BinaryLocator não tenha sido construído corretamente. De qualquer forma, um BinaryLocator adequado talvez seja uma solução possível.
Muito obrigado por quaisquer soluções ou sugestões.
Outras informações podem ser úteis:
versão 1.3.1.RELEASE
byte-buddy 0.7.7, empacotado no agente usando o descritor jar-with-dependencies do maven-assembly-plugin
apache httpclient 4.3.2