/*
* Copyright 2014 NAVER Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.navercorp.pinpoint.profiler.instrument;
import java.lang.instrument.Instrumentation;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.jar.JarFile;
import com.google.inject.Provider;
import com.navercorp.pinpoint.bootstrap.instrument.*;
import com.navercorp.pinpoint.profiler.metadata.ApiMetaDataService;
import com.navercorp.pinpoint.profiler.objectfactory.ObjectBinderFactory;
import com.navercorp.pinpoint.profiler.plugin.PluginConfig;
import com.navercorp.pinpoint.profiler.plugin.PluginInstrumentContext;
import com.navercorp.pinpoint.profiler.util.JavaAssistUtils;
import javassist.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.navercorp.pinpoint.exception.PinpointException;
import com.navercorp.pinpoint.profiler.instrument.classpool.IsolateMultipleClassPool;
import com.navercorp.pinpoint.profiler.instrument.classpool.MultipleClassPool;
import com.navercorp.pinpoint.profiler.instrument.classpool.NamedClassPool;
import com.navercorp.pinpoint.profiler.interceptor.registry.InterceptorRegistryBinder;
/**
* @author emeroad
*/
@Deprecated
public class JavassistEngine implements InstrumentEngine {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final boolean isInfo = logger.isInfoEnabled();
private final boolean isDebug = logger.isDebugEnabled();
private final Instrumentation instrumentation;
private final ObjectBinderFactory objectBinderFactory;
private final Provider<ApiMetaDataService> apiMetaDataService;
private final MultipleClassPool childClassPool;
private final InterceptorRegistryBinder interceptorRegistryBinder;
private final IsolateMultipleClassPool.EventListener classPoolEventListener = new IsolateMultipleClassPool.EventListener() {
@Override
public void onCreateClassPool(ClassLoader classLoader, NamedClassPool classPool) {
dumpClassLoaderLibList(classLoader, classPool);
}
private void dumpClassLoaderLibList(ClassLoader classLoader, NamedClassPool classPool) {
if (isInfo) {
if (classLoader instanceof URLClassLoader) {
final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
final URL[] urlList = urlClassLoader.getURLs();
if (urlList != null) {
final String classLoaderName = classLoader.getClass().getName();
final String classPoolName = classPool.getName();
logger.info("classLoader lib cl:{} classPool:{}", classLoaderName, classPoolName);
for (URL tempURL : urlList) {
String filePath = tempURL.getFile();
logger.info("lib:{} ", filePath);
}
}
}
}
}
};
public JavassistEngine(Instrumentation instrumentation, ObjectBinderFactory objectBinderFactory, InterceptorRegistryBinder interceptorRegistryBinder, Provider<ApiMetaDataService> apiMetaDataService, final List<String> bootStrapJars) {
if (instrumentation == null) {
throw new NullPointerException("instrumentation must not be null");
}
if (objectBinderFactory == null) {
throw new NullPointerException("objectBinderFactory must not be null");
}
if (interceptorRegistryBinder == null) {
throw new NullPointerException("interceptorRegistryBinder must not be null");
}
if (apiMetaDataService == null) {
throw new NullPointerException("apiMetaDataService must not be null");
}
this.instrumentation = instrumentation;
this.objectBinderFactory = objectBinderFactory;
this.apiMetaDataService = apiMetaDataService;
this.childClassPool = new IsolateMultipleClassPool(classPoolEventListener, new IsolateMultipleClassPool.ClassPoolHandler() {
@Override
public void handleClassPool(NamedClassPool systemClassPool) {
try {
if (bootStrapJars != null) {
// append bootstarp jars
for (String bootStrapJar : bootStrapJars) {
systemClassPool.appendClassPath(bootStrapJar);
}
}
} catch (NotFoundException ex) {
throw new PinpointException("bootStrapJar not found. Caused by:" + ex.getMessage(), ex);
}
// append pinpoint classLoader
systemClassPool.appendClassPath(new ClassClassPath(this.getClass()));
}
});
this.interceptorRegistryBinder = interceptorRegistryBinder;
}
@Override
public InstrumentClass getClass(InstrumentContext instrumentContext, ClassLoader classLoader, String jvmInternalClassName, byte[] classFileBuffer) throws NotFoundInstrumentException {
if (jvmInternalClassName == null) {
throw new NullPointerException("jvmInternalClassName must not be null");
}
if (isDebug) {
logger.debug("Get javassist class {}", jvmInternalClassName);
}
final CtClass cc = getCtClass(instrumentContext, classLoader, jvmInternalClassName, classFileBuffer);
final ApiMetaDataService apiMetaDataService = this.apiMetaDataService.get();
return new JavassistClass(objectBinderFactory, instrumentContext, interceptorRegistryBinder, apiMetaDataService, classLoader, cc);
}
private CtClass getCtClass(InstrumentContext instrumentContext, ClassLoader classLoader, String className, byte[] classfileBuffer) throws NotFoundInstrumentException {
final NamedClassPool classPool = getClassPool(classLoader);
try {
if (classfileBuffer == null) {
// compatibility code
logger.info("classFileBuffer is null className:{}", className);
return classPool.get(className);
} else {
final ClassPool contextCassPool = getContextClassPool(instrumentContext, classPool, className, classfileBuffer);
return contextCassPool.get(className);
}
} catch (NotFoundException e) {
throw new NotFoundInstrumentException(className + " class not found. Cause:" + e.getMessage(), e);
}
}
private ClassPool getContextClassPool(InstrumentContext instrumentContext, NamedClassPool parent, String jvmInternalClassName, byte[] classfileBuffer) throws NotFoundException {
final ClassPool contextCassPool = new ClassPool(parent);
contextCassPool.childFirstLookup = true;
final String javaName = JavaAssistUtils.jvmNameToJavaName(jvmInternalClassName);
if (isDebug) {
logger.debug("getContextClassPool() className={}", javaName);
}
final ClassPath byteArrayClassPath = new ByteArrayClassPath(javaName, classfileBuffer);
contextCassPool.insertClassPath(byteArrayClassPath);
// append plugin jar for jboss
// plugin class not found in jboss classLoader
if (instrumentContext instanceof PluginInstrumentContext) {
final PluginConfig pluginConfig = ((PluginInstrumentContext) instrumentContext).getPluginConfig();
if (pluginConfig != null) {
String jarPath = pluginConfig.getPluginJar().getPath();
contextCassPool.appendClassPath(jarPath);
}
}
return contextCassPool;
}
public CtClass getClass(ClassLoader classLoader, String jvmInternalClassName) throws NotFoundInstrumentException {
final NamedClassPool classPool = getClassPool(classLoader);
try {
return classPool.get(jvmInternalClassName);
} catch (NotFoundException e) {
throw new NotFoundInstrumentException(jvmInternalClassName + " class not found. Cause:" + e.getMessage(), e);
}
}
public NamedClassPool getClassPool(ClassLoader classLoader) {
return childClassPool.getClassPool(classLoader);
}
public boolean hasClass(String javassistClassName, ClassPool classPool) {
URL url = classPool.find(javassistClassName);
if (url == null) {
return false;
}
return true;
}
@Override
public boolean hasClass(ClassLoader classLoader, String classBinaryName) {
ClassPool classPool = getClassPool(classLoader);
return hasClass(classBinaryName, classPool);
}
@Override
public void appendToBootstrapClassPath(JarFile jarFile) {
if (jarFile == null) {
throw new NullPointerException("jarFile must not be null");
}
if (isInfo) {
logger.info("appendToBootstrapClassPath:{}", jarFile);
}
synchronized (this) {
this.instrumentation.appendToBootstrapClassLoaderSearch(jarFile);
try {
getClassPool(null).appendClassPath(jarFile.getName());
} catch (NotFoundException e) {
throw new PinpointException(e);
}
}
}
}