package com.navercorp.pinpoint.plugin.resin;
import java.security.ProtectionDomain;
import com.navercorp.pinpoint.bootstrap.async.AsyncTraceIdAccessor;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException;
import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod;
import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor;
import com.navercorp.pinpoint.bootstrap.instrument.MethodFilters;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate;
import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplateAware;
import com.navercorp.pinpoint.bootstrap.interceptor.scope.ExecutionPolicy;
import com.navercorp.pinpoint.bootstrap.interceptor.scope.InterceptorScope;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin;
import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext;
/**
*
* @author huangpengjie@fang.com
*
*/
public class ResinPlugin implements ProfilerPlugin, TransformTemplateAware {
private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
private TransformTemplate transformTemplate;
@Override
public void setup(ProfilerPluginSetupContext context) {
final ResinConfig config = new ResinConfig(context.getConfig());
if (logger.isInfoEnabled()) {
logger.info("ResinPlugin config:{}", config);
}
if (!config.isResinEnable()) {
logger.info("ResinPlugin disabled");
return;
}
ResinDetector resinDetector = new ResinDetector(config.getResinBootstrapMains());
context.addApplicationTypeDetector(resinDetector);
logger.info("Adding Resin transformers");
addTransformers(config);
}
private void addTransformers(ResinConfig config) {
addWebApp();
addServletInvocation();
// handle async request
addHttpServletRequestImpl();
addAsyncContextImpl();
addCauchoRequestWrapper();
addErrorPageManager();
}
private void addCauchoRequestWrapper() {
transformTemplate.transform("com.caucho.server.http.CauchoRequestWrapper", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
target.addGetter("com.navercorp.pinpoint.plugin.resin.HttpServletRequestGetter", "_request");
return target.toBytecode();
}
});
}
@Override
public void setTransformTemplate(TransformTemplate transformTemplate) {
this.transformTemplate = transformTemplate;
}
private void addErrorPageManager() {
transformTemplate.transform("com.caucho.server.webapp.ErrorPageManager", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
InstrumentMethod sendServletErrorMethodEditorBuilder = target.getDeclaredMethod("sendServletError", "java.lang.Throwable", "javax.servlet.ServletRequest", "javax.servlet.ServletResponse");
if (sendServletErrorMethodEditorBuilder != null) {
sendServletErrorMethodEditorBuilder.addInterceptor("com.navercorp.pinpoint.plugin.resin.interceptor.ErrorPageManagerInterceptor");
}
return target.toBytecode();
}
});
}
private void addWebApp() {
transformTemplate.transform("com.caucho.server.webapp.WebApp", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
InstrumentMethod initMethodEditorBuilder = target.getDeclaredMethod("init");
if (initMethodEditorBuilder != null) {
initMethodEditorBuilder.addInterceptor("com.navercorp.pinpoint.plugin.resin.interceptor.WebAppInterceptor");
}
return target.toBytecode();
}
});
}
private void addServletInvocation() {
transformTemplate.transform("com.caucho.server.dispatch.ServletInvocation", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
InterceptorScope scope = instrumentor.getInterceptorScope(ResinConstants.RESIN_SERVLET_SCOPE);
// trace request.
InstrumentMethod serviceMethodEditorBuilder = target.getDeclaredMethod("service", "javax.servlet.ServletRequest", "javax.servlet.ServletResponse");
if (serviceMethodEditorBuilder != null) {
serviceMethodEditorBuilder.addScopedInterceptor("com.navercorp.pinpoint.plugin.resin.interceptor.ServletInvocationInterceptor", scope, ExecutionPolicy.BOUNDARY);
}
// resin4 isAsyncSupported
InstrumentMethod isAsyncSupportedMethodEditorBuilder = target.getDeclaredMethod("isAsyncSupported");
if (isAsyncSupportedMethodEditorBuilder != null) {
target.addField(ResinConstants.VERSION_ACCESSOR);
}
return target.toBytecode();
}
});
}
private void addHttpServletRequestImpl() {
transformTemplate.transform("com.caucho.server.http.HttpServletRequestImpl", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
// trace asynchronous process.
InstrumentMethod startAsyncMethodEditor = target.getDeclaredMethod("startAsync", "javax.servlet.ServletRequest", "javax.servlet.ServletResponse");
if (startAsyncMethodEditor != null) {
target.addField(ResinConstants.TRACE_ACCESSOR);
target.addField(ResinConstants.ASYNC_ACCESSOR);
startAsyncMethodEditor.addInterceptor("com.navercorp.pinpoint.plugin.resin.interceptor.HttpServletRequestImplInterceptor");
}
// clear request. 4.x
InstrumentMethod finishRequestInvocationMethodEditor = target.getDeclaredMethod("finishRequest");
if (finishRequestInvocationMethodEditor != null) {
finishRequestInvocationMethodEditor.addInterceptor("com.navercorp.pinpoint.plugin.resin.interceptor.HttpRequestInterceptor");
}
return target.toBytecode();
}
});
}
private void addAsyncContextImpl() {
transformTemplate.transform("com.caucho.server.http.AsyncContextImpl", new TransformCallback() {
@Override
public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException {
InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer);
target.addField(AsyncTraceIdAccessor.class.getName());
for (InstrumentMethod method : target.getDeclaredMethods(MethodFilters.name("dispatch"))) {
method.addInterceptor("com.navercorp.pinpoint.plugin.resin.interceptor.AsyncContextImplDispatchMethodInterceptor");
}
return target.toBytecode();
}
});
}
}